diff --git a/server/app/api/search.py b/server/app/api/search.py index 4ba3834a..f4d2fc42 100644 --- a/server/app/api/search.py +++ b/server/app/api/search.py @@ -24,62 +24,91 @@ class SearchResults: """ query: str = "" - - class Tracks: - """ - Holds all the tracks search results. - """ - - results: List[models.Track] - - class Albums: - """ - Holds all the albums search results. - """ - - results: List[models.Album] - - class Artists: - """ - Holds all the artists search results. - """ - - results: List[models.Artist] + tracks: list[models.Track] + albums: list[models.Album] + playlists: list[models.Playlist] + artists: list[models.Artist] class DoSearch: + """Class containing the methods that perform searching.""" + def __init__(self, query: str) -> None: + """ + :param :str:`query`: the search query. + """ self.query = query - self.tracks = helpers.Get.get_all_tracks() - self.albums = helpers.Get.get_all_albums() - self.artists = helpers.Get.get_all_artists() - self.playlists = helpers.Get.get_all_playlists() + SearchResults.query = query def search_tracks(self): - results = searchlib.SearchTracks(self.tracks, self.query) + """Calls :class:`SearchTracks` which returns the tracks that fuzzily match + the search terms. Then adds them to the `SearchResults` store. + """ + self.tracks = helpers.Get.get_all_tracks() + tracks = searchlib.SearchTracks(self.tracks, self.query)() + SearchResults.tracks = tracks + + return tracks def search_artists(self): - SearchResults.Artists.results = searchlib.SearchArtists( - self.artists, self.query - ) + """Calls :class:`SearchArtists` which returns the artists that fuzzily match + the search term. Then adds them to the `SearchResults` store. + """ + self.artists = helpers.Get.get_all_artists() + artists = searchlib.SearchArtists(self.artists, self.query)() + SearchResults.artists = artists + + return artists + + def search_albums(self): + """Calls :class:`SearchAlbums` which returns the albums that fuzzily match + the search term. Then adds them to the `SearchResults` store. + """ + self.albums = helpers.Get.get_all_albums() + albums = searchlib.SearchAlbums(self.albums, self.query)() + SearchResults.albums = albums + + return albums + + def search_playlists(self): + """Calls :class:`SearchPlaylists` which returns the playlists that fuzzily match + the search term. Then adds them to the `SearchResults` store. + """ + self.playlists = helpers.Get.get_all_playlists() + playlists = searchlib.SearchPlaylists(self.playlists, self.query) + SearchResults.playlists = playlists + + return playlists + + def search_all(self): + """Calls all the search methods.""" + self.search_tracks() + self.search_albums() + self.search_artists() + self.search_playlists() @search_bp.route("/search/tracks", methods=["GET"]) def search_tracks(): """ - Searches for tracks. + Searches for tracks that match the search query. """ query = request.args.get("q") if not query: return {"error": "No query provided"}, 400 - results = searchlib.SearchTracks(query)() - SEARCH_RESULTS["tracks"] = results + if SearchResults.query == query and len(SearchResults.tracks) > 0: + return { + "tracks": SearchResults.tracks[:5], + "more": len(SearchResults.tracks) > 5, + }, 200 + + tracks = DoSearch(query).search_tracks() return { - "tracks": results[:5], - "more": len(results) > 5, + "tracks": tracks[:5], + "more": len(tracks) > 5, }, 200 @@ -93,12 +122,17 @@ def search_albums(): if not query: return {"error": "No query provided"}, 400 - results = searchlib.SearchAlbums(query)() - SEARCH_RESULTS["albums"] = results + if SearchResults.query == query and len(SearchResults.albums) > 0: + return { + "albums": SearchResults.albums[:6], + "more": len(SearchResults.albums) > 6, + }, 200 + + tracks = DoSearch(query).search_albums() return { - "albums": results[:6], - "more": len(results) > 6, + "albums": tracks[:6], + "more": len(tracks) > 6, }, 200 @@ -112,15 +146,41 @@ def search_artists(): if not query: return {"error": "No query provided"}, 400 - results = searchlib.SearchArtists(query)() - SEARCH_RESULTS["artists"] = results + if SearchResults.query == query and len(SearchResults.artists) > 0: + return { + "artists": SearchResults.artists[:6], + "more": len(SearchResults.artists) > 6, + }, 200 + + artists = DoSearch(query).search_artists() return { - "artists": results[:6], - "more": len(results) > 6, + "artists": artists[:6], + "more": len(artists) > 6, }, 200 +@search_bp.route("/search/top", methods=["GET"]) +def get_top_results(): + """ + Returns the top results for the search query. + """ + + query = request.args.get("q") + if not query: + return {"error": "No query provided"}, 400 + + DoSearch(query).search_all() + + max = 2 + return { + "tracks": SearchResults.tracks[:max], + "albums": SearchResults.albums[:max], + "artists": SearchResults.artists[:max], + "playlists": SearchResults.playlists[:max], + } + + @search_bp.route("/search") def search(): """ diff --git a/server/app/helpers.py b/server/app/helpers.py index 2db60988..dbe0981c 100644 --- a/server/app/helpers.py +++ b/server/app/helpers.py @@ -5,7 +5,7 @@ import os import random import threading from datetime import datetime -from typing import Dict +from typing import Dict, Set from typing import List from app import models @@ -185,9 +185,9 @@ class Get: a = instances.album_instance.get_all_albums() return [models.Album(a) for a in a] - def get_all_artists(self) -> set[str]: + def get_all_artists(self) -> Set[str]: tracks = self.get_all_tracks() - artists: set[str] = set() + artists: Set[str] = set() for track in tracks: for artist in track.artists: diff --git a/server/app/lib/albumslib.py b/server/app/lib/albumslib.py index 8a4e6baf..edaec8c6 100644 --- a/server/app/lib/albumslib.py +++ b/server/app/lib/albumslib.py @@ -157,21 +157,3 @@ def create_album(track: dict, tracklist: list) -> dict: # album["image"] = "".join(x for x in albumhash if x not in "\/:*?<>|") return album - - -def search_albums_by_name(query: str) -> List[models.Album]: - """ - Searches albums by album name. - """ - title_albums: List[models.Album] = [] - artist_albums: List[models.Album] = [] - - for album in api.ALBUMS: - if query.lower() in album.title.lower(): - title_albums.append(album) - - for album in api.ALBUMS: - if query.lower() in album.artist.lower(): - artist_albums.append(album) - - return [*title_albums, *artist_albums] diff --git a/server/app/lib/searchlib.py b/server/app/lib/searchlib.py index 6d0016b8..30744dc8 100644 --- a/server/app/lib/searchlib.py +++ b/server/app/lib/searchlib.py @@ -22,6 +22,7 @@ class Cutoff: tracks: int = 80 albums: int = 80 artists: int = 80 + playlists: int = 80 class Limit: @@ -32,18 +33,20 @@ class Limit: tracks: int = 50 albums: int = 50 artists: int = 50 + playlists: int = 50 class SearchTracks: - def __init__(self, query) -> None: + def __init__(self, tracks: List[models.Track], query: str) -> None: self.query = query + self.tracks = tracks def __call__(self) -> List[models.Track]: """ Gets all songs with a given title. """ - tracks = [track.title for track in api.TRACKS] + tracks = [track.title for track in self.tracks] results = process.extract( self.query, tracks, @@ -52,11 +55,11 @@ class SearchTracks: limit=Limit.tracks, ) - return [api.TRACKS[i[2]] for i in results] + return [self.tracks[i[2]] for i in results] class SearchArtists: - def __init__(self, artists: set[str], query) -> None: + def __init__(self, artists: set[str], query: str) -> None: self.query = query self.artists = artists @@ -85,24 +88,16 @@ class SearchArtists: class SearchAlbums: - def __init__(self, query) -> None: + def __init__(self, albums: List[models.Album], query: str) -> None: self.query = query - - def get_albums_by_name(self) -> List[models.Album]: - """ - Gets all albums with a given title. - """ - - albums = [album.title for album in api.ALBUMS] - results = process.extract(self.query, albums) - return [api.ALBUMS[i[2]] for i in results] + self.albums = albums def __call__(self) -> List[models.Album]: """ Gets all albums with a given title. """ - albums = [a.title for a in api.ALBUMS] + albums = [a.title for a in self.albums] results = process.extract( self.query, albums, @@ -111,7 +106,7 @@ class SearchAlbums: limit=Limit.albums, ) - return [api.ALBUMS[i[2]] for i in results] + return [self.albums[i[2]] for i in results] # get all artists that matched the query # for get all albums from the artists @@ -121,29 +116,19 @@ class SearchAlbums: # recheck next and previous artist on play next or add to playlist -class GetTopArtistTracks: - def __init__(self, artist: str) -> None: - self.artist = artist +class SearchPlaylists: + def __init__(self, playlists: List[models.Playlist], query: str) -> None: + self.playlists = playlists + self.query = query - def __call__(self) -> List[models.Track]: - """ - Gets all tracks from a given artist. - """ + def __call__(self) -> List[models.Playlist]: + playlists = [p.name for p in self.playlists] + results = process.extract( + self.query, + playlists, + scorer=fuzz.WRatio, + score_cutoff=Cutoff.playlists, + limit=Limit.playlists, + ) - return [track for track in api.TRACKS if self.artist in track.artists] - - -def get_search_albums(query: str) -> List[models.Album]: - """ - Gets all songs with a given album. - """ - return albumslib.search_albums_by_name(query) - - -def get_artists(artist: str) -> List[models.Track]: - """ - Gets all songs with a given artist. - """ - return [ - track for track in api.TRACKS if artist.lower() in str(track.artists).lower() - ] + return [self.playlists[i[2]] for i in results]