From 4aa8576c73cc1fd362a108e098d3408519034289 Mon Sep 17 00:00:00 2001 From: geoffrey45 Date: Sun, 22 May 2022 03:23:42 +0300 Subject: [PATCH] rewrite album and artist search with rapidfuzz --- server/app/api/search.py | 25 +++------ server/app/helpers.py | 18 ++++-- server/app/lib/searchlib.py | 83 ++++++++++++++++++++++------ server/app/models.py | 2 +- src/components/Search/TracksGrid.vue | 1 + 5 files changed, 88 insertions(+), 41 deletions(-) diff --git a/server/app/api/search.py b/server/app/api/search.py index 34374490..3730c988 100644 --- a/server/app/api/search.py +++ b/server/app/api/search.py @@ -23,25 +23,14 @@ def search(): """ query = request.args.get("q") or "Mexican girl" - albums = searchlib.get_search_albums(query) - artists_dicts = [] + albums = searchlib.SearchAlbums(query)() + artists_dicts = searchlib.SearchArtists(query)() - artist_tracks = searchlib.get_artists(query) + tracks = searchlib.SearchTracks(query)() + top_artist = artists_dicts[0]["name"] - for song in artist_tracks: - for artist in song.artists: - if query.lower() in artist.lower(): - - artist_obj = { - "name": artist, - "image": helpers.check_artist_image(artist), - } - - if artist_obj not in artists_dicts: - artists_dicts.append(artist_obj) - - _tracks = searchlib.SearchTracks(query)() - tracks = [*_tracks, *artist_tracks] + _tracks = searchlib.GetTopArtistTracks(top_artist)() + tracks = [*tracks, *[t for t in _tracks if t not in tracks]] SEARCH_RESULTS.clear() SEARCH_RESULTS["tracks"] = tracks @@ -65,6 +54,8 @@ def search_load_more(): type = request.args.get("type") start = int(request.args.get("start")) + print(type, start) + if type == "tracks": return { "tracks": SEARCH_RESULTS["tracks"][start : start + 5], diff --git a/server/app/helpers.py b/server/app/helpers.py index e6278fee..0d8bd637 100644 --- a/server/app/helpers.py +++ b/server/app/helpers.py @@ -1,16 +1,14 @@ """ This module contains mini functions for the server. """ -from datetime import datetime import os import random import threading import time -from typing import Dict -from typing import List +from datetime import datetime +from typing import Dict, List -from app import models -from app import settings +from app import models, settings app_dir = settings.APP_DIR @@ -120,7 +118,15 @@ def create_album_hash(title: str, artist: str) -> str: """ return (title + artist).replace(" ", "").lower() + def create_new_date(): now = datetime.now() str = now.strftime("%Y-%m-%d %H:%M:%S") - return str \ No newline at end of file + return str + + +def create_safe_name(name: str) -> str: + """ + Creates a url-safe name from a name. + """ + return "".join([i for i in name if i not in '/\\:*?"<>|']) diff --git a/server/app/lib/searchlib.py b/server/app/lib/searchlib.py index cf7438e0..fabacbc0 100644 --- a/server/app/lib/searchlib.py +++ b/server/app/lib/searchlib.py @@ -9,6 +9,30 @@ from app.lib import albumslib from rapidfuzz import fuzz, process +ratio = fuzz.ratio +wratio = fuzz.WRatio + + +class Cutoff: + """ + Holds all the default cutoff values. + """ + + tracks: int = 70 + albums: int = 70 + artists: int = 70 + + +class Limit: + """ + Holds all the default limit values. + """ + + tracks: int = 10 + albums: int = 10 + artists: int = 10 + + class SearchTracks: def __init__(self, query) -> None: self.query = query @@ -20,9 +44,13 @@ class SearchTracks: tracks = [track.title for track in api.TRACKS] results = process.extract( - self.query, tracks, scorer=fuzz.token_set_ratio, score_cutoff=50 + self.query, + tracks, + scorer=fuzz.WRatio, + score_cutoff=Cutoff.tracks, + limit=Limit.tracks, ) - print(results) + return [api.TRACKS[i[2]] for i in results] @@ -38,34 +66,33 @@ class SearchArtists: artists = [track.artists for track in api.TRACKS] - f_artists = [] + f_artists = set() + for artist in artists: - aa = artist.split(",") - f_artists.extend(aa) + for a in artist: + f_artists.add(a) return f_artists - @staticmethod - def get_valid_name(name: str) -> str: - """ - returns a valid artist name - """ - - return "".join([i for i in name if i not in '/\\:*?"<>|']) - def __call__(self) -> list: """ Gets all artists with a given name. """ artists = self.get_all_artist_names() - results = process.extract(self.query, artists) + results = process.extract( + self.query, + artists, + scorer=fuzz.WRatio, + score_cutoff=Cutoff.artists, + limit=Limit.artists, + ) f_artists = [] for artist in results: aa = { "name": artist[0], - "image": self.get_valid_name(artist[0]) + ".webp", + "image": helpers.create_safe_name(artist[0]) + ".webp", } f_artists.append(aa) @@ -90,14 +117,36 @@ class SearchAlbums: Gets all albums with a given title. """ - artists = SearchArtists(self.query)() - a_albums = [] + albums = [a.title for a in api.ALBUMS] + results = process.extract( + self.query, + albums, + scorer=fuzz.WRatio, + score_cutoff=Cutoff.albums, + limit=Limit.albums, + ) + + return [api.ALBUMS[i[2]] for i in results] # get all artists that matched the query # for get all albums from the artists # get all albums that matched the query # return [**artist_albums **albums] + # recheck next and previous artist on play next or add to playlist + + +class GetTopArtistTracks: + def __init__(self, artist: str) -> None: + self.artist = artist + + def __call__(self) -> List[models.Track]: + """ + Gets all tracks from a given artist. + """ + + return [track for track in api.TRACKS if self.artist in track.artists] + def get_search_albums(query: str) -> List[models.Album]: """ diff --git a/server/app/models.py b/server/app/models.py index 5cc7d112..c4ac3bed 100644 --- a/server/app/models.py +++ b/server/app/models.py @@ -18,7 +18,7 @@ class Track: trackid: str title: str - artists: str + artists: list albumartist: str album: str folder: str diff --git a/src/components/Search/TracksGrid.vue b/src/components/Search/TracksGrid.vue index 0eddae54..56354a97 100644 --- a/src/components/Search/TracksGrid.vue +++ b/src/components/Search/TracksGrid.vue @@ -35,6 +35,7 @@ const emit = defineEmits(["loadMore"]); function loadMore() { counter += 5; + console.log("load more", counter); emit("loadMore", counter); }