diff --git a/app/api/folder.py b/app/api/folder.py index fa05fce1..c87c8e38 100644 --- a/app/api/folder.py +++ b/app/api/folder.py @@ -2,6 +2,7 @@ Contains all the folder routes. """ import os +import psutil from pathlib import Path from flask import Blueprint, request @@ -10,7 +11,7 @@ from app import settings from app.lib.folderslib import GetFilesAndDirs from app.db.sqlite.settings import SettingsSQLMethods as db from app.models import Folder -from app.utils import create_folder_hash +from app.utils import create_folder_hash, is_windows, win_replace_slash api = Blueprint("folder", __name__, url_prefix="/") @@ -42,8 +43,8 @@ def get_folder_tree(): return { "folders": [ Folder( - name=f.name, - path=str(f), + name=f.name if f.name != "" else str(f).replace("\\", "/"), + path=win_replace_slash(str(f)), has_tracks=True, is_sym=f.is_symlink(), path_hash=create_folder_hash(*f.parts[1:]), @@ -61,26 +62,56 @@ def get_folder_tree(): } +def get_all_drives(): + """ + Returns a list of all the drives on a windows machine. + """ + drives = psutil.disk_partitions() + return [d.mountpoint for d in drives] + + @api.route("/folder/dir-browser", methods=["POST"]) def list_folders(): """ Returns a list of all the folders in the given folder. """ data = request.get_json() + is_win = is_windows() try: req_dir: str = data["folder"] except KeyError: - req_dir = settings.USER_HOME_DIR + req_dir = "$home" if req_dir == "$home": - req_dir = settings.USER_HOME_DIR + # 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() + ] + } - entries = os.scandir(req_dir) + req_dir = req_dir + "/" + + try: + entries = os.scandir(req_dir) + except PermissionError: + return {"folders": []} dirs = [e.name for e in entries if e.is_dir() and not e.name.startswith(".")] - dirs = [{"name": d, "path": os.path.join(req_dir, d)} for d in dirs] + dirs = [ + {"name": d, "path": win_replace_slash(os.path.join(req_dir, d))} for d in dirs + ] return { "folders": sorted(dirs, key=lambda i: i["name"]), } + + +# todo: + +# - handle showing windows disks in root_dir configuration +# - handle the above, but for all partitions mounted in linux. +# - handle the "\" in client's folder page breadcrumb diff --git a/app/api/settings.py b/app/api/settings.py index 0c1b2a83..69427065 100644 --- a/app/api/settings.py +++ b/app/api/settings.py @@ -48,6 +48,19 @@ def rebuild_store(db_dirs: list[str]): log.info("Rebuilding library... ✅") +def finalize(new_: list[str], removed_: list[str], db_dirs_: list[str]): + """ + Params: + new_: will be added to the database + removed_: will be removed from the database + db_dirs_: will be used to remove tracks that + are outside these directories from the database and store. + """ + sdb.remove_root_dirs(removed_) + sdb.add_root_dirs(new_) + rebuild_store(db_dirs_) + + @api.route("/settings/add-root-dirs", methods=["POST"]) def add_root_dirs(): """ @@ -66,33 +79,28 @@ def add_root_dirs(): except KeyError: return msg, 400 - def finalize(new_: list[str], removed_: list[str], db_dirs_: list[str]): - sdb.remove_root_dirs(removed_) - sdb.add_root_dirs(new_) - rebuild_store(db_dirs_) - - # --- db_dirs = sdb.get_root_dirs() _h = "$home" - try: - if db_dirs[0] == _h and new_dirs[0] == _h.strip(): - return {"msg": "Not changed!"} + db_home = any([d == _h for d in db_dirs]) # if $home is in db + incoming_home = any([d == _h for d in new_dirs]) # if $home is in incoming - if db_dirs[0] == _h: - sdb.remove_root_dirs(db_dirs) + # handle $home case + if db_home and incoming_home: + return {"msg": "Not changed!"} - if new_dirs[0] == _h: - finalize([_h], db_dirs, [settings.USER_HOME_DIR]) + if db_home or incoming_home: + sdb.remove_root_dirs(db_dirs) - return {"root_dirs": [_h]} - except IndexError: - pass + if incoming_home: + finalize([_h], [], [settings.USER_HOME_DIR]) + return {"root_dirs": [_h]} + + # --- for _dir in new_dirs: children = get_child_dirs(_dir, db_dirs) removed_dirs.extend(children) - # --- for _dir in removed_dirs: try: diff --git a/app/db/sqlite/settings.py b/app/db/sqlite/settings.py index 901aff10..d6e0ea41 100644 --- a/app/db/sqlite/settings.py +++ b/app/db/sqlite/settings.py @@ -1,5 +1,5 @@ -import json from app.db.sqlite.utils import SQLiteManager +from app.utils import win_replace_slash class SettingsSQLMethods: @@ -19,7 +19,8 @@ class SettingsSQLMethods: cur.execute(sql) dirs = cur.fetchall() - return [dir[0] for dir in dirs] + dirs = [dir[0] for dir in dirs] + return [win_replace_slash(d) for d in dirs] @staticmethod def add_root_dirs(dirs: list[str]): diff --git a/app/db/store.py b/app/db/store.py index f6e2bb60..26571126 100644 --- a/app/db/store.py +++ b/app/db/store.py @@ -17,6 +17,7 @@ from app.utils import ( create_folder_hash, get_all_artists, remove_duplicates, + win_replace_slash, ) @@ -174,7 +175,7 @@ class Store: return Folder( name=folder.name, - path=str(folder), + path=win_replace_slash(str(folder)), is_sym=folder.is_symlink(), has_tracks=True, path_hash=create_folder_hash(*folder.parts[1:]), @@ -218,9 +219,18 @@ class Store: ] all_folders = [Path(f) for f in all_folders] - all_folders = [f for f in all_folders if f.exists()] + # all_folders = [f for f in all_folders if f.exists()] - for path in tqdm(all_folders, desc="Processing folders"): + valid_folders = [] + + for folder in all_folders: + try: + if folder.exists(): + valid_folders.append(folder) + except PermissionError: + pass + + for path in tqdm(valid_folders, desc="Processing folders"): folder = cls.create_folder(str(path)) cls.folders.append(folder) diff --git a/app/lib/folderslib.py b/app/lib/folderslib.py index 9062e0cf..64906784 100644 --- a/app/lib/folderslib.py +++ b/app/lib/folderslib.py @@ -4,6 +4,8 @@ from concurrent.futures import ThreadPoolExecutor from app.db.store import Store from app.models import Folder, Track from app.settings import SUPPORTED_FILES +from app.logger import log +from app.utils import win_replace_slash class GetFilesAndDirs: @@ -26,14 +28,25 @@ class GetFilesAndDirs: ext = os.path.splitext(entry.name)[1].lower() if entry.is_dir() and not entry.name.startswith("."): - dirs.append(entry.path) + dirs.append(win_replace_slash(entry.path)) elif entry.is_file() and ext in SUPPORTED_FILES: - files.append(entry.path) + files.append(win_replace_slash(entry.path)) - # sort files by modified time - files.sort( - key=lambda f: os.path.getmtime(f) # pylint: disable=unnecessary-lambda - ) + files_ = [] + + for file in files: + try: + files_.append( + { + "path": file, + "time": os.path.getmtime(file), + } + ) + except OSError as e: + log.error(e) + + files_.sort(key=lambda f: f["time"]) + files = [f["path"] for f in files_] tracks = Store.get_tracks_by_filepaths(files) diff --git a/app/lib/populate.py b/app/lib/populate.py index 92849612..b1a4df1a 100644 --- a/app/lib/populate.py +++ b/app/lib/populate.py @@ -43,7 +43,10 @@ class Populate: if len(dirs_to_scan) == 0: log.warning( - "The root directory is not configured. Open the app in your web browser to configure." + ( + "The root directory is not configured. " + + "Open the app in your webbrowser to configure." + ) ) return @@ -85,7 +88,7 @@ class Populate: for file in tqdm(untagged, desc="Reading files"): if POPULATE_KEY != key: - raise PopulateCancelledError('Populate key changed') + raise PopulateCancelledError("Populate key changed") tags = get_tags(file) diff --git a/app/lib/taglib.py b/app/lib/taglib.py index 5d975d20..4f1f16e8 100644 --- a/app/lib/taglib.py +++ b/app/lib/taglib.py @@ -1,13 +1,17 @@ -import os import datetime +import os from io import BytesIO -from tinytag import TinyTag from PIL import Image, UnidentifiedImageError +from tinytag import TinyTag from app import settings -from app.utils import create_hash - +from app.utils import ( + create_hash, + parse_artist_from_filename, + parse_title_from_filename, + win_replace_slash, +) def parse_album_art(filepath: str): @@ -81,7 +85,7 @@ def get_tags(filepath: str): try: tags = TinyTag.get(filepath) - except: # pylint: disable=bare-except + except: # noqa: E722 return None no_albumartist: bool = (tags.albumartist == "") or (tags.albumartist is None) @@ -97,9 +101,22 @@ def get_tags(filepath: str): for tag in to_filename: p = getattr(tags, tag) if p == "" or p is None: - setattr(tags, tag, filename) + maybe = parse_title_from_filename(filename) + setattr(tags, tag, maybe) - to_check = ["album", "artist", "year", "albumartist"] + parse = ["artist", "albumartist"] + for tag in parse: + p = getattr(tags, tag) + + if p == "" or p is None: + maybe = parse_artist_from_filename(filename) + + if maybe != []: + setattr(tags, tag, ", ".join(maybe)) + else: + setattr(tags, tag, "Unknown") + + to_check = ["album", "year", "albumartist"] for prop in to_check: p = getattr(tags, prop) if (p is None) or (p == ""): @@ -127,10 +144,10 @@ def get_tags(filepath: str): tags.albumhash = create_hash(tags.album, tags.albumartist) tags.trackhash = create_hash(tags.artist, tags.album, tags.title) tags.image = f"{tags.albumhash}.webp" - tags.folder = os.path.dirname(filepath) + tags.folder = win_replace_slash(os.path.dirname(filepath)) tags.date = extract_date(tags.year) - tags.filepath = filepath + tags.filepath = win_replace_slash(filepath) tags.filetype = filetype tags = tags.__dict__ @@ -157,3 +174,8 @@ def get_tags(filepath: str): del tags[tag] return tags + + for tag in to_delete: + del tags[tag] + + return tags diff --git a/app/lib/watchdogg.py b/app/lib/watchdogg.py index 57875e75..f4003399 100644 --- a/app/lib/watchdogg.py +++ b/app/lib/watchdogg.py @@ -63,7 +63,10 @@ class Watcher: dir_map = [d for d in dir_map if d["realpath"] != d["original"]] - if len(dirs) > 0 and dirs[0] == "$home": + # if len(dirs) > 0 and dirs[0] == "$home": + # dirs = [settings.USER_HOME_DIR] + + if any([d == "$home" for d in dirs]): dirs = [settings.USER_HOME_DIR] event_handler = Handler(root_dirs=dirs, dir_map=dir_map) @@ -83,7 +86,7 @@ class Watcher: try: self.observer.start() log.info("Started watchdog") - except FileNotFoundError: + except (FileNotFoundError, PermissionError): log.error( "WatchdogError: Failed to start watchdog, root directories could not be resolved." ) @@ -189,10 +192,11 @@ class Handler(PatternMatchingEventHandler): def __init__(self, root_dirs: list[str], dir_map: dict[str:str]): self.root_dirs = root_dirs self.dir_map = dir_map + patterns = [f"*{f}" for f in settings.SUPPORTED_FILES] PatternMatchingEventHandler.__init__( self, - patterns=["*.flac", "*.mp3"], + patterns=patterns, ignore_directories=True, case_sensitive=False, ) diff --git a/app/models.py b/app/models.py index f5e57790..7911ee5c 100644 --- a/app/models.py +++ b/app/models.py @@ -60,7 +60,7 @@ class Track: if self.artist is not None: artists = utils.split_artists(self.artist) - featured = utils.extract_featured_artists_from_title(self.title) + 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]) diff --git a/app/settings.py b/app/settings.py index e3462874..ea2924cc 100644 --- a/app/settings.py +++ b/app/settings.py @@ -71,7 +71,7 @@ SM_ARTIST_IMG_SIZE = 64 The size of extracted images in pixels """ -FILES = ["flac", "mp3", "wav", "m4a"] +FILES = ["flac", "mp3", "wav", "m4a", "ogg", "wma", "opus", "alac", "aiff"] SUPPORTED_FILES = tuple(f".{file}" for file in FILES) # ===== SQLite ===== diff --git a/app/utils.py b/app/utils.py index 8be18367..39b86250 100644 --- a/app/utils.py +++ b/app/utils.py @@ -1,19 +1,18 @@ """ This module contains mini functions for the server. """ -import random -import re -import string -from pathlib import Path -from datetime import datetime - +import hashlib import os import platform +import random +import re import socket as Socket -import hashlib +import string import threading -import requests +from datetime import datetime +from pathlib import Path +import requests from unidecode import unidecode from app import models @@ -36,7 +35,8 @@ def background(func): def run_fast_scandir(_dir: str, full=False) -> tuple[list[str], list[str]]: """ - Scans a directory for files with a specific extension. Returns a list of files and folders in the directory. + Scans a directory for files with a specific extension. + Returns a list of files and folders in the directory. """ if _dir == "": @@ -46,20 +46,20 @@ def run_fast_scandir(_dir: str, full=False) -> tuple[list[str], list[str]]: files = [] try: - for _files in os.scandir(_dir): - if _files.is_dir() and not _files.name.startswith("."): - subfolders.append(_files.path) - if _files.is_file(): - ext = os.path.splitext(_files.name)[1].lower() + for _file in os.scandir(_dir): + if _file.is_dir() and not _file.name.startswith("."): + subfolders.append(_file.path) + if _file.is_file(): + ext = os.path.splitext(_file.name)[1].lower() if ext in SUPPORTED_FILES: - files.append(_files.path) + files.append(win_replace_slash(_file.path)) if full or len(files) == 0: for _dir in list(subfolders): - sub_dirs, _files = run_fast_scandir(_dir, full=True) + sub_dirs, _file = run_fast_scandir(_dir, full=True) subfolders.extend(sub_dirs) - files.extend(_files) - except (PermissionError, FileNotFoundError, ValueError): + files.extend(_file) + except (OSError, PermissionError, FileNotFoundError, ValueError): return [], [] return subfolders, files @@ -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) @@ -232,7 +232,8 @@ def bisection_search_string(strings: list[str], target: str) -> str | None: def get_home_res_path(filename: str): """ - Returns a path to resources in the home directory of this project. Used to resolve resources in builds. + Returns a path to resources in the home directory of this project. + Used to resolve resources in builds. """ try: return (CWD / ".." / filename).resolve() @@ -259,12 +260,7 @@ def is_windows(): return platform.system() == "Windows" -def split_artists(src: str): - artists = re.split(r"\s*[&,;]\s*", src) - return [a.strip() for a in artists] - - -def extract_featured_artists_from_title(title: str) -> list[str]: +def parse_feat_from_title(title: str) -> list[str]: """ Extracts featured artists from a song title using regex. """ @@ -275,7 +271,7 @@ def extract_featured_artists_from_title(title: str) -> list[str]: return [] artists = match.group(1) - artists = split_artists(artists) + artists = split_artists(artists, with_and=True) return artists @@ -284,3 +280,54 @@ def get_random_str(length=5): Generates a random string of length `length`. """ return "".join(random.choices(string.ascii_letters + string.digits, k=length)) + + +def win_replace_slash(path: str): + if is_windows(): + return path.replace("\\", "/").replace("//", "/") + + return path + + +def split_artists(src: str, with_and: bool = False): + exp = r"\s*(?:and|&|,|;)\s*" if with_and else r"\s*[,;]\s*" + + 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. + """ + + regex = r"^(.+?)\s*[-–—]\s*(?:.+?)$" + match = re.search(regex, title, re.IGNORECASE) + + if not match: + return [] + + artists = match.group(1) + artists = split_artists(artists) + return artists + + +def parse_title_from_filename(title: str): + """ + Extracts track title from a song title using regex. + """ + + regex = r"^(?:.+?)\s*[-–—]\s*(.+?)$" + match = re.search(regex, title, re.IGNORECASE) + + if not match: + return title + + res = match.group(1) + # 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)) diff --git a/manage.py b/manage.py index 7bfe7769..02b28f11 100644 --- a/manage.py +++ b/manage.py @@ -18,7 +18,6 @@ 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" @@ -180,6 +179,7 @@ if __name__ == "__main__": log_info() run_bg_checks() start_watchdog() + app.run( debug=True, threaded=True, diff --git a/poetry.lock b/poetry.lock index 69546dab..922dda6c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -340,14 +340,14 @@ tornado = ["tornado (>=0.2)"] [[package]] name = "hypothesis" -version = "6.65.0" +version = "6.65.1" description = "A library for property-based testing" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "hypothesis-6.65.0-py3-none-any.whl", hash = "sha256:24e3219b0b181414c06bb7a62649a6edb471f148d25c9c9687f47505b0f50b1c"}, - {file = "hypothesis-6.65.0.tar.gz", hash = "sha256:d25914dd4008b0292d116ac315f01f6691c5460c494a0291c01d96f4bc17fe68"}, + {file = "hypothesis-6.65.1-py3-none-any.whl", hash = "sha256:4b7ae16db09151d17e5feebea07f4f84693cc1573c25e280bc92e619df24182b"}, + {file = "hypothesis-6.65.1.tar.gz", hash = "sha256:fb9757f4b556fc73c2eaa2c1b7d39d0184c75e4cb77dadaf6fa59373838bd629"}, ] [package.dependencies] @@ -602,14 +602,14 @@ files = [ [[package]] name = "pathspec" -version = "0.10.3" +version = "0.11.0" description = "Utility library for gitignore style pattern matching of file paths." category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pathspec-0.10.3-py3-none-any.whl", hash = "sha256:3c95343af8b756205e2aba76e843ba9520a24dd84f68c22b9f93251507509dd6"}, - {file = "pathspec-0.10.3.tar.gz", hash = "sha256:56200de4077d9d0791465aa9095a01d421861e405b5096955051deefd697d6f6"}, + {file = "pathspec-0.11.0-py3-none-any.whl", hash = "sha256:3a66eb970cbac598f9e5ccb5b2cf58930cd8e3ed86d393d541eaf2d8b1705229"}, + {file = "pathspec-0.11.0.tar.gz", hash = "sha256:64d338d4e0914e91c1792321e6907b5a593f1ab1851de7fc269557a21b30ebbc"}, ] [[package]] @@ -749,6 +749,33 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "psutil" +version = "5.9.4" +description = "Cross-platform lib for process and system monitoring in Python." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "psutil-5.9.4-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c1ca331af862803a42677c120aff8a814a804e09832f166f226bfd22b56feee8"}, + {file = "psutil-5.9.4-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:68908971daf802203f3d37e78d3f8831b6d1014864d7a85937941bb35f09aefe"}, + {file = "psutil-5.9.4-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:3ff89f9b835100a825b14c2808a106b6fdcc4b15483141482a12c725e7f78549"}, + {file = "psutil-5.9.4-cp27-cp27m-win32.whl", hash = "sha256:852dd5d9f8a47169fe62fd4a971aa07859476c2ba22c2254d4a1baa4e10b95ad"}, + {file = "psutil-5.9.4-cp27-cp27m-win_amd64.whl", hash = "sha256:9120cd39dca5c5e1c54b59a41d205023d436799b1c8c4d3ff71af18535728e94"}, + {file = "psutil-5.9.4-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6b92c532979bafc2df23ddc785ed116fced1f492ad90a6830cf24f4d1ea27d24"}, + {file = "psutil-5.9.4-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:efeae04f9516907be44904cc7ce08defb6b665128992a56957abc9b61dca94b7"}, + {file = "psutil-5.9.4-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:54d5b184728298f2ca8567bf83c422b706200bcbbfafdc06718264f9393cfeb7"}, + {file = "psutil-5.9.4-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:16653106f3b59386ffe10e0bad3bb6299e169d5327d3f187614b1cb8f24cf2e1"}, + {file = "psutil-5.9.4-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54c0d3d8e0078b7666984e11b12b88af2db11d11249a8ac8920dd5ef68a66e08"}, + {file = "psutil-5.9.4-cp36-abi3-win32.whl", hash = "sha256:149555f59a69b33f056ba1c4eb22bb7bf24332ce631c44a319cec09f876aaeff"}, + {file = "psutil-5.9.4-cp36-abi3-win_amd64.whl", hash = "sha256:fd8522436a6ada7b4aad6638662966de0d61d241cb821239b2ae7013d41a43d4"}, + {file = "psutil-5.9.4-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:6001c809253a29599bc0dfd5179d9f8a5779f9dffea1da0f13c53ee568115e1e"}, + {file = "psutil-5.9.4.tar.gz", hash = "sha256:3d7f9739eb435d4b1338944abe23f49584bde5395f27487d2ee25ad9a8774a62"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] + [[package]] name = "pyinstaller" version = "5.7.0" @@ -1261,4 +1288,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.12" -content-hash = "15b3fba920faab237353240b2b3dbb32603744f6a8ff19e77fe6296d5252c2d7" +content-hash = "54e3995dc11627cb8d20d27ba6d593e4d6d102698c9bd3c29d5505287d22b9e2" diff --git a/pyproject.toml b/pyproject.toml index 14d7090b..f5ee0b8b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,7 @@ tqdm = "^4.64.0" rapidfuzz = "^2.13.7" tinytag = "^1.8.1" Unidecode = "^1.3.6" +psutil = "^5.9.4" [tool.poetry.dev-dependencies] pylint = "^2.15.5" @@ -28,6 +29,7 @@ pyinstaller = "^5.7.0" version = "^22.6.0" allow-prereleases = true + [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" diff --git a/tests/test_utils.py b/tests/test_utils.py index 79de5973..765467a1 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,33 +1,37 @@ +from hypothesis import given +from hypothesis import strategies as st + import app.utils -from hypothesis import given, strategies as st -from app.utils import extract_featured_artists_from_title +from app.utils import parse_feat_from_title def test_extract_featured_artists_from_title(): test_titles = [ "Own it (Featuring Ed Sheeran & Stormzy)", + "Own it (Featuring Ed Sheeran and Stormzy)", "Autograph (On my line)(Feat. Lil Peep)(Deluxe)", "Why so sad? (with Juice Wrld, Lil Peep)", "Why so sad? (with Juice Wrld/Lil Peep)", "Simmer (with Burna Boy)", - "Simmer (without Burna Boy)" + "Simmer (without Burna Boy)", ] results = [ ["Ed Sheeran", "Stormzy"], - ['Lil Peep'], - ["Juice Wrld", "Lil Peep"], + ["Ed Sheeran", "Stormzy"], + ["Lil Peep"], ["Juice Wrld", "Lil Peep"], + ["Juice Wrld/Lil Peep"], ["Burna Boy"], - [] + [], ] for title, expected in zip(test_titles, results): - assert extract_featured_artists_from_title(title) == expected + assert parse_feat_from_title(title) == expected # === HYPOTHESIS GHOSTWRITER TESTS === -@given(__dir=st.text(), full=st.booleans()) -def test_fuzz_run_fast_scandir(__dir: str, full) -> None: - app.utils.run_fast_scandir(_dir=__dir, full=full) +# @given(__dir=st.text(), full=st.booleans()) +# def test_fuzz_run_fast_scandir(__dir: str, full) -> None: +# app.utils.run_fast_scandir(_dir=__dir, full=full)