diff --git a/app/arg_handler.py b/app/arg_handler.py new file mode 100644 index 0000000..0f14b5b --- /dev/null +++ b/app/arg_handler.py @@ -0,0 +1,113 @@ +""" +Handles arguments passed to the program. +""" + +import sys + +from configparser import ConfigParser +import PyInstaller.__main__ as bundler + +from app import settings +from app.print_help import HELP_MESSAGE +from app.utils import is_windows + +config = ConfigParser() +config.read("pyinstaller.config.ini") + +ALLARGS = settings.ALLARGS +ARGS = sys.argv[1:] + + +class HandleArgs: + def __init__(self) -> None: + self.handle_build() + self.handle_host() + self.handle_port() + self.handle_no_feat() + self.handle_remove_prod() + self.handle_help() + self.handle_version() + + @staticmethod + def handle_build(): + """ + Runs Pyinstaller. + """ + if ALLARGS.build in ARGS: + with open("pyinstaller.config.ini", "w", encoding="utf-8") as file: + config["DEFAULT"]["BUILD"] = "True" + config.write(file) + + _s = ";" if is_windows() else ":" + + bundler.run( + [ + "manage.py", + "--onefile", + "--name", + "swingmusic", + "--clean", + f"--add-data=assets{_s}assets", + f"--add-data=client{_s}client", + f"--add-data=pyinstaller.config.ini{_s}.", + "-y", + ] + ) + + with open("pyinstaller.config.ini", "w", encoding="utf-8") as file: + config["DEFAULT"]["BUILD"] = "False" + config.write(file) + + sys.exit(0) + + @staticmethod + def handle_port(): + if ALLARGS.port in ARGS: + index = ARGS.index(ALLARGS.port) + try: + port = ARGS[index + 1] + except IndexError: + print("ERROR: Port not specified") + sys.exit(0) + + try: + settings.FLASKVARS.FLASK_PORT = int(port) # type: ignore + except ValueError: + print("ERROR: Port should be a number") + sys.exit(0) + + @staticmethod + def handle_host(): + if ALLARGS.host in ARGS: + index = ARGS.index(ALLARGS.host) + + try: + host = ARGS[index + 1] + except IndexError: + print("ERROR: Host not specified") + sys.exit(0) + + settings.FLASKVARS.FLASK_HOST = host # type: ignore + + @staticmethod + def handle_no_feat(): + # if ArgsEnum.no_feat in ARGS: + if any((a in ARGS for a in ALLARGS.show_feat)): + settings.EXTRACT_FEAT = False + + @staticmethod + def handle_remove_prod(): + if any((a in ARGS for a in ALLARGS.show_prod)): + settings.REMOVE_PROD = False + + @staticmethod + def handle_help(): + if any((a in ARGS for a in ALLARGS.help)): + print(HELP_MESSAGE) + sys.exit(0) + + @staticmethod + def handle_version(): + if any((a in ARGS for a in ALLARGS.version)): + print(settings.APP_VERSION) + sys.exit(0) diff --git a/app/models.py b/app/models.py index 8297e1d..43a7404 100644 --- a/app/models.py +++ b/app/models.py @@ -61,16 +61,21 @@ class Track: self.og_title = self.title if self.artist is not None: artists = utils.split_artists(self.artist) + new_title = self.title 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 settings.REMOVE_PROD: + new_title = utils.remove_prod(new_title) - if self.og_title == self.album: - self.album = new_title + # if track is a single + if self.og_title == self.album: + self.album = new_title + + self.title = new_title self.artist_hashes = [utils.create_hash(a, decode=True) for a in artists] diff --git a/app/print_help.py b/app/print_help.py new file mode 100644 index 0000000..016724b --- /dev/null +++ b/app/print_help.py @@ -0,0 +1,18 @@ +from app.settings import ALLARGS + +args = ALLARGS + +HELP_MESSAGE = f""" +Usage: swingmusic [options] + +Options: + {args.build}: Build the application (in development) + {args.host}: Set the host + {args.port}: Set the port + + {', '.join(args.show_feat)}: Do not extract featured artists from the song title + {', '.join(args.show_prod)}: Do not hide producers in the song title + + {', '.join(args.help)}: Show this help message + {', '.join(args.version)}: Show the app version +""" diff --git a/app/settings.py b/app/settings.py index 1bf459d..d1a1f23 100644 --- a/app/settings.py +++ b/app/settings.py @@ -28,7 +28,7 @@ def get_xdg_config_dir(): # ------- HELPER METHODS -------- -APP_VERSION = "v.1.1.0.beta" +APP_VERSION = "v1.1.0" # paths XDG_CONFIG_DIR = get_xdg_config_dir() @@ -76,22 +76,34 @@ 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) -HELP_MESSAGE = """ -Usage: swingmusic [options] -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 -""" +class FLASKVARS: + FLASK_PORT = 1970 + FLASK_HOST = "localhost" + + +class ALLARGS: + """ + Enumerates the possible app arguments. + """ + + build = "--build" + port = "--port" + host = "--host" + show_feat = ["--show-feat", "-sf"] + show_prod = ["--show-prod", "-sp"] + help = ["--help", "-h"] + version = ["--version", "-v"] + EXTRACT_FEAT = True """ Whether to extract the featured artists from the song title. -Changed using the `--no-feat` flag +""" + +REMOVE_PROD = True +""" +Whether to remove the producers from the song title. """ diff --git a/app/start_info_logger.py b/app/start_info_logger.py new file mode 100644 index 0000000..559ce1d --- /dev/null +++ b/app/start_info_logger.py @@ -0,0 +1,51 @@ +import os + +from app.utils import get_ip +from app.settings import TCOLOR, APP_VERSION, FLASKVARS, APP_DIR +from app import settings + + +def log_startup_info(): + lines = "------------------------------" + # clears terminal 👇 + os.system("cls" if os.name == "nt" else "echo -e \\\\033c") + + print(lines) + print(f"{TCOLOR.HEADER}SwingMusic {APP_VERSION} {TCOLOR.ENDC}") + + adresses = [FLASKVARS.FLASK_HOST] + + if FLASKVARS.FLASK_HOST == "0.0.0.0": + adresses = ["localhost", get_ip()] + + print("Started app on:") + for address in adresses: + # noinspection HttpUrlsUsage + print( + f"➤ {TCOLOR.OKGREEN}http://{address}:{FLASKVARS.FLASK_PORT}{TCOLOR.ENDC}" + ) + + print(lines) + print("\n") + + to_print = [ + [ + "Extract featured artists from titles", + settings.EXTRACT_FEAT + ], + [ + "Remove prod. from titles", + settings.REMOVE_PROD + ] + ] + + for item in to_print: + print( + f"{item[0]}: {TCOLOR.FAIL}{item[1]}{TCOLOR.ENDC}" + ) + + print( + f"{TCOLOR.YELLOW}Data folder: {APP_DIR}{TCOLOR.ENDC}" + ) + + print("\n") diff --git a/app/utils.py b/app/utils.py index 0bca37d..65fc309 100644 --- a/app/utils.py +++ b/app/utils.py @@ -279,6 +279,7 @@ def parse_feat_from_title(title: str) -> tuple[list[str], str]: artists = match.group(1) artists = split_artists(artists, with_and=True) + print(artists) # remove "feat" group from title new_title = re.sub(regex, "", title, flags=re.IGNORECASE) @@ -334,10 +335,26 @@ def parse_title_from_filename(title: str): return title res = match.group(1) - # remove text in brackets starting with "official" case insensitive + # remove text in brackets starting with "official" case-insensitive 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)) + +def remove_prod(title: str) -> str: + """ + Removes the producer string in a track title using regex. + """ + + # check if title contain title, if not return it. + if not ("prod." in title.lower()): + return title + + # check if title has brackets + if re.search(r'[()\[\]]', title): + regex = r'\s?(\(|\[)prod\..*?(\)|\])\s?' + else: + regex = r'\s?\bprod\.\s*\S+' + + # remove the producer string + title = re.sub(regex, "", title, flags=re.IGNORECASE) + return title.strip() diff --git a/manage.py b/manage.py index 04212e5..05e9f36 100644 --- a/manage.py +++ b/manage.py @@ -2,35 +2,22 @@ This file is used to run the application. """ import logging -import os -import sys -from configparser import ConfigParser -import PyInstaller.__main__ as bundler - -from app import settings from app.api import create_api +from app.arg_handler import HandleArgs from app.functions import run_periodic_checks from app.lib.watchdogg import Watcher as WatchDog -from app.settings import APP_VERSION, HELP_MESSAGE, TCOLOR +from app.settings import FLASKVARS from app.setup import run_setup -from app.utils import background, get_home_res_path, get_ip, is_windows +from app.start_info_logger import log_startup_info +from app.utils import background, get_home_res_path werkzeug = logging.getLogger("werkzeug") werkzeug.setLevel(logging.ERROR) - -class Variables: - FLASK_PORT = 1970 - FLASK_HOST = "localhost" - - app = create_api() app.static_folder = get_home_res_path("client") -config = ConfigParser() -config.read("pyinstaller.config.ini") - @app.route("/") def serve_client_files(path): @@ -48,110 +35,6 @@ def serve_client(): return app.send_static_file("index.html") -ARGS = sys.argv[1:] - - -class ArgsEnum: - """ - Enumerates the possible file arguments. - """ - - build = "--build" - port = "--port" - host = "--host" - no_feat = "--no-feat" - help = ["--help", "-h"] - version = ["--version", "-v"] - - -class HandleArgs: - def __init__(self) -> None: - self.handle_build() - self.handle_host() - self.handle_port() - self.handle_no_feat() - self.handle_help() - self.handle_version() - - @staticmethod - def handle_build(): - """ - Runs Pyinstaller. - """ - if ArgsEnum.build in ARGS: - with open("pyinstaller.config.ini", "w", encoding="utf-8") as file: - config["DEFAULT"]["BUILD"] = "True" - config.write(file) - - _s = ";" if is_windows() else ":" - - bundler.run( - [ - "manage.py", - "--onefile", - "--name", - "swingmusic", - "--clean", - f"--add-data=assets{_s}assets", - f"--add-data=client{_s}client", - f"--add-data=pyinstaller.config.ini{_s}.", - "-y", - ] - ) - - with open("pyinstaller.config.ini", "w", encoding="utf-8") as file: - config["DEFAULT"]["BUILD"] = "False" - config.write(file) - - sys.exit(0) - - @staticmethod - def handle_port(): - if ArgsEnum.port in ARGS: - index = ARGS.index(ArgsEnum.port) - try: - port = ARGS[index + 1] - except IndexError: - print("ERROR: Port not specified") - sys.exit(0) - - try: - Variables.FLASK_PORT = int(port) # type: ignore - except ValueError: - print("ERROR: Port should be a number") - sys.exit(0) - - @staticmethod - def handle_host(): - if ArgsEnum.host in ARGS: - index = ARGS.index(ArgsEnum.host) - - try: - host = ARGS[index + 1] - except IndexError: - print("ERROR: Host not specified") - sys.exit(0) - - 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)): - print(HELP_MESSAGE) - sys.exit(0) - - @staticmethod - def handle_version(): - if any((a in ARGS for a in ArgsEnum.version)): - print(APP_VERSION) - sys.exit(0) - - @background def run_bg_checks() -> None: run_setup() @@ -163,41 +46,6 @@ def start_watchdog(): WatchDog().run() -def log_startup_info(): - lines = "------------------------------" - # clears terminal 👇 - os.system("cls" if os.name == "nt" else "echo -e \\\\033c") - - print(lines) - print(f"{TCOLOR.HEADER}SwingMusic {APP_VERSION} {TCOLOR.ENDC}") - - adresses = [Variables.FLASK_HOST] - - if Variables.FLASK_HOST == "0.0.0.0": - adresses = ["localhost", get_ip()] - - print("Started app on:") - for address in adresses: - # noinspection HttpUrlsUsage - print( - f"➤ {TCOLOR.OKGREEN}http://{address}:{Variables.FLASK_PORT}{TCOLOR.ENDC}" - ) - - print(lines) - print("\n") - - if not settings.EXTRACT_FEAT: - print( - f"{TCOLOR.OKBLUE}Extracting featured artists from track titles: {TCOLOR.FAIL}DISABLED!{TCOLOR.ENDC}" - ) - - print( - f"{TCOLOR.OKBLUE}App data folder: {settings.APP_DIR}{TCOLOR.OKGREEN}{TCOLOR.ENDC}" - ) - - print("\n") - - if __name__ == "__main__": HandleArgs() log_startup_info() @@ -207,10 +55,9 @@ if __name__ == "__main__": app.run( debug=False, threaded=True, - host=Variables.FLASK_HOST, - port=Variables.FLASK_PORT, + host=FLASKVARS.FLASK_HOST, + port=FLASKVARS.FLASK_PORT, use_reloader=False, ) -# TODO: Find a way to verify the host string # TODO: Organize code in this file: move args to new file, etc. diff --git a/tests/test_utils.py b/tests/test_utils.py index 9c9e1eb..7546f7b 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -24,7 +24,7 @@ def test_extract_featured_artists_from_title(): ] for title, expected in zip(test_titles, results): - assert parse_feat_from_title(title) == expected + assert parse_feat_from_title(title)[0] == expected # === HYPOTHESIS GHOSTWRITER TESTS ===