mirror of
https://github.com/tcsenpai/swingmusic.git
synced 2025-06-06 03:05:35 +00:00
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:
parent
7e15680f26
commit
95c1524b68
@ -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."""
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
||||
|
@ -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:
|
||||
"""
|
||||
|
20
app/utils.py
20
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))
|
||||
|
25
manage.py
25
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()
|
||||
|
||||
|
@ -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={},
|
||||
|
Loading…
x
Reference in New Issue
Block a user