added artists to library and ui
This commit is contained in:
parent
e09c83c09d
commit
f8a7136472
19 changed files with 502 additions and 115 deletions
|
@ -1,7 +1,7 @@
|
|||
import 'package:equatable/equatable.dart';
|
||||
|
||||
class Artist extends Equatable {
|
||||
const Artist(this.name);
|
||||
const Artist({this.name});
|
||||
|
||||
final String name;
|
||||
|
||||
|
|
|
@ -2,11 +2,13 @@ import 'package:dartz/dartz.dart';
|
|||
|
||||
import '../../core/error/failures.dart';
|
||||
import '../entities/album.dart';
|
||||
import '../entities/artist.dart';
|
||||
import '../entities/song.dart';
|
||||
|
||||
abstract class MusicDataRepository {
|
||||
Future<Either<Failure, List<Song>>> getSongs();
|
||||
Future<Either<Failure, List<Song>>> getSongsFromAlbum(Album album);
|
||||
Future<Either<Failure, List<Album>>> getAlbums();
|
||||
Future<Either<Failure, List<Artist>>> getArtists();
|
||||
Future<void> updateDatabase();
|
||||
}
|
55
lib/presentation/pages/artists_page.dart
Normal file
55
lib/presentation/pages/artists_page.dart
Normal file
|
@ -0,0 +1,55 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../domain/entities/artist.dart';
|
||||
import '../state/music_data_store.dart';
|
||||
|
||||
class ArtistsPage extends StatefulWidget {
|
||||
const ArtistsPage({Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_ArtistsPageState createState() => _ArtistsPageState();
|
||||
}
|
||||
|
||||
class _ArtistsPageState extends State<ArtistsPage>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
print('ArtistsPage.build');
|
||||
final MusicDataStore musicDataStore = Provider.of<MusicDataStore>(context);
|
||||
|
||||
super.build(context);
|
||||
return Observer(builder: (_) {
|
||||
print('ArtistsPage.build -> Observer.builder');
|
||||
final bool isFetching = musicDataStore.isFetchingArtists;
|
||||
|
||||
if (isFetching) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: const <Widget>[
|
||||
CircularProgressIndicator(),
|
||||
Text('Loading items...'),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
final List<Artist> artists = musicDataStore.artists;
|
||||
return ListView.separated(
|
||||
itemCount: artists.length,
|
||||
itemBuilder: (_, int index) {
|
||||
final Artist artist = artists[index];
|
||||
return ListTile(
|
||||
title: Text(artist.name),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (BuildContext context, int index) => const Divider(
|
||||
height: 4.0,
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'albums_page.dart';
|
||||
import 'artists_page.dart';
|
||||
import 'songs_page.dart';
|
||||
|
||||
class LibraryTabContainer extends StatelessWidget {
|
||||
|
@ -32,8 +33,8 @@ class LibraryTabContainer extends StatelessWidget {
|
|||
const Expanded(
|
||||
child: TabBarView(
|
||||
children: <Widget>[
|
||||
Center(
|
||||
child: Text('Artists'),
|
||||
ArtistsPage(
|
||||
key: PageStorageKey('ArtistsPage'),
|
||||
),
|
||||
AlbumsPage(
|
||||
key: PageStorageKey('AlbumsPage'),
|
||||
|
|
|
@ -16,8 +16,8 @@ class SettingsPage extends StatelessWidget {
|
|||
Container(
|
||||
height: 12.0,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 16.0,
|
||||
vertical: 4.0,
|
||||
),
|
||||
|
@ -32,13 +32,15 @@ class SettingsPage extends StatelessWidget {
|
|||
ListTile(
|
||||
title: const Text('Update library'),
|
||||
subtitle: Observer(builder: (_) {
|
||||
final bool isFetchingArtists = store.isFetchingArtists;
|
||||
final bool isFetchingAlbums = store.isFetchingAlbums;
|
||||
final bool isFetchingSongs = store.isFetchingSongs;
|
||||
|
||||
if (!isFetchingAlbums && !isFetchingSongs) {
|
||||
if (!isFetchingArtists && !isFetchingAlbums && !isFetchingSongs) {
|
||||
final int artistCount = store.artists.length;
|
||||
final int albumCount = store.albums.length;
|
||||
final int songCount = store.songs.length;
|
||||
return Text('XX artists, $albumCount albums, $songCount songs');
|
||||
return Text('$artistCount artists, $albumCount albums, $songCount songs');
|
||||
}
|
||||
return const Text('');
|
||||
}),
|
||||
|
|
|
@ -6,7 +6,7 @@ part of 'audio_store.dart';
|
|||
// StoreGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// ignore_for_file: non_constant_identifier_names, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic
|
||||
// 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 {
|
||||
final _$currentSongAtom = Atom(name: '_AudioStore.currentSong');
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:meta/meta.dart';
|
|||
import 'package:mobx/mobx.dart';
|
||||
|
||||
import '../../domain/entities/album.dart';
|
||||
import '../../domain/entities/artist.dart';
|
||||
import '../../domain/entities/song.dart';
|
||||
import '../../domain/repositories/music_data_repository.dart';
|
||||
|
||||
|
@ -19,6 +20,11 @@ abstract class _MusicDataStore with Store {
|
|||
|
||||
bool _initialized = false;
|
||||
|
||||
@observable
|
||||
ObservableList<Artist> artists = <Artist>[].asObservable();
|
||||
@observable
|
||||
bool isFetchingArtists = false;
|
||||
|
||||
@observable
|
||||
ObservableList<Album> albums = <Album>[].asObservable();
|
||||
@observable
|
||||
|
@ -39,6 +45,7 @@ abstract class _MusicDataStore with Store {
|
|||
void init() {
|
||||
if (!_initialized) {
|
||||
print('MusicDataStore.init');
|
||||
fetchArtists();
|
||||
fetchAlbums();
|
||||
fetchSongs();
|
||||
|
||||
|
@ -57,6 +64,22 @@ abstract class _MusicDataStore with Store {
|
|||
isUpdatingDatabase = false;
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> fetchArtists() async {
|
||||
isFetchingArtists = true;
|
||||
final result = await _musicDataRepository.getArtists();
|
||||
|
||||
result.fold(
|
||||
(_) => artists = <Artist>[].asObservable(),
|
||||
(artistList) {
|
||||
artists.clear();
|
||||
artists.addAll(artistList);
|
||||
},
|
||||
);
|
||||
|
||||
isFetchingArtists = false;
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> fetchAlbums() async {
|
||||
isFetchingAlbums = true;
|
||||
|
|
|
@ -6,9 +6,40 @@ part of 'music_data_store.dart';
|
|||
// StoreGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// ignore_for_file: non_constant_identifier_names, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic
|
||||
// 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 _$artistsAtom = Atom(name: '_MusicDataStore.artists');
|
||||
|
||||
@override
|
||||
ObservableList<Artist> get artists {
|
||||
_$artistsAtom.reportRead();
|
||||
return super.artists;
|
||||
}
|
||||
|
||||
@override
|
||||
set artists(ObservableList<Artist> value) {
|
||||
_$artistsAtom.reportWrite(value, super.artists, () {
|
||||
super.artists = value;
|
||||
});
|
||||
}
|
||||
|
||||
final _$isFetchingArtistsAtom =
|
||||
Atom(name: '_MusicDataStore.isFetchingArtists');
|
||||
|
||||
@override
|
||||
bool get isFetchingArtists {
|
||||
_$isFetchingArtistsAtom.reportRead();
|
||||
return super.isFetchingArtists;
|
||||
}
|
||||
|
||||
@override
|
||||
set isFetchingArtists(bool value) {
|
||||
_$isFetchingArtistsAtom.reportWrite(value, super.isFetchingArtists, () {
|
||||
super.isFetchingArtists = value;
|
||||
});
|
||||
}
|
||||
|
||||
final _$albumsAtom = Atom(name: '_MusicDataStore.albums');
|
||||
|
||||
@override
|
||||
|
@ -108,6 +139,13 @@ mixin _$MusicDataStore on _MusicDataStore, Store {
|
|||
return _$updateDatabaseAsyncAction.run(() => super.updateDatabase());
|
||||
}
|
||||
|
||||
final _$fetchArtistsAsyncAction = AsyncAction('_MusicDataStore.fetchArtists');
|
||||
|
||||
@override
|
||||
Future<void> fetchArtists() {
|
||||
return _$fetchArtistsAsyncAction.run(() => super.fetchArtists());
|
||||
}
|
||||
|
||||
final _$fetchAlbumsAsyncAction = AsyncAction('_MusicDataStore.fetchAlbums');
|
||||
|
||||
@override
|
||||
|
@ -148,6 +186,8 @@ mixin _$MusicDataStore on _MusicDataStore, Store {
|
|||
@override
|
||||
String toString() {
|
||||
return '''
|
||||
artists: ${artists},
|
||||
isFetchingArtists: ${isFetchingArtists},
|
||||
albums: ${albums},
|
||||
isFetchingAlbums: ${isFetchingAlbums},
|
||||
songs: ${songs},
|
||||
|
|
|
@ -6,7 +6,7 @@ part of 'navigation_store.dart';
|
|||
// StoreGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// ignore_for_file: non_constant_identifier_names, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic
|
||||
// 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 _$NavigationStore on _NavigationStore, Store {
|
||||
final _$navIndexAtom = Atom(name: '_NavigationStore.navIndex');
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter_audio_query/flutter_audio_query.dart';
|
||||
|
||||
import '../models/album_model.dart';
|
||||
import '../models/artist_model.dart';
|
||||
import '../models/song_model.dart';
|
||||
import 'local_music_fetcher_contract.dart';
|
||||
|
||||
|
@ -9,6 +10,15 @@ class LocalMusicFetcherImpl implements LocalMusicFetcher {
|
|||
|
||||
final FlutterAudioQuery flutterAudioQuery;
|
||||
|
||||
@override
|
||||
Future<List<ArtistModel>> getArtists() async {
|
||||
final List<ArtistInfo> artistInfoList =
|
||||
await flutterAudioQuery.getArtists();
|
||||
return artistInfoList
|
||||
.map((ArtistInfo artistInfo) => ArtistModel.fromArtistInfo(artistInfo))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<AlbumModel>> getAlbums() async {
|
||||
final List<AlbumInfo> albumInfoList = await flutterAudioQuery.getAlbums();
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import '../models/album_model.dart';
|
||||
import '../models/artist_model.dart';
|
||||
import '../models/song_model.dart';
|
||||
|
||||
abstract class LocalMusicFetcher {
|
||||
Future<List<ArtistModel>> getArtists();
|
||||
Future<List<AlbumModel>> getAlbums();
|
||||
Future<List<SongModel>> getSongs();
|
||||
}
|
|
@ -8,6 +8,7 @@ import 'package:path/path.dart' as p;
|
|||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
import '../models/album_model.dart';
|
||||
import '../models/artist_model.dart';
|
||||
import '../models/song_model.dart';
|
||||
import 'music_data_source_contract.dart';
|
||||
|
||||
|
@ -15,6 +16,14 @@ part 'moor_music_data_source.g.dart';
|
|||
|
||||
const String MOOR_ISOLATE = 'MOOR_ISOLATE';
|
||||
|
||||
@DataClassName('MoorArtist')
|
||||
class Artists extends Table {
|
||||
TextColumn get name => text()();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {name};
|
||||
}
|
||||
|
||||
@DataClassName('MoorAlbum')
|
||||
class Albums extends Table {
|
||||
IntColumn get id => integer().autoIncrement()();
|
||||
|
@ -23,9 +32,6 @@ class Albums extends Table {
|
|||
TextColumn get albumArtPath => text().nullable()();
|
||||
IntColumn get year => integer().nullable()();
|
||||
BoolColumn get present => boolean().withDefault(const Constant(true))();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {id};
|
||||
}
|
||||
|
||||
@DataClassName('MoorSong')
|
||||
|
@ -44,7 +50,7 @@ class Songs extends Table {
|
|||
Set<Column> get primaryKey => {path};
|
||||
}
|
||||
|
||||
@UseMoor(tables: [Albums, Songs])
|
||||
@UseMoor(tables: [Artists, Albums, Songs])
|
||||
class MoorMusicDataSource extends _$MoorMusicDataSource
|
||||
implements MusicDataSource {
|
||||
/// Use MoorMusicDataSource in main isolate only.
|
||||
|
@ -190,6 +196,33 @@ class MoorMusicDataSource extends _$MoorMusicDataSource
|
|||
Future<void> resetSongsPresentFlag() async {
|
||||
update(songs).write(const SongsCompanion(present: Value(false)));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteAllArtists() async {
|
||||
delete(artists).go();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> insertArtist(ArtistModel artistModel) async {
|
||||
return into(artists).insert(artistModel.toArtistsCompanion());
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteAllAlbums() async {
|
||||
delete(albums).go();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteAllSongs() async {
|
||||
delete(songs).go();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<ArtistModel>> getArtists() {
|
||||
return select(artists).get().then((moorArtistList) => moorArtistList
|
||||
.map((moorArtist) => ArtistModel.fromMoorArtist(moorArtist))
|
||||
.toList());
|
||||
}
|
||||
}
|
||||
|
||||
LazyDatabase _openConnection() {
|
||||
|
|
|
@ -7,6 +7,154 @@ part of 'moor_music_data_source.dart';
|
|||
// **************************************************************************
|
||||
|
||||
// ignore_for_file: unnecessary_brace_in_string_interps, unnecessary_this
|
||||
class MoorArtist extends DataClass implements Insertable<MoorArtist> {
|
||||
final String name;
|
||||
MoorArtist({@required this.name});
|
||||
factory MoorArtist.fromData(Map<String, dynamic> data, GeneratedDatabase db,
|
||||
{String prefix}) {
|
||||
final effectivePrefix = prefix ?? '';
|
||||
final stringType = db.typeSystem.forDartType<String>();
|
||||
return MoorArtist(
|
||||
name: stringType.mapFromDatabaseResponse(data['${effectivePrefix}name']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, Expression>{};
|
||||
if (!nullToAbsent || name != null) {
|
||||
map['name'] = Variable<String>(name);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
ArtistsCompanion toCompanion(bool nullToAbsent) {
|
||||
return ArtistsCompanion(
|
||||
name: name == null && nullToAbsent ? const Value.absent() : Value(name),
|
||||
);
|
||||
}
|
||||
|
||||
factory MoorArtist.fromJson(Map<String, dynamic> json,
|
||||
{ValueSerializer serializer}) {
|
||||
serializer ??= moorRuntimeOptions.defaultSerializer;
|
||||
return MoorArtist(
|
||||
name: serializer.fromJson<String>(json['name']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
Map<String, dynamic> toJson({ValueSerializer serializer}) {
|
||||
serializer ??= moorRuntimeOptions.defaultSerializer;
|
||||
return <String, dynamic>{
|
||||
'name': serializer.toJson<String>(name),
|
||||
};
|
||||
}
|
||||
|
||||
MoorArtist copyWith({String name}) => MoorArtist(
|
||||
name: name ?? this.name,
|
||||
);
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('MoorArtist(')..write('name: $name')..write(')'))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => $mrjf(name.hashCode);
|
||||
@override
|
||||
bool operator ==(dynamic other) =>
|
||||
identical(this, other) ||
|
||||
(other is MoorArtist && other.name == this.name);
|
||||
}
|
||||
|
||||
class ArtistsCompanion extends UpdateCompanion<MoorArtist> {
|
||||
final Value<String> name;
|
||||
const ArtistsCompanion({
|
||||
this.name = const Value.absent(),
|
||||
});
|
||||
ArtistsCompanion.insert({
|
||||
@required String name,
|
||||
}) : name = Value(name);
|
||||
static Insertable<MoorArtist> custom({
|
||||
Expression<String> name,
|
||||
}) {
|
||||
return RawValuesInsertable({
|
||||
if (name != null) 'name': name,
|
||||
});
|
||||
}
|
||||
|
||||
ArtistsCompanion copyWith({Value<String> name}) {
|
||||
return ArtistsCompanion(
|
||||
name: name ?? this.name,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, Expression>{};
|
||||
if (name.present) {
|
||||
map['name'] = Variable<String>(name.value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('ArtistsCompanion(')..write('name: $name')..write(')'))
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
class $ArtistsTable extends Artists with TableInfo<$ArtistsTable, MoorArtist> {
|
||||
final GeneratedDatabase _db;
|
||||
final String _alias;
|
||||
$ArtistsTable(this._db, [this._alias]);
|
||||
final VerificationMeta _nameMeta = const VerificationMeta('name');
|
||||
GeneratedTextColumn _name;
|
||||
@override
|
||||
GeneratedTextColumn get name => _name ??= _constructName();
|
||||
GeneratedTextColumn _constructName() {
|
||||
return GeneratedTextColumn(
|
||||
'name',
|
||||
$tableName,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [name];
|
||||
@override
|
||||
$ArtistsTable get asDslTable => this;
|
||||
@override
|
||||
String get $tableName => _alias ?? 'artists';
|
||||
@override
|
||||
final String actualTableName = 'artists';
|
||||
@override
|
||||
VerificationContext validateIntegrity(Insertable<MoorArtist> instance,
|
||||
{bool isInserting = false}) {
|
||||
final context = VerificationContext();
|
||||
final data = instance.toColumns(true);
|
||||
if (data.containsKey('name')) {
|
||||
context.handle(
|
||||
_nameMeta, name.isAcceptableOrUnknown(data['name'], _nameMeta));
|
||||
} else if (isInserting) {
|
||||
context.missing(_nameMeta);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@override
|
||||
Set<GeneratedColumn> get $primaryKey => {name};
|
||||
@override
|
||||
MoorArtist map(Map<String, dynamic> data, {String tablePrefix}) {
|
||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null;
|
||||
return MoorArtist.fromData(data, _db, prefix: effectivePrefix);
|
||||
}
|
||||
|
||||
@override
|
||||
$ArtistsTable createAlias(String alias) {
|
||||
return $ArtistsTable(_db, alias);
|
||||
}
|
||||
}
|
||||
|
||||
class MoorAlbum extends DataClass implements Insertable<MoorAlbum> {
|
||||
final int id;
|
||||
final String title;
|
||||
|
@ -237,6 +385,19 @@ class AlbumsCompanion extends UpdateCompanion<MoorAlbum> {
|
|||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('AlbumsCompanion(')
|
||||
..write('id: $id, ')
|
||||
..write('title: $title, ')
|
||||
..write('artist: $artist, ')
|
||||
..write('albumArtPath: $albumArtPath, ')
|
||||
..write('year: $year, ')
|
||||
..write('present: $present')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
class $AlbumsTable extends Albums with TableInfo<$AlbumsTable, MoorAlbum> {
|
||||
|
@ -693,6 +854,22 @@ class SongsCompanion extends UpdateCompanion<MoorSong> {
|
|||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('SongsCompanion(')
|
||||
..write('title: $title, ')
|
||||
..write('albumTitle: $albumTitle, ')
|
||||
..write('albumId: $albumId, ')
|
||||
..write('artist: $artist, ')
|
||||
..write('path: $path, ')
|
||||
..write('duration: $duration, ')
|
||||
..write('albumArtPath: $albumArtPath, ')
|
||||
..write('trackNumber: $trackNumber, ')
|
||||
..write('present: $present')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
class $SongsTable extends Songs with TableInfo<$SongsTable, MoorSong> {
|
||||
|
@ -904,6 +1081,8 @@ abstract class _$MoorMusicDataSource extends GeneratedDatabase {
|
|||
_$MoorMusicDataSource(QueryExecutor e)
|
||||
: super(SqlTypeSystem.defaultInstance, e);
|
||||
_$MoorMusicDataSource.connect(DatabaseConnection c) : super.connect(c);
|
||||
$ArtistsTable _artists;
|
||||
$ArtistsTable get artists => _artists ??= $ArtistsTable(this);
|
||||
$AlbumsTable _albums;
|
||||
$AlbumsTable get albums => _albums ??= $AlbumsTable(this);
|
||||
$SongsTable _songs;
|
||||
|
@ -911,5 +1090,5 @@ abstract class _$MoorMusicDataSource extends GeneratedDatabase {
|
|||
@override
|
||||
Iterable<TableInfo> get allTables => allSchemaEntities.whereType<TableInfo>();
|
||||
@override
|
||||
List<DatabaseSchemaEntity> get allSchemaEntities => [albums, songs];
|
||||
List<DatabaseSchemaEntity> get allSchemaEntities => [artists, albums, songs];
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import '../models/album_model.dart';
|
||||
import '../models/artist_model.dart';
|
||||
import '../models/song_model.dart';
|
||||
|
||||
abstract class MusicDataSource {
|
||||
|
@ -26,4 +27,12 @@ abstract class MusicDataSource {
|
|||
String title, String album, String artist);
|
||||
Future<void> flagSongPresent(SongModel songModel);
|
||||
Future<void> removeNonpresentSongs();
|
||||
|
||||
Future<void> deleteAllArtists();
|
||||
Future<int> insertArtist(ArtistModel artistModel);
|
||||
|
||||
Future<void> deleteAllAlbums();
|
||||
Future<void> deleteAllSongs();
|
||||
|
||||
Future<List<ArtistModel>> getArtists();
|
||||
}
|
||||
|
|
28
lib/system/models/artist_model.dart
Normal file
28
lib/system/models/artist_model.dart
Normal file
|
@ -0,0 +1,28 @@
|
|||
import 'package:flutter_audio_query/flutter_audio_query.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:moor/moor.dart';
|
||||
|
||||
import '../../domain/entities/artist.dart';
|
||||
import '../datasources/moor_music_data_source.dart';
|
||||
|
||||
class ArtistModel extends Artist {
|
||||
const ArtistModel({
|
||||
@required String name,
|
||||
}) : super(
|
||||
name: name,
|
||||
);
|
||||
|
||||
factory ArtistModel.fromMoorArtist(MoorArtist moorArtist) => ArtistModel(
|
||||
name: moorArtist.name,
|
||||
);
|
||||
|
||||
factory ArtistModel.fromArtistInfo(ArtistInfo artistInfo) {
|
||||
return ArtistModel(
|
||||
name: artistInfo.name,
|
||||
);
|
||||
}
|
||||
|
||||
ArtistsCompanion toArtistsCompanion() => ArtistsCompanion(
|
||||
name: Value(name),
|
||||
);
|
||||
}
|
|
@ -38,7 +38,6 @@ class SongModel extends Song {
|
|||
);
|
||||
|
||||
factory SongModel.fromSongInfo(SongInfo songInfo) {
|
||||
final String trackNumber = songInfo.track;
|
||||
final String duration = songInfo.duration;
|
||||
|
||||
return SongModel(
|
||||
|
@ -49,7 +48,7 @@ class SongModel extends Song {
|
|||
path: songInfo.filePath,
|
||||
duration: duration == null ? null : int.parse(duration),
|
||||
albumArtPath: songInfo.albumArtwork,
|
||||
trackNumber: trackNumber == null ? null : int.parse(trackNumber),
|
||||
trackNumber: _parseTrackNumber(songInfo.track),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -130,4 +129,19 @@ class SongModel extends Song {
|
|||
'albumId': albumId,
|
||||
'trackNumber': trackNumber,
|
||||
});
|
||||
|
||||
static int _parseTrackNumber(String trackNumberString) {
|
||||
int trackNumber;
|
||||
if (trackNumberString == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
trackNumber = int.tryParse(trackNumberString);
|
||||
if (trackNumber == null) {
|
||||
if (trackNumberString.contains('/')) {
|
||||
trackNumber = int.tryParse(trackNumberString.split('/')[0]);
|
||||
}
|
||||
}
|
||||
return trackNumber;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,11 +3,13 @@ import 'package:meta/meta.dart';
|
|||
|
||||
import '../../core/error/failures.dart';
|
||||
import '../../domain/entities/album.dart';
|
||||
import '../../domain/entities/artist.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/artist_model.dart';
|
||||
import '../models/song_model.dart';
|
||||
|
||||
class MusicDataRepositoryImpl implements MusicDataRepository {
|
||||
|
@ -19,6 +21,12 @@ class MusicDataRepositoryImpl implements MusicDataRepository {
|
|||
final LocalMusicFetcher localMusicFetcher;
|
||||
final MusicDataSource musicDataSource;
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<Artist>>> getArtists() async {
|
||||
return musicDataSource.getArtists().then((List<ArtistModel> artists) =>
|
||||
Right<Failure, List<ArtistModel>>(artists));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<Album>>> getAlbums() async {
|
||||
return musicDataSource.getAlbums().then(
|
||||
|
@ -39,43 +47,31 @@ class MusicDataRepositoryImpl implements MusicDataRepository {
|
|||
|
||||
@override
|
||||
Future<void> updateDatabase() async {
|
||||
final List<AlbumModel> albums = await localMusicFetcher.getAlbums();
|
||||
await musicDataSource.deleteAllArtists();
|
||||
final List<ArtistModel> artists = await localMusicFetcher.getArtists();
|
||||
|
||||
for (final ArtistModel artist in artists) {
|
||||
await musicDataSource.insertArtist(artist);
|
||||
}
|
||||
|
||||
await musicDataSource.deleteAllAlbums();
|
||||
final List<AlbumModel> albums = await localMusicFetcher.getAlbums();
|
||||
final Map<int, int> albumIdMap = {};
|
||||
|
||||
await musicDataSource.resetAlbumsPresentFlag();
|
||||
for (final AlbumModel album in albums) {
|
||||
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;
|
||||
}
|
||||
final int newAlbumId = await musicDataSource.insertAlbum(album);
|
||||
albumIdMap[album.id] = newAlbumId;
|
||||
}
|
||||
await musicDataSource.removeNonpresentAlbums();
|
||||
|
||||
await musicDataSource.deleteAllSongs();
|
||||
final List<SongModel> songs = await localMusicFetcher.getSongs();
|
||||
|
||||
await musicDataSource.resetSongsPresentFlag();
|
||||
|
||||
for (final SongModel song in songs) {
|
||||
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.copyWith(albumId: albumIdMap[song.albumId]);
|
||||
final SongModel songToInsert =
|
||||
song.copyWith(albumId: albumIdMap[song.albumId]);
|
||||
|
||||
// TODO: fails if albumId is null
|
||||
await musicDataSource.insertSong(songToInsert);
|
||||
}
|
||||
// TODO: fails if albumId is null
|
||||
await musicDataSource.insertSong(songToInsert);
|
||||
}
|
||||
|
||||
await musicDataSource.removeNonpresentSongs();
|
||||
}
|
||||
}
|
||||
|
|
129
pubspec.lock
129
pubspec.lock
|
@ -7,28 +7,21 @@ packages:
|
|||
name: _fe_analyzer_shared
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
version: "6.0.0"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.39.10"
|
||||
analyzer_plugin_fork:
|
||||
version: "0.39.14"
|
||||
analyzer_plugin:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer_plugin_fork
|
||||
name: analyzer_plugin
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.2"
|
||||
archive:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: archive
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.13"
|
||||
version: "0.3.0"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -42,7 +35,7 @@ packages:
|
|||
name: async
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
version: "2.4.2"
|
||||
audio_service:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -84,14 +77,14 @@ packages:
|
|||
name: build_resolvers
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.9"
|
||||
version: "1.3.10"
|
||||
build_runner:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_runner
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.10.0"
|
||||
version: "1.10.1"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -113,6 +106,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "7.1.0"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
charcode:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -147,14 +147,14 @@ packages:
|
|||
name: code_builder
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.3.0"
|
||||
version: "3.4.0"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.14.12"
|
||||
version: "1.14.13"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -168,14 +168,14 @@ packages:
|
|||
name: crypto
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
version: "2.1.5"
|
||||
csslib:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: csslib
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.16.1"
|
||||
version: "0.16.2"
|
||||
dart_style:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -196,7 +196,14 @@ packages:
|
|||
name: equatable
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
version: "1.2.3"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -210,7 +217,7 @@ packages:
|
|||
name: file
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.1.0"
|
||||
version: "5.2.1"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -236,7 +243,7 @@ packages:
|
|||
name: flutter_cache_manager
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
version: "1.4.1"
|
||||
flutter_isolate:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -250,7 +257,7 @@ packages:
|
|||
name: flutter_mobx
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
version: "1.1.0+2"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
|
@ -267,7 +274,7 @@ packages:
|
|||
name: get_it
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
version: "4.0.4"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -295,7 +302,7 @@ packages:
|
|||
name: http
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.12.1"
|
||||
version: "0.12.2"
|
||||
http_multi_server:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -310,13 +317,6 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.1.4"
|
||||
image:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.12"
|
||||
intl:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -337,7 +337,7 @@ packages:
|
|||
name: js
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.6.1+1"
|
||||
version: "0.6.2"
|
||||
json_annotation:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -365,7 +365,7 @@ packages:
|
|||
name: matcher
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.12.6"
|
||||
version: "0.12.8"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -386,14 +386,14 @@ packages:
|
|||
name: mobx
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
version: "1.2.1+2"
|
||||
mobx_codegen:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: mobx_codegen
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
version: "1.1.0+1"
|
||||
mockito:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -407,7 +407,7 @@ packages:
|
|||
name: moor
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
version: "3.3.1"
|
||||
moor_ffi:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -421,7 +421,7 @@ packages:
|
|||
name: moor_generator
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
version: "3.3.1"
|
||||
nested:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -456,21 +456,21 @@ packages:
|
|||
name: path
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.6.4"
|
||||
version: "1.7.0"
|
||||
path_provider:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: path_provider
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.6.10"
|
||||
version: "1.6.11"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_linux
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.0.1+1"
|
||||
version: "0.0.1+2"
|
||||
path_provider_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -492,13 +492,6 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.9.0"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: petitparser
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -533,7 +526,7 @@ packages:
|
|||
name: provider
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.1.2"
|
||||
version: "4.3.2"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -575,7 +568,7 @@ packages:
|
|||
name: shelf
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.7.5"
|
||||
version: "0.7.7"
|
||||
shelf_web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -594,7 +587,7 @@ packages:
|
|||
name: source_gen
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.9.5"
|
||||
version: "0.9.6"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -608,28 +601,35 @@ packages:
|
|||
name: sqflite
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.0+2"
|
||||
version: "1.3.1"
|
||||
sqflite_common:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_common
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
version: "1.0.2+1"
|
||||
sqlite3:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqlite3
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.4"
|
||||
sqlparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqlparser
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.9.0"
|
||||
version: "0.10.1"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.9.3"
|
||||
version: "1.9.5"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -657,7 +657,7 @@ packages:
|
|||
name: synchronized
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
version: "2.2.0+2"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -671,7 +671,7 @@ packages:
|
|||
name: test_api
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.15"
|
||||
version: "0.2.17"
|
||||
timing:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -685,14 +685,14 @@ packages:
|
|||
name: typed_data
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.6"
|
||||
version: "1.2.0"
|
||||
uuid:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: uuid
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.4"
|
||||
version: "2.2.0"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -721,13 +721,6 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.0"
|
||||
xml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xml
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.6.1"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -736,5 +729,5 @@ packages:
|
|||
source: hosted
|
||||
version: "2.2.1"
|
||||
sdks:
|
||||
dart: ">=2.7.0 <3.0.0"
|
||||
flutter: ">=1.16.0 <2.0.0"
|
||||
dart: ">=2.9.0 <3.0.0"
|
||||
flutter: ">=1.20.0 <2.0.0"
|
||||
|
|
|
@ -4,8 +4,8 @@ description: The frustration-free music player.
|
|||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
sdk: ">=2.6.0 <3.0.0"
|
||||
flutter: ">=1.12.0"
|
||||
sdk: ">=2.9.0 <3.0.0"
|
||||
flutter: ">=1.20.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
|
@ -34,7 +34,7 @@ dev_dependencies:
|
|||
flutter_test:
|
||||
sdk: flutter
|
||||
moor_generator: ^3.0.0
|
||||
build_runner:
|
||||
build_runner: 1.10.1
|
||||
mobx_codegen: ^1.0.3
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue