add utility to remove prod names from track titles

+ move code to handle args and print startup info to other files
+ update app version number
This commit is contained in:
geoffrey45 2023-02-17 18:25:32 +03:00
parent 29e766b60a
commit 509c22c736
8 changed files with 242 additions and 179 deletions

113
app/arg_handler.py Normal file
View File

@ -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)

View File

@ -61,17 +61,22 @@ class Track:
self.og_title = self.title self.og_title = self.title
if self.artist is not None: if self.artist is not None:
artists = utils.split_artists(self.artist) artists = utils.split_artists(self.artist)
new_title = self.title
if settings.EXTRACT_FEAT: if settings.EXTRACT_FEAT:
featured, new_title = utils.parse_feat_from_title(self.title) featured, new_title = utils.parse_feat_from_title(self.title)
original_lower = "-".join([a.lower() for a in artists]) original_lower = "-".join([a.lower() for a in artists])
artists.extend([a for a in featured if a.lower() not in original_lower]) 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 track is a single
if self.og_title == self.album: if self.og_title == self.album:
self.album = new_title self.album = new_title
self.title = new_title
self.artist_hashes = [utils.create_hash(a, decode=True) for a in artists] self.artist_hashes = [utils.create_hash(a, decode=True) for a in artists]
self.artist = [Artist(a) for a in artists] self.artist = [Artist(a) for a in artists]

18
app/print_help.py Normal file
View File

@ -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
"""

View File

@ -28,7 +28,7 @@ def get_xdg_config_dir():
# ------- HELPER METHODS -------- # ------- HELPER METHODS --------
APP_VERSION = "v.1.1.0.beta" APP_VERSION = "v1.1.0"
# paths # paths
XDG_CONFIG_DIR = get_xdg_config_dir() 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) APP_DB_PATH = os.path.join(APP_DIR, APP_DB_NAME)
USERDATA_DB_PATH = os.path.join(APP_DIR, USER_DATA_DB_NAME) USERDATA_DB_PATH = os.path.join(APP_DIR, USER_DATA_DB_NAME)
HELP_MESSAGE = """
Usage: swingmusic [options]
Options: class FLASKVARS:
--build: Build the application FLASK_PORT = 1970
--host: Set the host FLASK_HOST = "localhost"
--port: Set the port
--no-feat: Do not extract featured artists from the song title
--help, -h: Show this help message class ALLARGS:
--version, -v: Show the version
""" """
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 EXTRACT_FEAT = True
""" """
Whether to extract the featured artists from the song title. 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.
""" """

51
app/start_info_logger.py Normal file
View File

@ -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")

View File

@ -279,6 +279,7 @@ def parse_feat_from_title(title: str) -> tuple[list[str], str]:
artists = match.group(1) artists = match.group(1)
artists = split_artists(artists, with_and=True) artists = split_artists(artists, with_and=True)
print(artists)
# remove "feat" group from title # remove "feat" group from title
new_title = re.sub(regex, "", title, flags=re.IGNORECASE) new_title = re.sub(regex, "", title, flags=re.IGNORECASE)
@ -334,10 +335,26 @@ def parse_title_from_filename(title: str):
return title return title
res = match.group(1) 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) res = re.sub(r"\s*\([^)]*official[^)]*\)", "", res, flags=re.IGNORECASE)
return res.strip() return res.strip()
# for title in sample_titles:
# print(parse_artist_from_filename(title)) def remove_prod(title: str) -> str:
# print(parse_title_from_filename(title)) """
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()

165
manage.py
View File

@ -2,35 +2,22 @@
This file is used to run the application. This file is used to run the application.
""" """
import logging 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.api import create_api
from app.arg_handler import HandleArgs
from app.functions import run_periodic_checks from app.functions import run_periodic_checks
from app.lib.watchdogg import Watcher as WatchDog 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.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 = logging.getLogger("werkzeug")
werkzeug.setLevel(logging.ERROR) werkzeug.setLevel(logging.ERROR)
class Variables:
FLASK_PORT = 1970
FLASK_HOST = "localhost"
app = create_api() app = create_api()
app.static_folder = get_home_res_path("client") app.static_folder = get_home_res_path("client")
config = ConfigParser()
config.read("pyinstaller.config.ini")
@app.route("/<path:path>") @app.route("/<path:path>")
def serve_client_files(path): def serve_client_files(path):
@ -48,110 +35,6 @@ def serve_client():
return app.send_static_file("index.html") 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 @background
def run_bg_checks() -> None: def run_bg_checks() -> None:
run_setup() run_setup()
@ -163,41 +46,6 @@ def start_watchdog():
WatchDog().run() 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__": if __name__ == "__main__":
HandleArgs() HandleArgs()
log_startup_info() log_startup_info()
@ -207,10 +55,9 @@ if __name__ == "__main__":
app.run( app.run(
debug=False, debug=False,
threaded=True, threaded=True,
host=Variables.FLASK_HOST, host=FLASKVARS.FLASK_HOST,
port=Variables.FLASK_PORT, port=FLASKVARS.FLASK_PORT,
use_reloader=False, use_reloader=False,
) )
# TODO: Find a way to verify the host string
# TODO: Organize code in this file: move args to new file, etc. # TODO: Organize code in this file: move args to new file, etc.

View File

@ -24,7 +24,7 @@ def test_extract_featured_artists_from_title():
] ]
for title, expected in zip(test_titles, results): 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 === # === HYPOTHESIS GHOSTWRITER TESTS ===