artist + album search

This commit is contained in:
Moritz Weber 2021-07-15 19:27:48 +02:00
parent d90e323d82
commit c6b0bc4581
4 changed files with 72 additions and 12 deletions

View file

@ -2,12 +2,18 @@ import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:get_it/get_it.dart';
import '../../domain/entities/album.dart';
import '../../domain/entities/artist.dart';
import '../../domain/entities/song.dart';
import '../state/audio_store.dart';
import '../state/navigation_store.dart';
import '../state/search_page_store.dart';
import '../theming.dart';
import '../widgets/album_art_list_tile.dart';
import '../widgets/song_bottom_sheet.dart';
import '../widgets/song_list_tile.dart';
import 'album_details_page.dart';
import 'artist_details_page.dart';
class SearchPage extends StatefulWidget {
const SearchPage({Key? key}) : super(key: key);
@ -23,6 +29,7 @@ class _SearchPageState extends State<SearchPage> {
final searchStore = GetIt.I<SearchPageStore>();
final audioStore = GetIt.I<AudioStore>();
final navStore = GetIt.I<NavigationStore>();
return SafeArea(
child: Column(
@ -40,11 +47,12 @@ class _SearchPageState extends State<SearchPage> {
hintStyle: TEXT_HEADER.copyWith(color: Colors.white),
fillColor: Colors.white10,
filled: true,
enabledBorder: const OutlineInputBorder(borderSide: BorderSide.none, gapPadding: 0.0),
focusedBorder: const OutlineInputBorder(borderSide: BorderSide.none, gapPadding: 0.0),
enabledBorder:
const OutlineInputBorder(borderSide: BorderSide.none, gapPadding: 0.0),
focusedBorder:
const OutlineInputBorder(borderSide: BorderSide.none, gapPadding: 0.0),
contentPadding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 8.0),
),
onChanged: (text) {
searchStore.search(text);
},
@ -64,6 +72,36 @@ class _SearchPageState extends State<SearchPage> {
onTap: () => audioStore.playSong(0, [song]),
onTapMore: () => SongBottomSheet()(song, context),
);
} else if (results[index] is Album) {
final album = results[index] as Album;
return AlbumArtListTile(
title: album.title,
subtitle: album.artist,
albumArtPath: album.albumArtPath,
onTap: () {
navStore.pushOnLibrary(
MaterialPageRoute<Widget>(
builder: (BuildContext context) => AlbumDetailsPage(
album: album,
),
),
);
},
);
} else if (results[index] is Artist) {
final artist = results[index] as Artist;
return ListTile(
title: Text(artist.name),
onTap: () {
navStore.pushOnLibrary(
MaterialPageRoute<Widget>(
builder: (BuildContext context) => ArtistDetailsPage(
artist: artist,
),
),
);
},
);
}
return Container();
},

View file

@ -303,10 +303,22 @@ class MusicDataDao extends DatabaseAccessor<MoorDatabase>
}
@override
Future<List> search(String searchText) {
print('Search: $searchText');
Future<List> search(String searchText, {int limit = 0}) async {
List<dynamic> result = [];
return (select(songs)..where((tbl) => tbl.title.like(searchText))).get().then(
(moorSongList) => moorSongList.map((moorSong) => SongModel.fromMoor(moorSong)).toList());
result += await (select(artists)..where((tbl) => tbl.name.like(searchText))).get().then(
(moorList) => moorList.map((moorArtist) => ArtistModel.fromMoor(moorArtist)).toList(),
);
result += await (select(albums)..where((tbl) => tbl.title.like(searchText))).get().then(
(moorList) => moorList.map((moorAlbum) => AlbumModel.fromMoor(moorAlbum)).toList(),
);
result += await (select(songs)..where((tbl) => tbl.title.like(searchText))).get().then(
(moorList) => moorList.map((moorSong) => SongModel.fromMoor(moorSong)).toList(),
);
if (limit <= 0) return result;
return result.take(limit).toList();
}
}

View file

@ -44,5 +44,5 @@ abstract class MusicDataSource {
Future<void> setAlbumOfDay(AlbumOfDay albumOfDay);
Future<AlbumOfDay?> getAlbumOfDay();
Future<List> search(String searchText);
Future<List> search(String searchText, {int limit});
}

View file

@ -230,15 +230,19 @@ class MusicDataRepositoryImpl implements MusicDataRepository {
Future<List> search(String searchText) async {
if (searchText == '') return [];
final searchTextLower = searchText.toLowerCase();
// TODO: need to clean the string? sql injection?
final dbResult = await _musicDataSource.search(_fuzzy(searchText));
print(dbResult.length);
final dbResult = await _musicDataSource.search(_fuzzy(searchTextLower), limit: 200);
final List<List> ratedResults = [];
for (final x in dbResult) {
if (x is SongModel) {
// print('${x.title}: ${x.title.similarityTo(searchText)}');
ratedResults.add([x.title.similarityTo(searchText), x]);
ratedResults.add([_similarity(x.title.toLowerCase(), searchTextLower), x]);
} else if (x is AlbumModel) {
ratedResults.add([_similarity(x.title.toLowerCase(), searchTextLower), x]);
} else if (x is ArtistModel) {
ratedResults.add([_similarity(x.name.toLowerCase(), searchTextLower), x]);
}
}
ratedResults.sort((List a, List b) => -(a[0] as double).compareTo(b[0] as double));
@ -247,6 +251,12 @@ class MusicDataRepositoryImpl implements MusicDataRepository {
return results.toList();
}
double _similarity(String value, String searchText) {
return value.startsWith(searchText)
? value.similarityTo(searchText) + 1
: value.similarityTo(searchText);
}
String _fuzzy(String text) {
String fuzzyText = '%$text%';
// for (final c in text.toLowerCase().replaceAll(' ', '').split('')) {