From 95c1524b68fcca896bb8073d9659c97039e83e22 Mon Sep 17 00:00:00 2001 From: geoffrey45 Date: Wed, 1 Feb 2023 13:34:53 +0300 Subject: [PATCH] feat: add --no-feat flag to disable extracting featured artists + support square brackets when extracting featured artists + remove feat artists from track title + fix dir browser for linux --- app/api/album.py | 10 +++------- app/api/folder.py | 44 +++++++++++++++++++++++++++++++------------- app/lib/searchlib.py | 2 +- app/models.py | 24 +++++++++++++++++------- app/settings.py | 12 +++++++----- app/utils.py | 20 +++++++++++++++----- manage.py | 25 ++++++++++++++++++++----- swingmusic.spec | 2 +- 8 files changed, 95 insertions(+), 44 deletions(-) diff --git a/app/api/album.py b/app/api/album.py index 99e3bb5..01c0f9a 100644 --- a/app/api/album.py +++ b/app/api/album.py @@ -72,12 +72,9 @@ def get_album(): 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.check_is_single(tracks) + + if album.is_single: album.is_single = True else: album.check_type() @@ -129,7 +126,6 @@ def get_artist_albums(): return {"data": albums} - # @album_bp.route("/album/bio", methods=["POST"]) # def get_album_bio(): # """Returns the album bio for the given album.""" diff --git a/app/api/folder.py b/app/api/folder.py index c87c8e3..370070a 100644 --- a/app/api/folder.py +++ b/app/api/folder.py @@ -54,6 +54,13 @@ def get_folder_tree(): "tracks": [], } + if is_windows(): + req_dir = req_dir + "/" + # TODO: Test this on Windows + else: + req_dir = "/" + req_dir + "/" if not req_dir.startswith("/") else req_dir + "/" + + print(req_dir) tracks, folders = GetFilesAndDirs(req_dir)() return { @@ -62,12 +69,20 @@ def get_folder_tree(): } -def get_all_drives(): +def get_all_drives(is_win: bool = False): """ - Returns a list of all the drives on a windows machine. + Returns a list of all the drives on a Windows machine. """ - drives = psutil.disk_partitions() - return [d.mountpoint for d in drives] + drives = psutil.disk_partitions(all=False) + drives = [d.mountpoint for d in drives] + + if is_win: + drives = [win_replace_slash(d) for d in drives] + else: + remove = ["/boot", "/boot/efi", "/tmp"] + drives = [d for d in drives if d not in remove] + + return drives @api.route("/folder/dir-browser", methods=["POST"]) @@ -85,15 +100,19 @@ def list_folders(): if req_dir == "$home": # req_dir = settings.USER_HOME_DIR - if is_win: - return { - "folders": [ - {"name": win_replace_slash(d), "path": win_replace_slash(d)} - for d in get_all_drives() - ] - } + # if is_win: + return { + "folders": [ + {"name": d, "path": d} + for d in get_all_drives(is_win=is_win) + ] + } - req_dir = req_dir + "/" + if is_win: + req_dir = req_dir + "/" + else: + req_dir = "/" + req_dir + "/" + req_dir = str(Path(req_dir).resolve()) try: entries = os.scandir(req_dir) @@ -109,7 +128,6 @@ def list_folders(): "folders": sorted(dirs, key=lambda i: i["name"]), } - # todo: # - handle showing windows disks in root_dir configuration diff --git a/app/lib/searchlib.py b/app/lib/searchlib.py index b77c125..f952e85 100644 --- a/app/lib/searchlib.py +++ b/app/lib/searchlib.py @@ -43,7 +43,7 @@ class SearchTracks: Gets all songs with a given title. """ - tracks = [track.title for track in self.tracks] + tracks = [track.og_title for track in self.tracks] results = process.extract( self.query, tracks, diff --git a/app/models.py b/app/models.py index 7911ee5..2600cf3 100644 --- a/app/models.py +++ b/app/models.py @@ -5,7 +5,7 @@ import dataclasses import json from dataclasses import dataclass -from app import utils +from app import utils, settings @dataclass(slots=True) @@ -55,20 +55,28 @@ class Track: image: str = "" artist_hashes: list[str] = dataclasses.field(default_factory=list) is_favorite: bool = False + og_title: str = "" def __post_init__(self): + self.og_title = self.title if self.artist is not None: artists = utils.split_artists(self.artist) - featured = utils.parse_feat_from_title(self.title) - original_lower = "-".join([a.lower() for a in artists]) - artists.extend([a for a in featured if a.lower() not in original_lower]) + if settings.EXTRACT_FEAT: + featured, new_title = utils.parse_feat_from_title(self.title) + original_lower = "-".join([a.lower() for a in artists]) + artists.extend([a for a in featured if a.lower() not in original_lower]) + + self.title = new_title + + if self.og_title == self.album: + self.album = new_title self.artist_hashes = [utils.create_hash(a, decode=True) for a in artists] self.artist = [Artist(a) for a in artists] - albumartists = str(self.albumartist).split(", ") + albumartists = utils.split_artists(self.albumartist) self.albumartist = [Artist(a) for a in albumartists] self.filetype = self.filepath.rsplit(".", maxsplit=1)[-1] @@ -157,8 +165,10 @@ class Album: if ( len(tracks) == 1 and tracks[0].title == self.title - and tracks[0].track == 1 - and tracks[0].disc == 1 + + # and tracks[0].track == 1 + # and tracks[0].disc == 1 + # Todo: Are the above commented checks necessary? ): self.is_single = True diff --git a/app/settings.py b/app/settings.py index ea2924c..5a9dba3 100644 --- a/app/settings.py +++ b/app/settings.py @@ -48,7 +48,6 @@ SM_THUMB_PATH = os.path.join(THUMBS_PATH, "small") LG_THUMBS_PATH = os.path.join(THUMBS_PATH, "large") MUSIC_DIR = os.path.join(USER_HOME_DIR, "Music") - # TEST_DIR = "/home/cwilvx/Downloads/Telegram Desktop" # TEST_DIR = "/mnt/dfc48e0f-103b-426e-9bf9-f25d3743bc96/Music/Chill/Wolftyla Radio" # HOME_DIR = TEST_DIR @@ -80,10 +79,6 @@ USER_DATA_DB_NAME = "userdata.db" APP_DB_PATH = os.path.join(APP_DIR, APP_DB_NAME) USERDATA_DB_PATH = os.path.join(APP_DIR, USER_DATA_DB_NAME) - -# ===== Store ===== -USE_STORE = True - HELP_MESSAGE = """ Usage: swingmusic [options] @@ -91,10 +86,17 @@ Options: --build: Build the application --host: Set the host --port: Set the port + --no-feat: Do not extract featured artists from the song title --help, -h: Show this help message --version, -v: Show the version """ +EXTRACT_FEAT = True +""" +Whether to extract the featured artists from the song title +Changed using the --no-feat flag +""" + class TCOLOR: """ diff --git a/app/utils.py b/app/utils.py index 39b8625..0bca37d 100644 --- a/app/utils.py +++ b/app/utils.py @@ -191,7 +191,7 @@ def get_albumartists(albums: list[models.Album]) -> set[str]: def get_all_artists( - tracks: list[models.Track], albums: list[models.Album] + tracks: list[models.Track], albums: list[models.Album] ) -> list[models.Artist]: artists_from_tracks = get_artists_from_tracks(tracks) artist_from_albums = get_albumartists(albums) @@ -260,19 +260,29 @@ def is_windows(): return platform.system() == "Windows" -def parse_feat_from_title(title: str) -> list[str]: +def parse_feat_from_title(title: str) -> tuple[list[str], str]: """ Extracts featured artists from a song title using regex. """ regex = r"\((?:feat|ft|featuring|with)\.?\s+(.+?)\)" + # regex for square brackets 👇 + sqr_regex = r"\[(?:feat|ft|featuring|with)\.?\s+(.+?)\]" + match = re.search(regex, title, re.IGNORECASE) if not match: - return [] + match = re.search(sqr_regex, title, re.IGNORECASE) + regex = sqr_regex + + if not match: + return [], title artists = match.group(1) artists = split_artists(artists, with_and=True) - return artists + + # remove "feat" group from title + new_title = re.sub(regex, "", title, flags=re.IGNORECASE) + return artists, new_title def get_random_str(length=5): @@ -295,6 +305,7 @@ def split_artists(src: str, with_and: bool = False): artists = re.split(exp, src) return [a.strip() for a in artists] + def parse_artist_from_filename(title: str): """ Extracts artist names from a song title using regex. @@ -327,7 +338,6 @@ def parse_title_from_filename(title: str): res = re.sub(r"\s*\([^)]*official[^)]*\)", "", res, flags=re.IGNORECASE) return res.strip() - # for title in sample_titles: # print(parse_artist_from_filename(title)) # print(parse_title_from_filename(title)) diff --git a/manage.py b/manage.py index 02b28f1..9af1c69 100644 --- a/manage.py +++ b/manage.py @@ -8,6 +8,7 @@ from configparser import ConfigParser import PyInstaller.__main__ as bundler +from app import settings from app.api import create_api from app.functions import run_periodic_checks from app.lib.watchdogg import Watcher as WatchDog @@ -18,6 +19,7 @@ from app.utils import background, get_home_res_path, get_ip, is_windows werkzeug = logging.getLogger("werkzeug") werkzeug.setLevel(logging.ERROR) + class Variables: FLASK_PORT = 1970 FLASK_HOST = "localhost" @@ -57,6 +59,7 @@ class ArgsEnum: build = "--build" port = "--port" host = "--host" + no_feat = "--no-feat" help = ["--help", "-h"] version = ["--version", "-v"] @@ -66,6 +69,7 @@ class HandleArgs: self.handle_build() self.handle_host() self.handle_port() + self.handle_no_feat() self.handle_help() self.handle_version() @@ -130,6 +134,11 @@ class HandleArgs: Variables.FLASK_HOST = host # type: ignore + @staticmethod + def handle_no_feat(): + if ArgsEnum.no_feat in ARGS: + settings.EXTRACT_FEAT = False + @staticmethod def handle_help(): if any((a in ARGS for a in ArgsEnum.help)): @@ -154,11 +163,17 @@ def start_watchdog(): WatchDog().run() -def log_info(): - lines = " ---------------------------------------" +def log_startup_info(): + lines = "---------------------------------------" + # clears terminal 👇 os.system("cls" if os.name == "nt" else "echo -e \\\\033c") + # TODO: Check whether the line above breaks Windows terminal's CTRL D + print(lines) - print(f" {TCOLOR.HEADER}{APP_VERSION} {TCOLOR.ENDC}") + print(f"{TCOLOR.HEADER}{APP_VERSION} {TCOLOR.ENDC}") + + if not settings.EXTRACT_FEAT: + print(f"{TCOLOR.OKBLUE}Extracting featured artists from track titles: {TCOLOR.FAIL}DISABLED!{TCOLOR.ENDC}") adresses = [Variables.FLASK_HOST] @@ -167,7 +182,7 @@ def log_info(): for address in adresses: print( - f" Started app on: {TCOLOR.OKGREEN}http://{address}:{Variables.FLASK_PORT}{TCOLOR.ENDC}" + f"Started app on: {TCOLOR.OKGREEN}http://{address}:{Variables.FLASK_PORT}{TCOLOR.ENDC}" ) print(lines) @@ -176,7 +191,7 @@ def log_info(): if __name__ == "__main__": HandleArgs() - log_info() + log_startup_info() run_bg_checks() start_watchdog() diff --git a/swingmusic.spec b/swingmusic.spec index 97b5111..efd0e87 100644 --- a/swingmusic.spec +++ b/swingmusic.spec @@ -8,7 +8,7 @@ a = Analysis( ['manage.py'], pathex=[], binaries=[], - datas=[('assets', 'assets'), ('client', 'client'), ('app/migrations', 'app/migrations'), ('pyinstaller.config.ini', '.')], + datas=[('assets', 'assets'), ('client', 'client'), ('pyinstaller.config.ini', '.')], hiddenimports=[], hookspath=[], hooksconfig={},