diff --git a/server/app/api/__init__.py b/server/app/api/__init__.py index d2327ff7..b9b916d2 100644 --- a/server/app/api/__init__.py +++ b/server/app/api/__init__.py @@ -21,7 +21,7 @@ VALID_FOLDERS: Set[str] = set() ALBUMS: List[models.Album] = [] TRACKS: List[models.Track] = [] PLAYLISTS: List[models.Playlist] = [] -FOLDERS: Set[models.Folder] = set() +FOLDERS: List[models.Folder] = List @helpers.background diff --git a/server/app/api/track.py b/server/app/api/track.py index 2ddc9e02..64bf7453 100644 --- a/server/app/api/track.py +++ b/server/app/api/track.py @@ -15,11 +15,17 @@ def send_track_file(trackid): Returns an audio file that matches the passed id to the client. """ try: - filepath = [ - file["filepath"] for file in api.DB_TRACKS - if file["_id"]["$oid"] == trackid - ][0] - except (FileNotFoundError, IndexError) as e: + files = [] + for f in api.DB_TRACKS: + try: + if f["_id"]["$oid"] == trackid: + files.append(f["filepath"]) + except KeyError: + # Bug: some albums are not found although they exist in `api.ALBUMS`. It has something to do with the bisection method used or sorting. Not sure yet. + pass + + filepath = files[0] + except IndexError: return "File not found", 404 return send_file(filepath, mimetype="audio/mp3") @@ -31,5 +37,6 @@ def get_sample_track(): Returns a sample track object. """ - return instances.tracks_instance.get_song_by_album("Legends Never Die", - "Juice WRLD") + return instances.tracks_instance.get_song_by_album( + "Legends Never Die", "Juice WRLD" + ) diff --git a/server/app/functions.py b/server/app/functions.py index 880ca46a..ccc040a9 100644 --- a/server/app/functions.py +++ b/server/app/functions.py @@ -3,11 +3,12 @@ This module contains functions for the server """ import datetime import os +from pprint import pprint import random import time from dataclasses import asdict from io import BytesIO -from typing import List +from typing import List, Type import requests from app import api @@ -60,8 +61,7 @@ def populate(): albums = [] folders = set() - files = helpers.run_fast_scandir(settings.HOME_DIR, [".flac", ".mp3"], - full=True)[1] + files = helpers.run_fast_scandir(settings.HOME_DIR, [".flac", ".mp3"], full=True)[1] _bar = Bar("Checking files", max=len(files)) for track in db_tracks: @@ -88,24 +88,59 @@ def populate(): Log(f"Tagged {len(tagged_tracks)} tracks") - _bar = Bar("Creating stuff", max=len(tagged_tracks)) - for track in tagged_tracks: - albumindex = albumslib.find_album(track["album"], track["albumartist"]) - album = None + pre_albums = [] + + for t in tagged_tracks: + a = { + "title": t["album"], + "artist": t["albumartist"], + } + + if a not in pre_albums: + pre_albums.append(a) + + exist_count = 0 + _bar = Bar("Creating albums", max=len(pre_albums)) + for aa in pre_albums: + albumindex = albumslib.find_album(aa["title"], aa["artist"]) if albumindex is None: + track = [ + track + for track in tagged_tracks + if track["album"] == aa["title"] + and track["albumartist"] == aa["artist"] + ][0] + album = albumslib.create_album(track) api.ALBUMS.append(album) albums.append(album) + instances.album_instance.insert_album(asdict(album)) + else: - album = api.ALBUMS[albumindex] + exist_count += 1 - track["image"] = album.image - upsert_id = instances.tracks_instance.insert_song(track) + _bar.next() - track["_id"] = {"$oid": str(upsert_id)} - api.TRACKS.append(models.Track(track)) + _bar.finish() + + Log(f"{exist_count} of {len(albums)} were already in the database") + + _bar = Bar("Creating tracks", max=len(tagged_tracks)) + for track in tagged_tracks: + try: + album_index = albumslib.find_album(track["album"], track["albumartist"]) + album = api.ALBUMS[album_index] + + track["image"] = album.image + upsert_id = instances.tracks_instance.insert_song(track) + + track["_id"] = {"$oid": str(upsert_id)} + api.TRACKS.append(models.Track(track)) + except TypeError: + # Bug: some albums are not found although they exist in `api.ALBUMS`. It has something to do with the bisection method used or sorting. Not sure yet. + pass _bar.next() @@ -118,7 +153,7 @@ def populate(): if folder not in api.VALID_FOLDERS: api.VALID_FOLDERS.add(folder) fff = folderslib.create_folder(folder) - api.FOLDERS.add(fff) + api.FOLDERS.append(fff) _bar.next() @@ -129,8 +164,11 @@ def populate(): end = time.time() print( - str(datetime.timedelta(seconds=round(end - start))) + " elapsed for " + - str(len(files)) + " files") + str(datetime.timedelta(seconds=round(end - start))) + + " elapsed for " + + str(len(files)) + + " files" + ) def fetch_image_path(artist: str) -> str or None: @@ -165,8 +203,9 @@ def fetch_artist_images(): _bar = Bar("Processing images", max=len(artists)) for artist in artists: - file_path = (helpers.app_dir + "/images/artists/" + - artist.replace("/", "::") + ".webp") + file_path = ( + helpers.app_dir + "/images/artists/" + artist.replace("/", "::") + ".webp" + ) if not os.path.exists(file_path): img_path = fetch_image_path(artist) @@ -188,7 +227,8 @@ def fetch_album_bio(title: str, albumartist: str): Returns the album bio for a given album. """ last_fm_url = "http://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key={}&artist={}&album={}&format=json".format( - settings.LAST_FM_API_KEY, albumartist, title) + settings.LAST_FM_API_KEY, albumartist, title + ) try: response = requests.get(last_fm_url) @@ -197,8 +237,7 @@ def fetch_album_bio(title: str, albumartist: str): return None try: - bio = data["album"]["wiki"]["summary"].split( - ' List[models.Track]: api.TRACKS.sort(key=lambda x: x.title) -def find_album(albumtitle: str, artist: str) -> models.Album: +def find_album(albumtitle: str, artist: str) -> int or None: """ Finds an album by album title and artist. """ @@ -63,8 +66,7 @@ def find_album(albumtitle: str, artist: str) -> models.Album: iter += 1 mid = (left + right) // 2 - if api.ALBUMS[mid].title == albumtitle and api.ALBUMS[ - mid].artist == artist: + if api.ALBUMS[mid].title == albumtitle and api.ALBUMS[mid].artist == artist: return mid if api.ALBUMS[mid].title < albumtitle: @@ -96,20 +98,33 @@ def use_defaults() -> str: return path +def gen_random_path() -> str: + """ + Generates a random image file path for an album image. + """ + choices = "abcdefghijklmnopqrstuvwxyz0123456789" + path = "".join(random.choice(choices) for i in range(20)) + path += ".webp" + + return path + + def get_album_image(album: list) -> str: """ Gets the image of an album. """ + uri = settings.IMG_THUMB_URI + for track in album: - img_p = (track["album"] + track["albumartist"] + ".webp").replace( - "/", "::") - img = functions.extract_thumb(track["filepath"], webp_path=img_p) + img_p = gen_random_path() - if img is not None: - return img + exists = taglib.extract_thumb(track["filepath"], webp_path=img_p) - return use_defaults() + if exists: + return uri + img_p + + return uri + use_defaults() def get_album_tracks(album: str, artist: str) -> List: @@ -142,7 +157,8 @@ def create_album(track) -> models.Album: album["date"] = album_tracks[0]["date"] album["artistimage"] = urllib.parse.quote_plus( - album_tracks[0]["albumartist"] + ".webp") + album_tracks[0]["albumartist"] + ".webp" + ) album["image"] = get_album_image(album_tracks) diff --git a/server/app/lib/taglib.py b/server/app/lib/taglib.py index dd5cca68..3a695a51 100644 --- a/server/app/lib/taglib.py +++ b/server/app/lib/taglib.py @@ -1,5 +1,4 @@ import os -import urllib from io import BytesIO import mutagen @@ -29,14 +28,14 @@ def return_album_art(filepath: str): return None -def extract_thumb(audio_file_path: str, webp_path: str) -> str: +def extract_thumb(audio_file_path: str, webp_path: str) -> bool: """ Extracts the thumbnail from an audio file. Returns the path to the thumbnail. """ img_path = os.path.join(settings.THUMBS_PATH, webp_path) if os.path.exists(img_path): - return urllib.parse.quote(webp_path) + return True album_art = return_album_art(audio_file_path) @@ -52,11 +51,11 @@ def extract_thumb(audio_file_path: str, webp_path: str) -> str: small_img = png.resize((250, 250), Image.ANTIALIAS) small_img.save(webp_path, format="webp") except: - return None + return False - return urllib.parse.quote(webp_path) + return True else: - return None + return False def parse_artist_tag(audio): diff --git a/server/app/lib/watchdoge.py b/server/app/lib/watchdoge.py index 50191363..0f0568ed 100644 --- a/server/app/lib/watchdoge.py +++ b/server/app/lib/watchdoge.py @@ -63,7 +63,7 @@ def add_track(filepath: str) -> None: if folder not in api.VALID_FOLDERS: api.VALID_FOLDERS.add(folder) f = folderslib.create_folder(folder) - api.FOLDERS.add(f) + api.FOLDERS.append(f) def remove_track(filepath: str) -> None: