Move server code to this repo (#95)

move server code to this repo
This commit is contained in:
Mungai Njoroge
2023-01-13 20:01:52 +03:00
committed by GitHub
parent dd257e919d
commit 198957bcae
318 changed files with 6259 additions and 16797 deletions
+30
View File
@@ -0,0 +1,30 @@
"""
This module combines all API blueprints into a single Flask app instance.
"""
from flask import Flask
from flask_cors import CORS
from app.api import album, artist, favorites, folder, playlist, search, track
from app.imgserver import imgbp as imgserver
def create_api():
"""
Creates the Flask instance, registers modules and registers all the API blueprints.
"""
app = Flask(__name__, static_url_path="")
CORS(app)
with app.app_context():
app.register_blueprint(album.albumbp)
app.register_blueprint(artist.artistbp)
app.register_blueprint(track.trackbp)
app.register_blueprint(search.searchbp)
app.register_blueprint(folder.folderbp)
app.register_blueprint(playlist.playlistbp)
app.register_blueprint(favorites.favbp)
app.register_blueprint(imgserver)
return app
+151
View File
@@ -0,0 +1,151 @@
"""
Contains all the album routes.
"""
from dataclasses import asdict
from flask import Blueprint, request
from app import utils
from app.db.sqlite.albums import SQLiteAlbumMethods as adb
from app.db.sqlite.favorite import SQLiteFavoriteMethods as favdb
from app.db.store import Store
from app.models import FavType, Track
get_album_by_id = adb.get_album_by_id
get_albums_by_albumartist = adb.get_albums_by_albumartist
check_is_fav = favdb.check_is_favorite
albumbp = Blueprint("album", __name__, url_prefix="")
@albumbp.route("/album", methods=["POST"])
def get_album():
"""Returns all the tracks in the given album."""
data = request.get_json()
error_msg = {"msg": "No hash provided"}
if data is None:
return error_msg, 400
try:
albumhash = data["hash"]
except KeyError:
return error_msg, 400
error_msg = {"error": "Album not created yet."}
album = Store.get_album_by_hash(albumhash)
if album is None:
return error_msg, 204
tracks = Store.get_tracks_by_albumhash(albumhash)
if tracks is None:
return error_msg, 404
if len(tracks) == 0:
return error_msg, 204
def get_album_genres(tracks: list[Track]):
genres = set()
for track in tracks:
if track.genre is not None:
genres.update(track.genre)
return list(genres)
album.genres = get_album_genres(tracks)
tracks = utils.remove_duplicates(tracks)
album.count = len(tracks)
for track in tracks:
if track.date != "Unknown":
album.date = track.date
break
try:
album.duration = sum((t.duration for t in tracks))
except AttributeError:
album.duration = 0
if (
album.count == 1
and tracks[0].title == album.title
# and tracks[0].track == 1
# and tracks[0].disc == 1
):
album.is_single = True
else:
album.check_type()
album.is_favorite = check_is_fav(albumhash, FavType.album)
return {"tracks": tracks, "info": album}
@albumbp.route("/album/<albumhash>/tracks", methods=["GET"])
def get_album_tracks(albumhash: str):
"""
Returns all the tracks in the given album.
"""
tracks = Store.get_tracks_by_albumhash(albumhash)
tracks = [asdict(t) for t in tracks]
for t in tracks:
track = str(t["track"]).zfill(3)
t["pos"] = int(f"{t['disc']}{track}")
tracks = sorted(tracks, key=lambda t: t["pos"])
return {"tracks": tracks}
@albumbp.route("/album/from-artist", methods=["POST"])
def get_artist_albums():
data = request.get_json()
if data is None:
return {"msg": "No albumartist provided"}
albumartists: str = data["albumartists"] # type: ignore
limit: int = data.get("limit")
exclude: str = data.get("exclude")
albumartists: list[str] = albumartists.split(",") # type: ignore
albums = [
{
"artisthash": a,
"albums": Store.get_albums_by_albumartist(a, limit, exclude=exclude),
}
for a in albumartists
]
albums = [a for a in albums if len(a["albums"]) > 0]
return {"data": albums}
# @album_bp.route("/album/bio", methods=["POST"])
# def get_album_bio():
# """Returns the album bio for the given album."""
# data = request.get_json()
# album_hash = data["hash"]
# err_msg = {"bio": "No bio found"}
# album = instances.album_instance.find_album_by_hash(album_hash)
# if album is None:
# return err_msg, 404
# bio = FetchAlbumBio(album["title"], album["artist"])()
# if bio is None:
# return err_msg, 404
# return {"bio": bio}
+323
View File
@@ -0,0 +1,323 @@
"""
Contains all the artist(s) routes.
"""
from collections import deque
from flask import Blueprint, request
from app.db.store import Store
from app.models import Album, FavType, Track
from app.utils import remove_duplicates
from app.db.sqlite.favorite import SQLiteFavoriteMethods as favdb
artistbp = Blueprint("artist", __name__, url_prefix="/")
class CacheEntry:
"""
The cache entry class for the artists cache.
"""
def __init__(
self, artisthash: str, albumhashes: set[str], tracks: list[Track]
) -> None:
self.albums: list[Album] = []
self.tracks: list[Track] = []
self.artisthash: str = artisthash
self.albumhashes: set[str] = albumhashes
if len(tracks) > 0:
self.tracks: list[Track] = tracks
self.type_checked = False
self.albums_fetched = False
class ArtistsCache:
"""
Holds artist page cache.
"""
artists: deque[CacheEntry] = deque(maxlen=6)
@classmethod
def get_albums_by_artisthash(cls, artisthash: str):
"""
Returns the cached albums for the given artisthash.
"""
for (index, albums) in enumerate(cls.artists):
if albums.artisthash == artisthash:
return (albums.albums, index)
return ([], -1)
@classmethod
def albums_cached(cls, artisthash: str) -> bool:
"""
Returns True if the artist is in the cache.
"""
for entry in cls.artists:
if entry.artisthash == artisthash and len(entry.albums) > 0:
return True
return False
@classmethod
def albums_fetched(cls, artisthash: str):
"""
Checks if the albums have been fetched for the given artisthash.
"""
for entry in cls.artists:
if entry.artisthash == artisthash:
return entry.albums_fetched
@classmethod
def tracks_cached(cls, artisthash: str) -> bool:
"""
Checks if the tracks have been cached for the given artisthash.
"""
for entry in cls.artists:
if entry.artisthash == artisthash and len(entry.tracks) > 0:
return True
return False
@classmethod
def add_entry(cls, artisthash: str, albumhashes: set[str], tracks: list[Track]):
"""
Adds a new entry to the cache.
"""
cls.artists.append(CacheEntry(artisthash, albumhashes, tracks))
@classmethod
def get_tracks(cls, artisthash: str):
"""
Returns the cached tracks for the given artisthash.
"""
entry = [a for a in cls.artists if a.artisthash == artisthash][0]
return entry.tracks
@classmethod
def get_albums(cls, artisthash: str):
"""
Returns the cached albums for the given artisthash.
"""
entry = [a for a in cls.artists if a.artisthash == artisthash][0]
albums = [Store.get_album_by_hash(h) for h in entry.albumhashes]
entry.albums = [album for album in albums if album is not None]
store_albums = Store.get_albums_by_artisthash(artisthash)
all_albums_hash = "-".join([a.albumhash for a in entry.albums])
for album in store_albums:
if album.albumhash not in all_albums_hash:
entry.albums.append(album)
entry.albums_fetched = True
@classmethod
def process_album_type(cls, artisthash: str):
"""
Checks the cached albums type for the given artisthash.
"""
entry = [a for a in cls.artists if a.artisthash == artisthash][0]
for album in entry.albums:
album.check_type()
album_tracks = Store.get_tracks_by_albumhash(album.albumhash)
album_tracks = remove_duplicates(album_tracks)
album.check_is_single(album_tracks)
entry.type_checked = True
def add_albums_to_cache(artisthash: str):
"""
Fetches albums and adds them to the cache.
"""
tracks = Store.get_tracks_by_artist(artisthash)
if len(tracks) == 0:
return False
albumhashes = set(t.albumhash for t in tracks)
ArtistsCache.add_entry(artisthash, albumhashes, [])
return True
# =======================================================
# ===================== ROUTES ==========================
# =======================================================
@artistbp.route("/artist/<artisthash>", methods=["GET"])
def get_artist(artisthash: str):
"""
Get artist data.
"""
limit = request.args.get("limit")
if limit is None:
limit = 6
limit = int(limit)
artist = Store.get_artist_by_hash(artisthash)
if artist is None:
return {"error": "Artist not found"}, 404
tracks_cached = ArtistsCache.tracks_cached(artisthash)
if tracks_cached:
tracks = ArtistsCache.get_tracks(artisthash)
else:
tracks = Store.get_tracks_by_artist(artisthash)
albumhashes = set(t.albumhash for t in tracks)
hashes_from_albums = set(
a.albumhash for a in Store.get_albums_by_artisthash(artisthash)
)
albumhashes = albumhashes.union(hashes_from_albums)
ArtistsCache.add_entry(artisthash, albumhashes, tracks)
tcount = len(tracks)
acount = Store.count_albums_by_artisthash(artisthash)
if acount == 0 and tcount < 10:
limit = tcount
artist.trackcount = tcount
artist.albumcount = acount
artist.duration = sum(t.duration for t in tracks)
artist.is_favorite = favdb.check_is_favorite(artisthash, FavType.artist)
return {"artist": artist, "tracks": tracks[:limit]}
@artistbp.route("/artist/<artisthash>/albums", methods=["GET"])
def get_artist_albums(artisthash: str):
limit = request.args.get("limit")
if limit is None:
limit = 6
return_all = request.args.get("all")
limit = int(limit)
all_albums = []
is_cached = ArtistsCache.albums_cached(artisthash)
if not is_cached:
add_albums_to_cache(artisthash)
albums_fetched = ArtistsCache.albums_fetched(artisthash)
if not albums_fetched:
ArtistsCache.get_albums(artisthash)
all_albums, index = ArtistsCache.get_albums_by_artisthash(artisthash)
if not ArtistsCache.artists[index].type_checked:
ArtistsCache.process_album_type(artisthash)
singles = [a for a in all_albums if a.is_single]
eps = [a for a in all_albums if a.is_EP]
def remove_EPs_and_singles(albums: list[Album]):
albums = [a for a in albums if not a.is_EP]
albums = [a for a in albums if not a.is_single]
return albums
albums = filter(lambda a: artisthash in a.albumartisthash, all_albums)
albums = list(albums)
albums = remove_EPs_and_singles(albums)
appearances = filter(lambda a: artisthash not in a.albumartisthash, all_albums)
appearances = list(appearances)
appearances = remove_EPs_and_singles(appearances)
artist = Store.get_artist_by_hash(artisthash)
if return_all is not None:
limit = len(all_albums)
return {
"artistname": artist.name,
"albums": albums[:limit],
"singles": singles[:limit],
"eps": eps[:limit],
"appearances": appearances[:limit],
}
@artistbp.route("/artist/<artisthash>/tracks", methods=["GET"])
def get_artist_tracks(artisthash: str):
"""
Returns all artists by a given artist.
"""
tracks = Store.get_tracks_by_artist(artisthash)
return {"tracks": tracks}
# artist = Store.get_artist_by_hash(artisthash)
# if artist is None:
# return {"error": "Artist not found"}, 404
# return {"albums": albums[:limit]}
# @artist_bp.route("/artist/<artist>")
# @cache.cached()
# def get_artist_data(artist: str):
# """Returns the artist's data, tracks and albums"""
# artist = urllib.parse.unquote(artist)
# artist_obj = instances.artist_instance.get_artists_by_name(artist)
# def get_artist_tracks():
# songs = instances.tracks_instance.find_songs_by_artist(artist)
# return songs
# artist_songs = get_artist_tracks()
# songs = utils.remove_duplicates(artist_songs)
# def get_artist_albums():
# artist_albums = []
# albums_with_count = []
# albums = instances.tracks_instance.find_songs_by_albumartist(artist)
# for song in albums:
# if song["album"] not in artist_albums:
# artist_albums.append(song["album"])
# for album in artist_albums:
# count = 0
# length = 0
# for song in artist_songs:
# if song["album"] == album:
# count = count + 1
# length = length + song["length"]
# album_ = {"title": album, "count": count, "length": length}
# albums_with_count.append(album_)
# return albums_with_count
# return {
# "artist": artist_obj,
# "songs": songs,
# "albums": get_artist_albums()
# }
+210
View File
@@ -0,0 +1,210 @@
from flask import Blueprint, request
from app.db.sqlite.favorite import SQLiteFavoriteMethods as favdb
from app.db.store import Store
from app.models import FavType
from app.utils import UseBisection
favbp = Blueprint("favorite", __name__, url_prefix="/")
def remove_none(items: list):
return [i for i in items if i is not None]
@favbp.route("/favorite/add", methods=["POST"])
def add_favorite():
"""
Adds a favorite to the database.
"""
data = request.get_json()
if data is None:
return {"error": "No data provided"}, 400
itemhash = data.get("hash")
itemtype = data.get("type")
favdb.insert_one_favorite(itemtype, itemhash)
if itemtype == FavType.track:
Store.add_fav_track(itemhash)
return {"msg": "Added to favorites"}
@favbp.route("/favorite/remove", methods=["POST"])
def remove_favorite():
"""
Removes a favorite from the database.
"""
data = request.get_json()
if data is None:
return {"error": "No data provided"}, 400
itemhash = data.get("hash")
itemtype = data.get("type")
favdb.delete_favorite(itemtype, itemhash)
if itemtype == FavType.track:
Store.remove_fav_track(itemhash)
return {"msg": "Removed from favorites"}
@favbp.route("/albums/favorite")
def get_favorite_albums():
limit = request.args.get("limit")
if limit is None:
limit = 6
limit = int(limit)
albums = favdb.get_fav_albums()
albumhashes = [a[1] for a in albums]
albumhashes.reverse()
src_albums = sorted(Store.albums, key=lambda x: x.albumhash)
fav_albums = UseBisection(src_albums, "albumhash", albumhashes)()
fav_albums = remove_none(fav_albums)
if limit == 0:
limit = len(albums)
return {"albums": fav_albums[:limit]}
@favbp.route("/tracks/favorite")
def get_favorite_tracks():
limit = request.args.get("limit")
if limit is None:
limit = 6
limit = int(limit)
tracks = favdb.get_fav_tracks()
trackhashes = [t[1] for t in tracks]
trackhashes.reverse()
src_tracks = sorted(Store.tracks, key=lambda x: x.trackhash)
tracks = UseBisection(src_tracks, "trackhash", trackhashes)()
tracks = remove_none(tracks)
if limit == 0:
limit = len(tracks)
return {"tracks": tracks[:limit]}
@favbp.route("/artists/favorite")
def get_favorite_artists():
limit = request.args.get("limit")
if limit is None:
limit = 6
limit = int(limit)
artists = favdb.get_fav_artists()
artisthashes = [a[1] for a in artists]
artisthashes.reverse()
src_artists = sorted(Store.artists, key=lambda x: x.artisthash)
artists = UseBisection(src_artists, "artisthash", artisthashes)()
artists = remove_none(artists)
if limit == 0:
limit = len(artists)
return {"artists": artists[:limit]}
@favbp.route("/favorites")
def get_all_favorites():
"""
Returns all the favorites in the database.
"""
track_limit = request.args.get("track_limit")
album_limit = request.args.get("album_limit")
artist_limit = request.args.get("artist_limit")
if track_limit is None:
track_limit = 6
if album_limit is None:
album_limit = 6
if artist_limit is None:
artist_limit = 6
track_limit = int(track_limit)
album_limit = int(album_limit)
artist_limit = int(artist_limit)
favs = favdb.get_all()
favs.reverse()
tracks = []
albums = []
artists = []
for fav in favs:
if (
len(tracks) >= track_limit
and len(albums) >= album_limit
and len(artists) >= artist_limit
):
break
if fav[2] == FavType.track:
tracks.append(fav[1])
elif fav[2] == FavType.album:
albums.append(fav[1])
elif fav[2] == FavType.artist:
artists.append(fav[1])
src_tracks = sorted(Store.tracks, key=lambda x: x.trackhash)
src_albums = sorted(Store.albums, key=lambda x: x.albumhash)
src_artists = sorted(Store.artists, key=lambda x: x.artisthash)
tracks = tracks[:track_limit]
albums = albums[:album_limit]
artists = artists[:artist_limit]
tracks = UseBisection(src_tracks, "trackhash", tracks)()
albums = UseBisection(src_albums, "albumhash", albums)()
artists = UseBisection(src_artists, "artisthash", artists)()
tracks = remove_none(tracks)
albums = remove_none(albums)
artists = remove_none(artists)
return {
"tracks": tracks,
"albums": albums,
"artists": artists,
}
@favbp.route("/favorites/check")
def check_favorite():
"""
Checks if a favorite exists in the database.
"""
itemhash = request.args.get("hash")
itemtype = request.args.get("type")
if itemhash is None:
return {"error": "No hash provided"}, 400
if itemtype is None:
return {"error": "No type provided"}, 400
exists = favdb.check_is_favorite(itemhash, itemtype)
return {"is_favorite": exists}
+32
View File
@@ -0,0 +1,32 @@
"""
Contains all the folder routes.
"""
from flask import Blueprint, request
from app import settings
from app.lib.folderslib import GetFilesAndDirs
folderbp = Blueprint("folder", __name__, url_prefix="/")
@folderbp.route("/folder", methods=["POST"])
def get_folder_tree():
"""
Returns a list of all the folders and tracks in the given folder.
"""
data = request.get_json()
if data is not None:
req_dir: str = data["folder"]
else:
req_dir = settings.HOME_DIR
if req_dir == "$home":
req_dir = settings.HOME_DIR
tracks, folders = GetFilesAndDirs(req_dir)()
return {
"tracks": tracks,
"folders": sorted(folders, key=lambda i: i.name),
}
+226
View File
@@ -0,0 +1,226 @@
"""
All playlist-related routes.
"""
import json
from datetime import datetime
from flask import Blueprint, request
from PIL import UnidentifiedImageError
from app import models, serializer
from app.db.sqlite.playlists import SQLitePlaylistMethods
from app.db.store import Store
from app.lib import playlistlib
from app.utils import create_new_date, remove_duplicates
playlistbp = Blueprint("playlist", __name__, url_prefix="/")
PL = SQLitePlaylistMethods
insert_one_playlist = PL.insert_one_playlist
get_playlist_by_name = PL.get_playlist_by_name
count_playlist_by_name = PL.count_playlist_by_name
get_all_playlists = PL.get_all_playlists
get_playlist_by_id = PL.get_playlist_by_id
tracks_to_playlist = PL.add_tracks_to_playlist
add_artist_to_playlist = PL.add_artist_to_playlist
update_playlist = PL.update_playlist
delete_playlist = PL.delete_playlist
# get_tracks_by_trackhashes = SQLiteTrackMethods.get_tracks_by_trackhashes
@playlistbp.route("/playlists", methods=["GET"])
def send_all_playlists():
"""
Gets all the playlists.
"""
playlists = get_all_playlists()
playlists = list(playlists)
playlists.sort(
key=lambda p: datetime.strptime(p.last_updated, "%Y-%m-%d %H:%M:%S"),
reverse=True,
)
return {"data": playlists}
@playlistbp.route("/playlist/new", methods=["POST"])
def create_playlist():
"""
Creates a new playlist. Accepts POST method with a JSON body.
"""
data = request.get_json()
if data is None:
return {"error": "Playlist name not provided"}, 400
existing_playlist_count = count_playlist_by_name(data["name"])
if existing_playlist_count > 0:
return {"error": "Playlist already exists"}, 409
playlist = {
"artisthashes": json.dumps([]),
"banner_pos": 50,
"has_gif": 0,
"image": None,
"last_updated": create_new_date(),
"name": data["name"],
"trackhashes": json.dumps([]),
}
playlist = insert_one_playlist(playlist)
if playlist is None:
return {"error": "Playlist could not be created"}, 500
return {"playlist": playlist}, 201
@playlistbp.route("/playlist/<playlist_id>/add", methods=["POST"])
def add_track_to_playlist(playlist_id: str):
"""
Takes a playlist ID and a track hash, and adds the track to the playlist
"""
data = request.get_json()
if data is None:
return {"error": "Track hash not provided"}, 400
trackhash = data["track"]
insert_count = tracks_to_playlist(int(playlist_id), [trackhash])
if insert_count == 0:
return {"error": "Track already exists in playlist"}, 409
add_artist_to_playlist(int(playlist_id), trackhash)
return {"msg": "Done"}, 200
@playlistbp.route("/playlist/<playlistid>")
def get_playlist(playlistid: str):
"""
Gets a playlist by id, and if it exists, it gets all the tracks in the playlist and returns them.
"""
playlist = get_playlist_by_id(int(playlistid))
if playlist is None:
return {"msg": "Playlist not found"}, 404
tracks = Store.get_tracks_by_trackhashes(list(playlist.trackhashes))
tracks = remove_duplicates(tracks)
duration = sum(t.duration for t in tracks)
playlist.last_updated = serializer.date_string_to_time_passed(playlist.last_updated)
playlist.duration = duration
return {"info": playlist, "tracks": tracks}
@playlistbp.route("/playlist/<playlistid>/update", methods=["PUT"])
def update_playlist_info(playlistid: str):
if playlistid is None:
return {"error": "Playlist ID not provided"}, 400
db_playlist = get_playlist_by_id(int(playlistid))
if db_playlist is None:
return {"error": "Playlist not found"}, 404
image = None
if "image" in request.files:
image = request.files["image"]
data = request.form
playlist = {
"id": int(playlistid),
"artisthashes": json.dumps([]),
"banner_pos": db_playlist.banner_pos,
"has_gif": 0,
"image": db_playlist.image,
"last_updated": create_new_date(),
"name": str(data.get("name")).strip(),
"trackhashes": json.dumps([]),
}
if image:
try:
playlist["image"] = playlistlib.save_p_image(image, playlistid)
if image.content_type == "image/gif":
playlist["has_gif"] = 1
# reset banner position to center.
playlist["banner_pos"] = 50
PL.update_banner_pos(int(playlistid), 50)
except UnidentifiedImageError:
return {"error": "Failed: Invalid image"}, 400
p_tuple = (*playlist.values(),)
print("banner pos:", playlist["banner_pos"])
update_playlist(int(playlistid), playlist)
playlist = models.Playlist(*p_tuple)
playlist.last_updated = serializer.date_string_to_time_passed(playlist.last_updated)
return {
"data": playlist,
}
# @playlist_bp.route("/playlist/artists", methods=["POST"])
# def get_playlist_artists():
# data = request.get_json()
# pid = data["pid"]
# artists = playlistlib.GetPlaylistArtists(pid)()
# return {"data": artists}
@playlistbp.route("/playlist/delete", methods=["POST"])
def remove_playlist():
"""
Deletes a playlist by ID.
"""
message = {"error": "Playlist ID not provided"}
data = request.get_json()
if data is None:
return message, 400
try:
pid = data["pid"]
except KeyError:
return message, 400
delete_playlist(pid)
return {"msg": "Done"}, 200
@playlistbp.route("/playlist/<pid>/set-image-pos", methods=["POST"])
def update_image_position(pid: int):
data = request.get_json()
message = {"msg": "No data provided"}
if data is None:
return message, 400
try:
pos = data["pos"]
except KeyError:
return message, 400
PL.update_banner_pos(pid, pos)
return {"msg": "Image position saved"}, 200
+218
View File
@@ -0,0 +1,218 @@
"""
Contains all the search routes.
"""
from flask import Blueprint, request
from app import models, utils
from app.db.store import Store
from app.lib import searchlib
searchbp = Blueprint("search", __name__, url_prefix="/")
SEARCH_COUNT = 12
"""The max amount of items to return per request"""
class SearchResults:
"""
Holds all the search results.
"""
query: str = ""
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.tracks: list[models.Track] = []
self.query = query
SearchResults.query = query
def search_tracks(self):
"""Calls :class:`SearchTracks` which returns the tracks that fuzzily match
the search terms. Then adds them to the `SearchResults` store.
"""
self.tracks = Store.tracks
tracks = searchlib.SearchTracks(self.tracks, self.query)()
if len(tracks) == 0:
return []
tracks = utils.remove_duplicates(tracks)
SearchResults.tracks = tracks
return tracks
def search_artists(self):
"""Calls :class:`SearchArtists` which returns the artists that fuzzily match
the search term. Then adds them to the `SearchResults` store.
"""
# self.artists = utils.Get.get_all_artists()
artists = [a.name for a in Store.artists]
artists = searchlib.SearchArtists(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.
"""
# albums = utils.Get.get_all_albums()
albums = Store.albums
albums = searchlib.SearchAlbums(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.
# """
# playlists = utils.Get.get_all_playlists()
# playlists = [serializer.Playlist(playlist) for playlist in playlists]
# playlists = searchlib.SearchPlaylists(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()
@searchbp.route("/search/tracks", methods=["GET"])
def search_tracks():
"""
Searches for tracks that match the search query.
"""
query = request.args.get("q")
if not query:
return {"error": "No query provided"}, 400
tracks = DoSearch(query).search_tracks()
return {
"tracks": tracks[:SEARCH_COUNT],
"more": len(tracks) > SEARCH_COUNT,
}
@searchbp.route("/search/albums", methods=["GET"])
def search_albums():
"""
Searches for albums.
"""
query = request.args.get("q")
if not query:
return {"error": "No query provided"}, 400
tracks = DoSearch(query).search_albums()
return {
"albums": tracks[:SEARCH_COUNT],
"more": len(tracks) > SEARCH_COUNT,
}
@searchbp.route("/search/artists", methods=["GET"])
def search_artists():
"""
Searches for artists.
"""
query = request.args.get("q")
if not query:
return {"error": "No query provided"}, 400
artists = DoSearch(query).search_artists()
return {
"artists": artists[:SEARCH_COUNT],
"more": len(artists) > SEARCH_COUNT,
}
# @searchbp.route("/search/playlists", methods=["GET"])
# def search_playlists():
# """
# Searches for playlists.
# """
# query = request.args.get("q")
# if not query:
# return {"error": "No query provided"}, 400
# playlists = DoSearch(query).search_playlists()
# return {
# "playlists": playlists[:SEARCH_COUNT],
# "more": len(playlists) > SEARCH_COUNT,
# }
@searchbp.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],
}
@searchbp.route("/search/loadmore")
def search_load_more():
"""
Returns more songs, albums or artists from a search query.
"""
s_type = request.args.get("type")
index = int(request.args.get("index") or 0)
if s_type == "tracks":
t = SearchResults.tracks
return {
"tracks": t[index : index + SEARCH_COUNT],
"more": len(t) > index + SEARCH_COUNT,
}
elif s_type == "albums":
a = SearchResults.albums
return {
"albums": a[index : index + SEARCH_COUNT],
"more": len(a) > index + SEARCH_COUNT,
}
elif s_type == "artists":
a = SearchResults.artists
return {
"artists": a[index : index + SEARCH_COUNT],
"more": len(a) > index + SEARCH_COUNT,
}
+33
View File
@@ -0,0 +1,33 @@
"""
Contains all the track routes.
"""
from flask import Blueprint, send_file
from app.db.store import Store
trackbp = Blueprint("track", __name__, url_prefix="/")
@trackbp.route("/file/<trackhash>")
def send_track_file(trackhash: str):
"""
Returns an audio file that matches the passed id to the client.
Falls back to track hash if id is not found.
"""
msg = {"msg": "File Not Found"}
if trackhash is None:
return msg, 404
try:
track = Store.get_tracks_by_trackhashes([trackhash])[0]
except IndexError:
track = None
if track is None:
return msg, 404
audio_type = track.filepath.rsplit(".", maxsplit=1)[-1]
try:
return send_file(track.filepath, mimetype=f"audio/{audio_type}")
except FileNotFoundError:
return msg, 404