database stuff, can't remember

This commit is contained in:
Moritz Weber 2020-06-28 10:40:53 +02:00
parent f92731779d
commit 6da0b5c559
10 changed files with 507 additions and 158 deletions

View file

@ -13,34 +13,30 @@ mixin _$AudioStore on _AudioStore, Store {
@override
ObservableStream<Song> get currentSong {
_$currentSongAtom.context.enforceReadPolicy(_$currentSongAtom);
_$currentSongAtom.reportObserved();
_$currentSongAtom.reportRead();
return super.currentSong;
}
@override
set currentSong(ObservableStream<Song> value) {
_$currentSongAtom.context.conditionallyRunInAction(() {
_$currentSongAtom.reportWrite(value, super.currentSong, () {
super.currentSong = value;
_$currentSongAtom.reportChanged();
}, _$currentSongAtom, name: '${_$currentSongAtom.name}_set');
});
}
final _$songAtom = Atom(name: '_AudioStore.song');
@override
Song get song {
_$songAtom.context.enforceReadPolicy(_$songAtom);
_$songAtom.reportObserved();
_$songAtom.reportRead();
return super.song;
}
@override
set song(Song value) {
_$songAtom.context.conditionallyRunInAction(() {
_$songAtom.reportWrite(value, super.song, () {
super.song = value;
_$songAtom.reportChanged();
}, _$songAtom, name: '${_$songAtom.name}_set');
});
}
final _$playbackStateStreamAtom =
@ -48,19 +44,15 @@ mixin _$AudioStore on _AudioStore, Store {
@override
ObservableStream<PlaybackState> get playbackStateStream {
_$playbackStateStreamAtom.context
.enforceReadPolicy(_$playbackStateStreamAtom);
_$playbackStateStreamAtom.reportObserved();
_$playbackStateStreamAtom.reportRead();
return super.playbackStateStream;
}
@override
set playbackStateStream(ObservableStream<PlaybackState> value) {
_$playbackStateStreamAtom.context.conditionallyRunInAction(() {
_$playbackStateStreamAtom.reportWrite(value, super.playbackStateStream, () {
super.playbackStateStream = value;
_$playbackStateStreamAtom.reportChanged();
}, _$playbackStateStreamAtom,
name: '${_$playbackStateStreamAtom.name}_set');
});
}
final _$currentPositionStreamAtom =
@ -68,43 +60,40 @@ mixin _$AudioStore on _AudioStore, Store {
@override
ObservableStream<int> get currentPositionStream {
_$currentPositionStreamAtom.context
.enforceReadPolicy(_$currentPositionStreamAtom);
_$currentPositionStreamAtom.reportObserved();
_$currentPositionStreamAtom.reportRead();
return super.currentPositionStream;
}
@override
set currentPositionStream(ObservableStream<int> value) {
_$currentPositionStreamAtom.context.conditionallyRunInAction(() {
_$currentPositionStreamAtom.reportWrite(value, super.currentPositionStream,
() {
super.currentPositionStream = value;
_$currentPositionStreamAtom.reportChanged();
}, _$currentPositionStreamAtom,
name: '${_$currentPositionStreamAtom.name}_set');
});
}
final _$playSongAsyncAction = AsyncAction('playSong');
final _$playSongAsyncAction = AsyncAction('_AudioStore.playSong');
@override
Future<void> playSong(int index, List<Song> songList) {
return _$playSongAsyncAction.run(() => super.playSong(index, songList));
}
final _$playAsyncAction = AsyncAction('play');
final _$playAsyncAction = AsyncAction('_AudioStore.play');
@override
Future<void> play() {
return _$playAsyncAction.run(() => super.play());
}
final _$pauseAsyncAction = AsyncAction('pause');
final _$pauseAsyncAction = AsyncAction('_AudioStore.pause');
@override
Future<void> pause() {
return _$pauseAsyncAction.run(() => super.pause());
}
final _$updateSongAsyncAction = AsyncAction('updateSong');
final _$updateSongAsyncAction = AsyncAction('_AudioStore.updateSong');
@override
Future<void> updateSong(Song streamValue) {
@ -115,7 +104,8 @@ mixin _$AudioStore on _AudioStore, Store {
@override
void init() {
final _$actionInfo = _$_AudioStoreActionController.startAction();
final _$actionInfo =
_$_AudioStoreActionController.startAction(name: '_AudioStore.init');
try {
return super.init();
} finally {
@ -125,8 +115,11 @@ mixin _$AudioStore on _AudioStore, Store {
@override
String toString() {
final string =
'currentSong: ${currentSong.toString()},song: ${song.toString()},playbackStateStream: ${playbackStateStream.toString()},currentPositionStream: ${currentPositionStream.toString()}';
return '{$string}';
return '''
currentSong: ${currentSong},
song: ${song},
playbackStateStream: ${playbackStateStream},
currentPositionStream: ${currentPositionStream}
''';
}
}

View file

@ -13,51 +13,45 @@ mixin _$MusicDataStore on _MusicDataStore, Store {
@override
ObservableFuture<List<Album>> get albumsFuture {
_$albumsFutureAtom.context.enforceReadPolicy(_$albumsFutureAtom);
_$albumsFutureAtom.reportObserved();
_$albumsFutureAtom.reportRead();
return super.albumsFuture;
}
@override
set albumsFuture(ObservableFuture<List<Album>> value) {
_$albumsFutureAtom.context.conditionallyRunInAction(() {
_$albumsFutureAtom.reportWrite(value, super.albumsFuture, () {
super.albumsFuture = value;
_$albumsFutureAtom.reportChanged();
}, _$albumsFutureAtom, name: '${_$albumsFutureAtom.name}_set');
});
}
final _$songsAtom = Atom(name: '_MusicDataStore.songs');
@override
ObservableList<Song> get songs {
_$songsAtom.context.enforceReadPolicy(_$songsAtom);
_$songsAtom.reportObserved();
_$songsAtom.reportRead();
return super.songs;
}
@override
set songs(ObservableList<Song> value) {
_$songsAtom.context.conditionallyRunInAction(() {
_$songsAtom.reportWrite(value, super.songs, () {
super.songs = value;
_$songsAtom.reportChanged();
}, _$songsAtom, name: '${_$songsAtom.name}_set');
});
}
final _$isFetchingSongsAtom = Atom(name: '_MusicDataStore.isFetchingSongs');
@override
bool get isFetchingSongs {
_$isFetchingSongsAtom.context.enforceReadPolicy(_$isFetchingSongsAtom);
_$isFetchingSongsAtom.reportObserved();
_$isFetchingSongsAtom.reportRead();
return super.isFetchingSongs;
}
@override
set isFetchingSongs(bool value) {
_$isFetchingSongsAtom.context.conditionallyRunInAction(() {
_$isFetchingSongsAtom.reportWrite(value, super.isFetchingSongs, () {
super.isFetchingSongs = value;
_$isFetchingSongsAtom.reportChanged();
}, _$isFetchingSongsAtom, name: '${_$isFetchingSongsAtom.name}_set');
});
}
final _$isUpdatingDatabaseAtom =
@ -65,59 +59,56 @@ mixin _$MusicDataStore on _MusicDataStore, Store {
@override
bool get isUpdatingDatabase {
_$isUpdatingDatabaseAtom.context
.enforceReadPolicy(_$isUpdatingDatabaseAtom);
_$isUpdatingDatabaseAtom.reportObserved();
_$isUpdatingDatabaseAtom.reportRead();
return super.isUpdatingDatabase;
}
@override
set isUpdatingDatabase(bool value) {
_$isUpdatingDatabaseAtom.context.conditionallyRunInAction(() {
_$isUpdatingDatabaseAtom.reportWrite(value, super.isUpdatingDatabase, () {
super.isUpdatingDatabase = value;
_$isUpdatingDatabaseAtom.reportChanged();
}, _$isUpdatingDatabaseAtom, name: '${_$isUpdatingDatabaseAtom.name}_set');
});
}
final _$albumSongsAtom = Atom(name: '_MusicDataStore.albumSongs');
@override
ObservableList<Song> get albumSongs {
_$albumSongsAtom.context.enforceReadPolicy(_$albumSongsAtom);
_$albumSongsAtom.reportObserved();
_$albumSongsAtom.reportRead();
return super.albumSongs;
}
@override
set albumSongs(ObservableList<Song> value) {
_$albumSongsAtom.context.conditionallyRunInAction(() {
_$albumSongsAtom.reportWrite(value, super.albumSongs, () {
super.albumSongs = value;
_$albumSongsAtom.reportChanged();
}, _$albumSongsAtom, name: '${_$albumSongsAtom.name}_set');
});
}
final _$updateDatabaseAsyncAction = AsyncAction('updateDatabase');
final _$updateDatabaseAsyncAction =
AsyncAction('_MusicDataStore.updateDatabase');
@override
Future<void> updateDatabase() {
return _$updateDatabaseAsyncAction.run(() => super.updateDatabase());
}
final _$fetchAlbumsAsyncAction = AsyncAction('fetchAlbums');
final _$fetchAlbumsAsyncAction = AsyncAction('_MusicDataStore.fetchAlbums');
@override
Future<void> fetchAlbums() {
return _$fetchAlbumsAsyncAction.run(() => super.fetchAlbums());
}
final _$fetchSongsAsyncAction = AsyncAction('fetchSongs');
final _$fetchSongsAsyncAction = AsyncAction('_MusicDataStore.fetchSongs');
@override
Future<void> fetchSongs() {
return _$fetchSongsAsyncAction.run(() => super.fetchSongs());
}
final _$fetchSongsFromAlbumAsyncAction = AsyncAction('fetchSongsFromAlbum');
final _$fetchSongsFromAlbumAsyncAction =
AsyncAction('_MusicDataStore.fetchSongsFromAlbum');
@override
Future<void> fetchSongsFromAlbum(Album album) {
@ -130,7 +121,8 @@ mixin _$MusicDataStore on _MusicDataStore, Store {
@override
void init() {
final _$actionInfo = _$_MusicDataStoreActionController.startAction();
final _$actionInfo = _$_MusicDataStoreActionController.startAction(
name: '_MusicDataStore.init');
try {
return super.init();
} finally {
@ -140,8 +132,12 @@ mixin _$MusicDataStore on _MusicDataStore, Store {
@override
String toString() {
final string =
'albumsFuture: ${albumsFuture.toString()},songs: ${songs.toString()},isFetchingSongs: ${isFetchingSongs.toString()},isUpdatingDatabase: ${isUpdatingDatabase.toString()},albumSongs: ${albumSongs.toString()}';
return '{$string}';
return '''
albumsFuture: ${albumsFuture},
songs: ${songs},
isFetchingSongs: ${isFetchingSongs},
isUpdatingDatabase: ${isUpdatingDatabase},
albumSongs: ${albumSongs}
''';
}
}

View file

@ -13,17 +13,15 @@ mixin _$NavigationStore on _NavigationStore, Store {
@override
int get navIndex {
_$navIndexAtom.context.enforceReadPolicy(_$navIndexAtom);
_$navIndexAtom.reportObserved();
_$navIndexAtom.reportRead();
return super.navIndex;
}
@override
set navIndex(int value) {
_$navIndexAtom.context.conditionallyRunInAction(() {
_$navIndexAtom.reportWrite(value, super.navIndex, () {
super.navIndex = value;
_$navIndexAtom.reportChanged();
}, _$navIndexAtom, name: '${_$navIndexAtom.name}_set');
});
}
final _$_NavigationStoreActionController =
@ -31,7 +29,8 @@ mixin _$NavigationStore on _NavigationStore, Store {
@override
void init() {
final _$actionInfo = _$_NavigationStoreActionController.startAction();
final _$actionInfo = _$_NavigationStoreActionController.startAction(
name: '_NavigationStore.init');
try {
return super.init();
} finally {
@ -41,7 +40,8 @@ mixin _$NavigationStore on _NavigationStore, Store {
@override
void setNavIndex(int i) {
final _$actionInfo = _$_NavigationStoreActionController.startAction();
final _$actionInfo = _$_NavigationStoreActionController.startAction(
name: '_NavigationStore.setNavIndex');
try {
return super.setNavIndex(i);
} finally {
@ -51,7 +51,8 @@ mixin _$NavigationStore on _NavigationStore, Store {
@override
String toString() {
final string = 'navIndex: ${navIndex.toString()}';
return '{$string}';
return '''
navIndex: ${navIndex}
''';
}
}

View file

@ -21,6 +21,7 @@ class LocalMusicFetcherImpl implements LocalMusicFetcher {
Future<List<SongModel>> getSongs() async {
final List<SongInfo> songInfoList = await flutterAudioQuery.getSongs();
return songInfoList
.where((songInfo) => songInfo.isMusic)
.map((SongInfo songInfo) => SongModel.fromSongInfo(songInfo))
.toList();
}

View file

@ -17,24 +17,31 @@ const String MOOR_ISOLATE = 'MOOR_ISOLATE';
@DataClassName('MoorAlbum')
class Albums extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get title => text()();
TextColumn get artist => text()();
TextColumn get albumArtPath => text().nullable()();
IntColumn get year => integer().nullable()();
BoolColumn get present => boolean().withDefault(const Constant(true))();
@override
Set<Column> get primaryKey => {title, artist};
Set<Column> get primaryKey => {id};
}
@DataClassName('MoorSong')
class Songs extends Table {
TextColumn get title => text()();
TextColumn get album => text()();
TextColumn get albumTitle => text()();
IntColumn get albumId => integer()();
TextColumn get artist => text()();
TextColumn get path => text()();
IntColumn get duration => integer().nullable()();
TextColumn get albumArtPath => text().nullable()();
IntColumn get trackNumber => integer().nullable()();
BoolColumn get present => boolean().withDefault(const Constant(true))();
@override
Set<Column> get primaryKey => {path};
}
@UseMoor(tables: [Albums, Songs])
@ -42,10 +49,13 @@ class MoorMusicDataSource extends _$MoorMusicDataSource
implements MusicDataSource {
/// Use MoorMusicDataSource in main isolate only.
MoorMusicDataSource() : super(_openConnection());
/// Used for testing with in-memory database.
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;
@ -59,9 +69,8 @@ class MoorMusicDataSource extends _$MoorMusicDataSource
// TODO: insert can throw exception -> implications?
@override
Future<void> insertAlbum(AlbumModel albumModel) async {
await into(albums).insert(albumModel.toAlbumsCompanion());
return;
Future<int> insertAlbum(AlbumModel albumModel) async {
return await into(albums).insert(albumModel.toAlbumsCompanion());
}
@override
@ -77,17 +86,18 @@ class MoorMusicDataSource extends _$MoorMusicDataSource
.toList());
}
@override
@override
Future<List<SongModel>> getSongsFromAlbum(AlbumModel album) {
return (select(songs)..where((tbl) => tbl.album.equals(album.title))).get().then((moorSongList) => moorSongList
.map((moorSong) => SongModel.fromMoorSong(moorSong))
.toList());
return (select(songs)..where((tbl) => tbl.albumTitle.equals(album.title)))
.get()
.then((moorSongList) => moorSongList
.map((moorSong) => SongModel.fromMoorSong(moorSong))
.toList());
}
@override
Future<void> insertSong(SongModel songModel) async {
await into(songs).insert(songModel.toSongsCompanion());
return;
}
@override
@ -95,6 +105,89 @@ class MoorMusicDataSource extends _$MoorMusicDataSource
final List<SongModel> songList = await getSongs();
return songList.contains(songModel);
}
@override
Future<void> flagAlbumPresent(AlbumModel albumModel) async {
if (albumModel.id != null) {
(update(albums)..where((t) => t.id.equals(albumModel.id)))
.write(const AlbumsCompanion(present: Value(true)));
} else {
throw UnimplementedError();
}
}
@override
Future<AlbumModel> getAlbumByTitleArtist(String title, String artist) {
return (select(albums)
..where((t) => t.title.equals(title) & t.artist.equals(artist)))
.getSingle()
.then(
(moorAlbum) {
if (moorAlbum == null) {
return null;
}
return AlbumModel.fromMoorAlbum(moorAlbum);
},
);
}
@override
Future<void> removeNonpresentAlbums() async {
(delete(albums)..where((t) => t.present.not())).go();
}
@override
Future<void> resetAlbumsPresentFlag() async {
update(albums).write(const AlbumsCompanion(present: Value(false)));
// return;
}
@override
Future<void> flagSongPresent(SongModel songModel) async {
(update(songs)..where((t) => t.path.equals(songModel.path)))
.write(const SongsCompanion(present: Value(true)));
}
@override
Future<SongModel> getSongByPath(String path) async {
return (select(songs)..where((t) => t.path.equals(path))).getSingle().then(
(moorSong) {
if (moorSong == null) {
return null;
}
return SongModel.fromMoorSong(moorSong);
},
);
}
@override
Future<SongModel> getSongByTitleAlbumArtist(
String title, String album, String artist) async {
return (select(songs)
..where((t) =>
t.title.equals(title) &
t.albumTitle.equals(album) &
t.artist.equals(artist)))
.getSingle()
.then(
(moorSong) {
if (moorSong == null) {
return null;
}
return SongModel.fromMoorSong(moorSong);
},
);
}
@override
Future<void> removeNonpresentSongs() async {
(delete(songs)..where((t) => t.present.not())).go();
}
@override
Future<void> resetSongsPresentFlag() async {
update(songs).write(const SongsCompanion(present: Value(false)));
}
}
LazyDatabase _openConnection() {
@ -144,7 +237,7 @@ void _startBackground(_IsolateStartRequest request) {
// functions can only take one parameter.
class _IsolateStartRequest {
_IsolateStartRequest(this.sendMoorIsolate, this.targetPath);
final SendPort sendMoorIsolate;
final String targetPath;
}

View file

@ -8,21 +8,27 @@ part of 'moor_music_data_source.dart';
// ignore_for_file: unnecessary_brace_in_string_interps, unnecessary_this
class MoorAlbum extends DataClass implements Insertable<MoorAlbum> {
final int id;
final String title;
final String artist;
final String albumArtPath;
final int year;
final bool present;
MoorAlbum(
{@required this.title,
{@required this.id,
@required this.title,
@required this.artist,
this.albumArtPath,
this.year});
this.year,
@required this.present});
factory MoorAlbum.fromData(Map<String, dynamic> data, GeneratedDatabase db,
{String prefix}) {
final effectivePrefix = prefix ?? '';
final stringType = db.typeSystem.forDartType<String>();
final intType = db.typeSystem.forDartType<int>();
final stringType = db.typeSystem.forDartType<String>();
final boolType = db.typeSystem.forDartType<bool>();
return MoorAlbum(
id: intType.mapFromDatabaseResponse(data['${effectivePrefix}id']),
title:
stringType.mapFromDatabaseResponse(data['${effectivePrefix}title']),
artist:
@ -30,11 +36,16 @@ class MoorAlbum extends DataClass implements Insertable<MoorAlbum> {
albumArtPath: stringType
.mapFromDatabaseResponse(data['${effectivePrefix}album_art_path']),
year: intType.mapFromDatabaseResponse(data['${effectivePrefix}year']),
present:
boolType.mapFromDatabaseResponse(data['${effectivePrefix}present']),
);
}
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
if (!nullToAbsent || id != null) {
map['id'] = Variable<int>(id);
}
if (!nullToAbsent || title != null) {
map['title'] = Variable<String>(title);
}
@ -47,110 +58,168 @@ class MoorAlbum extends DataClass implements Insertable<MoorAlbum> {
if (!nullToAbsent || year != null) {
map['year'] = Variable<int>(year);
}
if (!nullToAbsent || present != null) {
map['present'] = Variable<bool>(present);
}
return map;
}
AlbumsCompanion toCompanion(bool nullToAbsent) {
return AlbumsCompanion(
id: id == null && nullToAbsent ? const Value.absent() : Value(id),
title:
title == null && nullToAbsent ? const Value.absent() : Value(title),
artist:
artist == null && nullToAbsent ? const Value.absent() : Value(artist),
albumArtPath: albumArtPath == null && nullToAbsent
? const Value.absent()
: Value(albumArtPath),
year: year == null && nullToAbsent ? const Value.absent() : Value(year),
present: present == null && nullToAbsent
? const Value.absent()
: Value(present),
);
}
factory MoorAlbum.fromJson(Map<String, dynamic> json,
{ValueSerializer serializer}) {
serializer ??= moorRuntimeOptions.defaultSerializer;
return MoorAlbum(
id: serializer.fromJson<int>(json['id']),
title: serializer.fromJson<String>(json['title']),
artist: serializer.fromJson<String>(json['artist']),
albumArtPath: serializer.fromJson<String>(json['albumArtPath']),
year: serializer.fromJson<int>(json['year']),
present: serializer.fromJson<bool>(json['present']),
);
}
@override
Map<String, dynamic> toJson({ValueSerializer serializer}) {
serializer ??= moorRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'id': serializer.toJson<int>(id),
'title': serializer.toJson<String>(title),
'artist': serializer.toJson<String>(artist),
'albumArtPath': serializer.toJson<String>(albumArtPath),
'year': serializer.toJson<int>(year),
'present': serializer.toJson<bool>(present),
};
}
MoorAlbum copyWith(
{String title, String artist, String albumArtPath, int year}) =>
{int id,
String title,
String artist,
String albumArtPath,
int year,
bool present}) =>
MoorAlbum(
id: id ?? this.id,
title: title ?? this.title,
artist: artist ?? this.artist,
albumArtPath: albumArtPath ?? this.albumArtPath,
year: year ?? this.year,
present: present ?? this.present,
);
@override
String toString() {
return (StringBuffer('MoorAlbum(')
..write('id: $id, ')
..write('title: $title, ')
..write('artist: $artist, ')
..write('albumArtPath: $albumArtPath, ')
..write('year: $year')
..write('year: $year, ')
..write('present: $present')
..write(')'))
.toString();
}
@override
int get hashCode => $mrjf($mrjc(title.hashCode,
$mrjc(artist.hashCode, $mrjc(albumArtPath.hashCode, year.hashCode))));
int get hashCode => $mrjf($mrjc(
id.hashCode,
$mrjc(
title.hashCode,
$mrjc(
artist.hashCode,
$mrjc(albumArtPath.hashCode,
$mrjc(year.hashCode, present.hashCode))))));
@override
bool operator ==(dynamic other) =>
identical(this, other) ||
(other is MoorAlbum &&
other.id == this.id &&
other.title == this.title &&
other.artist == this.artist &&
other.albumArtPath == this.albumArtPath &&
other.year == this.year);
other.year == this.year &&
other.present == this.present);
}
class AlbumsCompanion extends UpdateCompanion<MoorAlbum> {
final Value<int> id;
final Value<String> title;
final Value<String> artist;
final Value<String> albumArtPath;
final Value<int> year;
final Value<bool> present;
const AlbumsCompanion({
this.id = const Value.absent(),
this.title = const Value.absent(),
this.artist = const Value.absent(),
this.albumArtPath = const Value.absent(),
this.year = const Value.absent(),
this.present = const Value.absent(),
});
AlbumsCompanion.insert({
this.id = const Value.absent(),
@required String title,
@required String artist,
this.albumArtPath = const Value.absent(),
this.year = const Value.absent(),
this.present = const Value.absent(),
}) : title = Value(title),
artist = Value(artist);
static Insertable<MoorAlbum> custom({
Expression<int> id,
Expression<String> title,
Expression<String> artist,
Expression<String> albumArtPath,
Expression<int> year,
Expression<bool> present,
}) {
return RawValuesInsertable({
if (id != null) 'id': id,
if (title != null) 'title': title,
if (artist != null) 'artist': artist,
if (albumArtPath != null) 'album_art_path': albumArtPath,
if (year != null) 'year': year,
if (present != null) 'present': present,
});
}
AlbumsCompanion copyWith(
{Value<String> title,
{Value<int> id,
Value<String> title,
Value<String> artist,
Value<String> albumArtPath,
Value<int> year}) {
Value<int> year,
Value<bool> present}) {
return AlbumsCompanion(
id: id ?? this.id,
title: title ?? this.title,
artist: artist ?? this.artist,
albumArtPath: albumArtPath ?? this.albumArtPath,
year: year ?? this.year,
present: present ?? this.present,
);
}
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
if (id.present) {
map['id'] = Variable<int>(id.value);
}
if (title.present) {
map['title'] = Variable<String>(title.value);
}
@ -163,6 +232,9 @@ class AlbumsCompanion extends UpdateCompanion<MoorAlbum> {
if (year.present) {
map['year'] = Variable<int>(year.value);
}
if (present.present) {
map['present'] = Variable<bool>(present.value);
}
return map;
}
}
@ -171,6 +243,15 @@ class $AlbumsTable extends Albums with TableInfo<$AlbumsTable, MoorAlbum> {
final GeneratedDatabase _db;
final String _alias;
$AlbumsTable(this._db, [this._alias]);
final VerificationMeta _idMeta = const VerificationMeta('id');
GeneratedIntColumn _id;
@override
GeneratedIntColumn get id => _id ??= _constructId();
GeneratedIntColumn _constructId() {
return GeneratedIntColumn('id', $tableName, false,
hasAutoIncrement: true, declaredAsPrimaryKey: true);
}
final VerificationMeta _titleMeta = const VerificationMeta('title');
GeneratedTextColumn _title;
@override
@ -221,8 +302,18 @@ class $AlbumsTable extends Albums with TableInfo<$AlbumsTable, MoorAlbum> {
);
}
final VerificationMeta _presentMeta = const VerificationMeta('present');
GeneratedBoolColumn _present;
@override
List<GeneratedColumn> get $columns => [title, artist, albumArtPath, year];
GeneratedBoolColumn get present => _present ??= _constructPresent();
GeneratedBoolColumn _constructPresent() {
return GeneratedBoolColumn('present', $tableName, false,
defaultValue: const Constant(true));
}
@override
List<GeneratedColumn> get $columns =>
[id, title, artist, albumArtPath, year, present];
@override
$AlbumsTable get asDslTable => this;
@override
@ -234,6 +325,9 @@ class $AlbumsTable extends Albums with TableInfo<$AlbumsTable, MoorAlbum> {
{bool isInserting = false}) {
final context = VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('id')) {
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id'], _idMeta));
}
if (data.containsKey('title')) {
context.handle(
_titleMeta, title.isAcceptableOrUnknown(data['title'], _titleMeta));
@ -256,11 +350,15 @@ class $AlbumsTable extends Albums with TableInfo<$AlbumsTable, MoorAlbum> {
context.handle(
_yearMeta, year.isAcceptableOrUnknown(data['year'], _yearMeta));
}
if (data.containsKey('present')) {
context.handle(_presentMeta,
present.isAcceptableOrUnknown(data['present'], _presentMeta));
}
return context;
}
@override
Set<GeneratedColumn> get $primaryKey => {title, artist};
Set<GeneratedColumn> get $primaryKey => {id};
@override
MoorAlbum map(Map<String, dynamic> data, {String tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null;
@ -275,30 +373,37 @@ class $AlbumsTable extends Albums with TableInfo<$AlbumsTable, MoorAlbum> {
class MoorSong extends DataClass implements Insertable<MoorSong> {
final String title;
final String album;
final String albumTitle;
final int albumId;
final String artist;
final String path;
final int duration;
final String albumArtPath;
final int trackNumber;
final bool present;
MoorSong(
{@required this.title,
@required this.album,
@required this.albumTitle,
@required this.albumId,
@required this.artist,
@required this.path,
this.duration,
this.albumArtPath,
this.trackNumber});
this.trackNumber,
@required this.present});
factory MoorSong.fromData(Map<String, dynamic> data, GeneratedDatabase db,
{String prefix}) {
final effectivePrefix = prefix ?? '';
final stringType = db.typeSystem.forDartType<String>();
final intType = db.typeSystem.forDartType<int>();
final boolType = db.typeSystem.forDartType<bool>();
return MoorSong(
title:
stringType.mapFromDatabaseResponse(data['${effectivePrefix}title']),
album:
stringType.mapFromDatabaseResponse(data['${effectivePrefix}album']),
albumTitle: stringType
.mapFromDatabaseResponse(data['${effectivePrefix}album_title']),
albumId:
intType.mapFromDatabaseResponse(data['${effectivePrefix}album_id']),
artist:
stringType.mapFromDatabaseResponse(data['${effectivePrefix}artist']),
path: stringType.mapFromDatabaseResponse(data['${effectivePrefix}path']),
@ -308,6 +413,8 @@ class MoorSong extends DataClass implements Insertable<MoorSong> {
.mapFromDatabaseResponse(data['${effectivePrefix}album_art_path']),
trackNumber: intType
.mapFromDatabaseResponse(data['${effectivePrefix}track_number']),
present:
boolType.mapFromDatabaseResponse(data['${effectivePrefix}present']),
);
}
@override
@ -316,8 +423,11 @@ class MoorSong extends DataClass implements Insertable<MoorSong> {
if (!nullToAbsent || title != null) {
map['title'] = Variable<String>(title);
}
if (!nullToAbsent || album != null) {
map['album'] = Variable<String>(album);
if (!nullToAbsent || albumTitle != null) {
map['album_title'] = Variable<String>(albumTitle);
}
if (!nullToAbsent || albumId != null) {
map['album_id'] = Variable<int>(albumId);
}
if (!nullToAbsent || artist != null) {
map['artist'] = Variable<String>(artist);
@ -334,20 +444,53 @@ class MoorSong extends DataClass implements Insertable<MoorSong> {
if (!nullToAbsent || trackNumber != null) {
map['track_number'] = Variable<int>(trackNumber);
}
if (!nullToAbsent || present != null) {
map['present'] = Variable<bool>(present);
}
return map;
}
SongsCompanion toCompanion(bool nullToAbsent) {
return SongsCompanion(
title:
title == null && nullToAbsent ? const Value.absent() : Value(title),
albumTitle: albumTitle == null && nullToAbsent
? const Value.absent()
: Value(albumTitle),
albumId: albumId == null && nullToAbsent
? const Value.absent()
: Value(albumId),
artist:
artist == null && nullToAbsent ? const Value.absent() : Value(artist),
path: path == null && nullToAbsent ? const Value.absent() : Value(path),
duration: duration == null && nullToAbsent
? const Value.absent()
: Value(duration),
albumArtPath: albumArtPath == null && nullToAbsent
? const Value.absent()
: Value(albumArtPath),
trackNumber: trackNumber == null && nullToAbsent
? const Value.absent()
: Value(trackNumber),
present: present == null && nullToAbsent
? const Value.absent()
: Value(present),
);
}
factory MoorSong.fromJson(Map<String, dynamic> json,
{ValueSerializer serializer}) {
serializer ??= moorRuntimeOptions.defaultSerializer;
return MoorSong(
title: serializer.fromJson<String>(json['title']),
album: serializer.fromJson<String>(json['album']),
albumTitle: serializer.fromJson<String>(json['albumTitle']),
albumId: serializer.fromJson<int>(json['albumId']),
artist: serializer.fromJson<String>(json['artist']),
path: serializer.fromJson<String>(json['path']),
duration: serializer.fromJson<int>(json['duration']),
albumArtPath: serializer.fromJson<String>(json['albumArtPath']),
trackNumber: serializer.fromJson<int>(json['trackNumber']),
present: serializer.fromJson<bool>(json['present']),
);
}
@override
@ -355,42 +498,50 @@ class MoorSong extends DataClass implements Insertable<MoorSong> {
serializer ??= moorRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'title': serializer.toJson<String>(title),
'album': serializer.toJson<String>(album),
'albumTitle': serializer.toJson<String>(albumTitle),
'albumId': serializer.toJson<int>(albumId),
'artist': serializer.toJson<String>(artist),
'path': serializer.toJson<String>(path),
'duration': serializer.toJson<int>(duration),
'albumArtPath': serializer.toJson<String>(albumArtPath),
'trackNumber': serializer.toJson<int>(trackNumber),
'present': serializer.toJson<bool>(present),
};
}
MoorSong copyWith(
{String title,
String album,
String albumTitle,
int albumId,
String artist,
String path,
int duration,
String albumArtPath,
int trackNumber}) =>
int trackNumber,
bool present}) =>
MoorSong(
title: title ?? this.title,
album: album ?? this.album,
albumTitle: albumTitle ?? this.albumTitle,
albumId: albumId ?? this.albumId,
artist: artist ?? this.artist,
path: path ?? this.path,
duration: duration ?? this.duration,
albumArtPath: albumArtPath ?? this.albumArtPath,
trackNumber: trackNumber ?? this.trackNumber,
present: present ?? this.present,
);
@override
String toString() {
return (StringBuffer('MoorSong(')
..write('title: $title, ')
..write('album: $album, ')
..write('albumTitle: $albumTitle, ')
..write('albumId: $albumId, ')
..write('artist: $artist, ')
..write('path: $path, ')
..write('duration: $duration, ')
..write('albumArtPath: $albumArtPath, ')
..write('trackNumber: $trackNumber')
..write('trackNumber: $trackNumber, ')
..write('present: $present')
..write(')'))
.toString();
}
@ -399,91 +550,114 @@ class MoorSong extends DataClass implements Insertable<MoorSong> {
int get hashCode => $mrjf($mrjc(
title.hashCode,
$mrjc(
album.hashCode,
albumTitle.hashCode,
$mrjc(
artist.hashCode,
albumId.hashCode,
$mrjc(
path.hashCode,
$mrjc(duration.hashCode,
$mrjc(albumArtPath.hashCode, trackNumber.hashCode)))))));
artist.hashCode,
$mrjc(
path.hashCode,
$mrjc(
duration.hashCode,
$mrjc(
albumArtPath.hashCode,
$mrjc(trackNumber.hashCode,
present.hashCode)))))))));
@override
bool operator ==(dynamic other) =>
identical(this, other) ||
(other is MoorSong &&
other.title == this.title &&
other.album == this.album &&
other.albumTitle == this.albumTitle &&
other.albumId == this.albumId &&
other.artist == this.artist &&
other.path == this.path &&
other.duration == this.duration &&
other.albumArtPath == this.albumArtPath &&
other.trackNumber == this.trackNumber);
other.trackNumber == this.trackNumber &&
other.present == this.present);
}
class SongsCompanion extends UpdateCompanion<MoorSong> {
final Value<String> title;
final Value<String> album;
final Value<String> albumTitle;
final Value<int> albumId;
final Value<String> artist;
final Value<String> path;
final Value<int> duration;
final Value<String> albumArtPath;
final Value<int> trackNumber;
final Value<bool> present;
const SongsCompanion({
this.title = const Value.absent(),
this.album = const Value.absent(),
this.albumTitle = const Value.absent(),
this.albumId = const Value.absent(),
this.artist = const Value.absent(),
this.path = const Value.absent(),
this.duration = const Value.absent(),
this.albumArtPath = const Value.absent(),
this.trackNumber = const Value.absent(),
this.present = const Value.absent(),
});
SongsCompanion.insert({
@required String title,
@required String album,
@required String albumTitle,
@required int albumId,
@required String artist,
@required String path,
this.duration = const Value.absent(),
this.albumArtPath = const Value.absent(),
this.trackNumber = const Value.absent(),
this.present = const Value.absent(),
}) : title = Value(title),
album = Value(album),
albumTitle = Value(albumTitle),
albumId = Value(albumId),
artist = Value(artist),
path = Value(path);
static Insertable<MoorSong> custom({
Expression<String> title,
Expression<String> album,
Expression<String> albumTitle,
Expression<int> albumId,
Expression<String> artist,
Expression<String> path,
Expression<int> duration,
Expression<String> albumArtPath,
Expression<int> trackNumber,
Expression<bool> present,
}) {
return RawValuesInsertable({
if (title != null) 'title': title,
if (album != null) 'album': album,
if (albumTitle != null) 'album_title': albumTitle,
if (albumId != null) 'album_id': albumId,
if (artist != null) 'artist': artist,
if (path != null) 'path': path,
if (duration != null) 'duration': duration,
if (albumArtPath != null) 'album_art_path': albumArtPath,
if (trackNumber != null) 'track_number': trackNumber,
if (present != null) 'present': present,
});
}
SongsCompanion copyWith(
{Value<String> title,
Value<String> album,
Value<String> albumTitle,
Value<int> albumId,
Value<String> artist,
Value<String> path,
Value<int> duration,
Value<String> albumArtPath,
Value<int> trackNumber}) {
Value<int> trackNumber,
Value<bool> present}) {
return SongsCompanion(
title: title ?? this.title,
album: album ?? this.album,
albumTitle: albumTitle ?? this.albumTitle,
albumId: albumId ?? this.albumId,
artist: artist ?? this.artist,
path: path ?? this.path,
duration: duration ?? this.duration,
albumArtPath: albumArtPath ?? this.albumArtPath,
trackNumber: trackNumber ?? this.trackNumber,
present: present ?? this.present,
);
}
@ -493,8 +667,11 @@ class SongsCompanion extends UpdateCompanion<MoorSong> {
if (title.present) {
map['title'] = Variable<String>(title.value);
}
if (album.present) {
map['album'] = Variable<String>(album.value);
if (albumTitle.present) {
map['album_title'] = Variable<String>(albumTitle.value);
}
if (albumId.present) {
map['album_id'] = Variable<int>(albumId.value);
}
if (artist.present) {
map['artist'] = Variable<String>(artist.value);
@ -511,6 +688,9 @@ class SongsCompanion extends UpdateCompanion<MoorSong> {
if (trackNumber.present) {
map['track_number'] = Variable<int>(trackNumber.value);
}
if (present.present) {
map['present'] = Variable<bool>(present.value);
}
return map;
}
}
@ -531,13 +711,25 @@ class $SongsTable extends Songs with TableInfo<$SongsTable, MoorSong> {
);
}
final VerificationMeta _albumMeta = const VerificationMeta('album');
GeneratedTextColumn _album;
final VerificationMeta _albumTitleMeta = const VerificationMeta('albumTitle');
GeneratedTextColumn _albumTitle;
@override
GeneratedTextColumn get album => _album ??= _constructAlbum();
GeneratedTextColumn _constructAlbum() {
GeneratedTextColumn get albumTitle => _albumTitle ??= _constructAlbumTitle();
GeneratedTextColumn _constructAlbumTitle() {
return GeneratedTextColumn(
'album',
'album_title',
$tableName,
false,
);
}
final VerificationMeta _albumIdMeta = const VerificationMeta('albumId');
GeneratedIntColumn _albumId;
@override
GeneratedIntColumn get albumId => _albumId ??= _constructAlbumId();
GeneratedIntColumn _constructAlbumId() {
return GeneratedIntColumn(
'album_id',
$tableName,
false,
);
@ -607,9 +799,27 @@ class $SongsTable extends Songs with TableInfo<$SongsTable, MoorSong> {
);
}
final VerificationMeta _presentMeta = const VerificationMeta('present');
GeneratedBoolColumn _present;
@override
List<GeneratedColumn> get $columns =>
[title, album, artist, path, duration, albumArtPath, trackNumber];
GeneratedBoolColumn get present => _present ??= _constructPresent();
GeneratedBoolColumn _constructPresent() {
return GeneratedBoolColumn('present', $tableName, false,
defaultValue: const Constant(true));
}
@override
List<GeneratedColumn> get $columns => [
title,
albumTitle,
albumId,
artist,
path,
duration,
albumArtPath,
trackNumber,
present
];
@override
$SongsTable get asDslTable => this;
@override
@ -627,11 +837,19 @@ class $SongsTable extends Songs with TableInfo<$SongsTable, MoorSong> {
} else if (isInserting) {
context.missing(_titleMeta);
}
if (data.containsKey('album')) {
if (data.containsKey('album_title')) {
context.handle(
_albumMeta, album.isAcceptableOrUnknown(data['album'], _albumMeta));
_albumTitleMeta,
albumTitle.isAcceptableOrUnknown(
data['album_title'], _albumTitleMeta));
} else if (isInserting) {
context.missing(_albumMeta);
context.missing(_albumTitleMeta);
}
if (data.containsKey('album_id')) {
context.handle(_albumIdMeta,
albumId.isAcceptableOrUnknown(data['album_id'], _albumIdMeta));
} else if (isInserting) {
context.missing(_albumIdMeta);
}
if (data.containsKey('artist')) {
context.handle(_artistMeta,
@ -661,11 +879,15 @@ class $SongsTable extends Songs with TableInfo<$SongsTable, MoorSong> {
trackNumber.isAcceptableOrUnknown(
data['track_number'], _trackNumberMeta));
}
if (data.containsKey('present')) {
context.handle(_presentMeta,
present.isAcceptableOrUnknown(data['present'], _presentMeta));
}
return context;
}
@override
Set<GeneratedColumn> get $primaryKey => <GeneratedColumn>{};
Set<GeneratedColumn> get $primaryKey => {path};
@override
MoorSong map(Map<String, dynamic> data, {String tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null;

View file

@ -4,10 +4,26 @@ import '../models/song_model.dart';
abstract class MusicDataSource {
Future<List<AlbumModel>> getAlbums();
Future<bool> albumExists(AlbumModel albumModel);
Future<void> insertAlbum(AlbumModel albumModel);
/// Insert album into the database. Return the ID of the inserted album.
Future<int> insertAlbum(AlbumModel albumModel);
Future<List<SongModel>> getSongs();
Future<List<SongModel>> getSongsFromAlbum(AlbumModel album);
Future<bool> songExists(SongModel songModel);
Future<void> insertSong(SongModel songModel);
Future<void> resetAlbumsPresentFlag();
/// Get an AlbumModel with the matching title and artist. Returns null if there is none.
Future<AlbumModel> getAlbumByTitleArtist(String title, String artist);
Future<void> flagAlbumPresent(AlbumModel albumModel);
Future<void> removeNonpresentAlbums();
Future<void> resetSongsPresentFlag();
Future<SongModel> getSongByPath(String path);
Future<SongModel> getSongByTitleAlbumArtist(
String title, String album, String artist);
Future<void> flagSongPresent(SongModel songModel);
Future<void> removeNonpresentSongs();
}

View file

@ -6,7 +6,7 @@ import '../../domain/entities/album.dart';
import '../datasources/moor_music_data_source.dart';
class AlbumModel extends Album {
AlbumModel({
const AlbumModel({
this.id,
@required String title,
@required String artist,
@ -20,6 +20,7 @@ class AlbumModel extends Album {
);
factory AlbumModel.fromMoorAlbum(MoorAlbum moorAlbum) => AlbumModel(
id: moorAlbum.id,
title: moorAlbum.title,
artist: moorAlbum.artist,
albumArtPath: moorAlbum.albumArtPath,
@ -30,6 +31,7 @@ class AlbumModel extends Album {
final String _year = albumInfo.firstYear;
return AlbumModel(
id: int.parse(albumInfo.id),
title: albumInfo.title,
artist: albumInfo.artist,
albumArtPath: albumInfo.albumArt,

View file

@ -7,8 +7,7 @@ import '../../domain/entities/song.dart';
import '../datasources/moor_music_data_source.dart';
class SongModel extends Song {
const SongModel({
this.id,
SongModel({
@required String title,
@required String album,
@required String artist,
@ -16,6 +15,7 @@ class SongModel extends Song {
@required int duration,
int trackNumber,
String albumArtPath,
this.albumId
}) : super(
title: title,
album: album,
@ -29,7 +29,8 @@ class SongModel extends Song {
factory SongModel.fromMoorSong(MoorSong moorSong) => SongModel(
title: moorSong.title,
artist: moorSong.artist,
album: moorSong.album,
album: moorSong.albumTitle,
albumId: moorSong.albumId,
path: moorSong.path,
duration: moorSong.duration,
albumArtPath: moorSong.albumArtPath,
@ -44,6 +45,7 @@ class SongModel extends Song {
title: songInfo.title,
artist: songInfo.artist,
album: songInfo.album,
albumId: int.parse(songInfo.albumId),
path: songInfo.filePath,
duration: duration == null ? null : int.parse(duration),
albumArtPath: songInfo.albumArtwork,
@ -69,10 +71,11 @@ class SongModel extends Song {
);
}
final int id;
int albumId;
SongsCompanion toSongsCompanion() => SongsCompanion(
album: Value(album),
albumTitle: Value(album),
albumId: Value(albumId),
artist: Value(artist),
title: Value(title),
path: Value(path),

View file

@ -1,14 +1,14 @@
import 'package:dartz/dartz.dart';
import 'package:meta/meta.dart';
import 'package:mucke/domain/entities/song.dart';
import 'package:mucke/system/models/song_model.dart';
import '../../core/error/failures.dart';
import '../../domain/entities/album.dart';
import '../../domain/entities/song.dart';
import '../../domain/repositories/music_data_repository.dart';
import '../datasources/local_music_fetcher_contract.dart';
import '../datasources/music_data_source_contract.dart';
import '../models/album_model.dart';
import '../models/song_model.dart';
class MusicDataRepositoryImpl implements MusicDataRepository {
MusicDataRepositoryImpl({
@ -31,30 +31,52 @@ class MusicDataRepositoryImpl implements MusicDataRepository {
(List<SongModel> songs) => Right<Failure, List<SongModel>>(songs));
}
@override
@override
Future<Either<Failure, List<Song>>> getSongsFromAlbum(Album album) async {
return musicDataSource.getSongsFromAlbum(album as AlbumModel).then(
(List<SongModel> songs) => Right<Failure, List<SongModel>>(songs));
}
// TODO: should remove albums that are not longer on the device
@override
Future<void> updateDatabase() async {
final List<AlbumModel> albums = await localMusicFetcher.getAlbums();
// TODO: compare with list of albums instead -> musicDataSource.getAlbums
final Map<int, int> albumIdMap = {};
await musicDataSource.resetAlbumsPresentFlag();
for (final AlbumModel album in albums) {
if (!await musicDataSource.albumExists(album)) {
musicDataSource.insertAlbum(album);
final storedAlbum = await musicDataSource.getAlbumByTitleArtist(
album.title, album.artist);
if (storedAlbum != null) {
albumIdMap[album.id] = storedAlbum.id;
await musicDataSource.flagAlbumPresent(storedAlbum);
} else {
final int newAlbumId = await musicDataSource.insertAlbum(album);
albumIdMap[album.id] = newAlbumId;
}
}
await musicDataSource.removeNonpresentAlbums();
final List<SongModel> songs = await localMusicFetcher.getSongs();
await musicDataSource.resetSongsPresentFlag();
for (final SongModel song in songs) {
if (!await musicDataSource.songExists(song)) {
musicDataSource.insertSong(song);
SongModel storedSong = await musicDataSource.getSongByPath(song.path);
storedSong ??= await musicDataSource.getSongByTitleAlbumArtist(
song.title, song.album, song.artist);
if (storedSong != null) {
await musicDataSource.flagSongPresent(storedSong);
} else {
final SongModel songToInsert = song;
songToInsert.albumId = albumIdMap[song.albumId];
// TODO: fails if albumId is null
await musicDataSource.insertSong(songToInsert);
}
}
await musicDataSource.removeNonpresentSongs();
}
}