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
This commit is contained in:
geoffrey45 2023-02-01 13:34:53 +03:00
parent 7e15680f26
commit 95c1524b68
8 changed files with 95 additions and 44 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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={},