update supported audio files in settings.py

+ add win_replace_slash function to format win path strings
+ misc
This commit is contained in:
geoffrey45
2023-01-30 15:59:28 +03:00
parent 93a04ba041
commit 7e15680f26
15 changed files with 268 additions and 96 deletions
+38 -7
View File
@@ -2,6 +2,7 @@
Contains all the folder routes. Contains all the folder routes.
""" """
import os import os
import psutil
from pathlib import Path from pathlib import Path
from flask import Blueprint, request from flask import Blueprint, request
@@ -10,7 +11,7 @@ from app import settings
from app.lib.folderslib import GetFilesAndDirs from app.lib.folderslib import GetFilesAndDirs
from app.db.sqlite.settings import SettingsSQLMethods as db from app.db.sqlite.settings import SettingsSQLMethods as db
from app.models import Folder 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="/") api = Blueprint("folder", __name__, url_prefix="/")
@@ -42,8 +43,8 @@ def get_folder_tree():
return { return {
"folders": [ "folders": [
Folder( Folder(
name=f.name, name=f.name if f.name != "" else str(f).replace("\\", "/"),
path=str(f), path=win_replace_slash(str(f)),
has_tracks=True, has_tracks=True,
is_sym=f.is_symlink(), is_sym=f.is_symlink(),
path_hash=create_folder_hash(*f.parts[1:]), 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"]) @api.route("/folder/dir-browser", methods=["POST"])
def list_folders(): def list_folders():
""" """
Returns a list of all the folders in the given folder. Returns a list of all the folders in the given folder.
""" """
data = request.get_json() data = request.get_json()
is_win = is_windows()
try: try:
req_dir: str = data["folder"] req_dir: str = data["folder"]
except KeyError: except KeyError:
req_dir = settings.USER_HOME_DIR req_dir = "$home"
if 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 = [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 { return {
"folders": sorted(dirs, key=lambda i: i["name"]), "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
+25 -17
View File
@@ -48,6 +48,19 @@ def rebuild_store(db_dirs: list[str]):
log.info("Rebuilding library... ✅") 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"]) @api.route("/settings/add-root-dirs", methods=["POST"])
def add_root_dirs(): def add_root_dirs():
""" """
@@ -66,33 +79,28 @@ def add_root_dirs():
except KeyError: except KeyError:
return msg, 400 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() db_dirs = sdb.get_root_dirs()
_h = "$home" _h = "$home"
try: db_home = any([d == _h for d in db_dirs]) # if $home is in db
if db_dirs[0] == _h and new_dirs[0] == _h.strip(): incoming_home = any([d == _h for d in new_dirs]) # if $home is in incoming
return {"msg": "Not changed!"}
if db_dirs[0] == _h: # handle $home case
sdb.remove_root_dirs(db_dirs) if db_home and incoming_home:
return {"msg": "Not changed!"}
if new_dirs[0] == _h: if db_home or incoming_home:
finalize([_h], db_dirs, [settings.USER_HOME_DIR]) sdb.remove_root_dirs(db_dirs)
return {"root_dirs": [_h]} if incoming_home:
except IndexError: finalize([_h], [], [settings.USER_HOME_DIR])
pass return {"root_dirs": [_h]}
# ---
for _dir in new_dirs: for _dir in new_dirs:
children = get_child_dirs(_dir, db_dirs) children = get_child_dirs(_dir, db_dirs)
removed_dirs.extend(children) removed_dirs.extend(children)
# ---
for _dir in removed_dirs: for _dir in removed_dirs:
try: try:
+3 -2
View File
@@ -1,5 +1,5 @@
import json
from app.db.sqlite.utils import SQLiteManager from app.db.sqlite.utils import SQLiteManager
from app.utils import win_replace_slash
class SettingsSQLMethods: class SettingsSQLMethods:
@@ -19,7 +19,8 @@ class SettingsSQLMethods:
cur.execute(sql) cur.execute(sql)
dirs = cur.fetchall() 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 @staticmethod
def add_root_dirs(dirs: list[str]): def add_root_dirs(dirs: list[str]):
+13 -3
View File
@@ -17,6 +17,7 @@ from app.utils import (
create_folder_hash, create_folder_hash,
get_all_artists, get_all_artists,
remove_duplicates, remove_duplicates,
win_replace_slash,
) )
@@ -174,7 +175,7 @@ class Store:
return Folder( return Folder(
name=folder.name, name=folder.name,
path=str(folder), path=win_replace_slash(str(folder)),
is_sym=folder.is_symlink(), is_sym=folder.is_symlink(),
has_tracks=True, has_tracks=True,
path_hash=create_folder_hash(*folder.parts[1:]), 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 = [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)) folder = cls.create_folder(str(path))
cls.folders.append(folder) cls.folders.append(folder)
+19 -6
View File
@@ -4,6 +4,8 @@ from concurrent.futures import ThreadPoolExecutor
from app.db.store import Store from app.db.store import Store
from app.models import Folder, Track from app.models import Folder, Track
from app.settings import SUPPORTED_FILES from app.settings import SUPPORTED_FILES
from app.logger import log
from app.utils import win_replace_slash
class GetFilesAndDirs: class GetFilesAndDirs:
@@ -26,14 +28,25 @@ class GetFilesAndDirs:
ext = os.path.splitext(entry.name)[1].lower() ext = os.path.splitext(entry.name)[1].lower()
if entry.is_dir() and not entry.name.startswith("."): 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: 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_ = []
files.sort(
key=lambda f: os.path.getmtime(f) # pylint: disable=unnecessary-lambda 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) tracks = Store.get_tracks_by_filepaths(files)
+5 -2
View File
@@ -43,7 +43,10 @@ class Populate:
if len(dirs_to_scan) == 0: if len(dirs_to_scan) == 0:
log.warning( 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 return
@@ -85,7 +88,7 @@ class Populate:
for file in tqdm(untagged, desc="Reading files"): for file in tqdm(untagged, desc="Reading files"):
if POPULATE_KEY != key: if POPULATE_KEY != key:
raise PopulateCancelledError('Populate key changed') raise PopulateCancelledError("Populate key changed")
tags = get_tags(file) tags = get_tags(file)
+31 -9
View File
@@ -1,13 +1,17 @@
import os
import datetime import datetime
import os
from io import BytesIO from io import BytesIO
from tinytag import TinyTag
from PIL import Image, UnidentifiedImageError from PIL import Image, UnidentifiedImageError
from tinytag import TinyTag
from app import settings 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): def parse_album_art(filepath: str):
@@ -81,7 +85,7 @@ def get_tags(filepath: str):
try: try:
tags = TinyTag.get(filepath) tags = TinyTag.get(filepath)
except: # pylint: disable=bare-except except: # noqa: E722
return None return None
no_albumartist: bool = (tags.albumartist == "") or (tags.albumartist is 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: for tag in to_filename:
p = getattr(tags, tag) p = getattr(tags, tag)
if p == "" or p is None: 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: for prop in to_check:
p = getattr(tags, prop) p = getattr(tags, prop)
if (p is None) or (p == ""): if (p is None) or (p == ""):
@@ -127,10 +144,10 @@ def get_tags(filepath: str):
tags.albumhash = create_hash(tags.album, tags.albumartist) tags.albumhash = create_hash(tags.album, tags.albumartist)
tags.trackhash = create_hash(tags.artist, tags.album, tags.title) tags.trackhash = create_hash(tags.artist, tags.album, tags.title)
tags.image = f"{tags.albumhash}.webp" 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.date = extract_date(tags.year)
tags.filepath = filepath tags.filepath = win_replace_slash(filepath)
tags.filetype = filetype tags.filetype = filetype
tags = tags.__dict__ tags = tags.__dict__
@@ -157,3 +174,8 @@ def get_tags(filepath: str):
del tags[tag] del tags[tag]
return tags return tags
for tag in to_delete:
del tags[tag]
return tags
+7 -3
View File
@@ -63,7 +63,10 @@ class Watcher:
dir_map = [d for d in dir_map if d["realpath"] != d["original"]] 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] dirs = [settings.USER_HOME_DIR]
event_handler = Handler(root_dirs=dirs, dir_map=dir_map) event_handler = Handler(root_dirs=dirs, dir_map=dir_map)
@@ -83,7 +86,7 @@ class Watcher:
try: try:
self.observer.start() self.observer.start()
log.info("Started watchdog") log.info("Started watchdog")
except FileNotFoundError: except (FileNotFoundError, PermissionError):
log.error( log.error(
"WatchdogError: Failed to start watchdog, root directories could not be resolved." "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]): def __init__(self, root_dirs: list[str], dir_map: dict[str:str]):
self.root_dirs = root_dirs self.root_dirs = root_dirs
self.dir_map = dir_map self.dir_map = dir_map
patterns = [f"*{f}" for f in settings.SUPPORTED_FILES]
PatternMatchingEventHandler.__init__( PatternMatchingEventHandler.__init__(
self, self,
patterns=["*.flac", "*.mp3"], patterns=patterns,
ignore_directories=True, ignore_directories=True,
case_sensitive=False, case_sensitive=False,
) )
+1 -1
View File
@@ -60,7 +60,7 @@ class Track:
if self.artist is not None: if self.artist is not None:
artists = utils.split_artists(self.artist) 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]) 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])
+1 -1
View File
@@ -71,7 +71,7 @@ SM_ARTIST_IMG_SIZE = 64
The size of extracted images in pixels 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) SUPPORTED_FILES = tuple(f".{file}" for file in FILES)
# ===== SQLite ===== # ===== SQLite =====
+74 -27
View File
@@ -1,19 +1,18 @@
""" """
This module contains mini functions for the server. This module contains mini functions for the server.
""" """
import random import hashlib
import re
import string
from pathlib import Path
from datetime import datetime
import os import os
import platform import platform
import random
import re
import socket as Socket import socket as Socket
import hashlib import string
import threading import threading
import requests from datetime import datetime
from pathlib import Path
import requests
from unidecode import unidecode from unidecode import unidecode
from app import models from app import models
@@ -36,7 +35,8 @@ def background(func):
def run_fast_scandir(_dir: str, full=False) -> tuple[list[str], list[str]]: 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 == "": if _dir == "":
@@ -46,20 +46,20 @@ def run_fast_scandir(_dir: str, full=False) -> tuple[list[str], list[str]]:
files = [] files = []
try: try:
for _files in os.scandir(_dir): for _file in os.scandir(_dir):
if _files.is_dir() and not _files.name.startswith("."): if _file.is_dir() and not _file.name.startswith("."):
subfolders.append(_files.path) subfolders.append(_file.path)
if _files.is_file(): if _file.is_file():
ext = os.path.splitext(_files.name)[1].lower() ext = os.path.splitext(_file.name)[1].lower()
if ext in SUPPORTED_FILES: if ext in SUPPORTED_FILES:
files.append(_files.path) files.append(win_replace_slash(_file.path))
if full or len(files) == 0: if full or len(files) == 0:
for _dir in list(subfolders): 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) subfolders.extend(sub_dirs)
files.extend(_files) files.extend(_file)
except (PermissionError, FileNotFoundError, ValueError): except (OSError, PermissionError, FileNotFoundError, ValueError):
return [], [] return [], []
return subfolders, files return subfolders, files
@@ -191,7 +191,7 @@ def get_albumartists(albums: list[models.Album]) -> set[str]:
def get_all_artists( def get_all_artists(
tracks: list[models.Track], albums: list[models.Album] tracks: list[models.Track], albums: list[models.Album]
) -> list[models.Artist]: ) -> list[models.Artist]:
artists_from_tracks = get_artists_from_tracks(tracks) artists_from_tracks = get_artists_from_tracks(tracks)
artist_from_albums = get_albumartists(albums) 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): 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: try:
return (CWD / ".." / filename).resolve() return (CWD / ".." / filename).resolve()
@@ -259,12 +260,7 @@ def is_windows():
return platform.system() == "Windows" return platform.system() == "Windows"
def split_artists(src: str): def parse_feat_from_title(title: str) -> list[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]:
""" """
Extracts featured artists from a song title using regex. Extracts featured artists from a song title using regex.
""" """
@@ -275,7 +271,7 @@ def extract_featured_artists_from_title(title: str) -> list[str]:
return [] return []
artists = match.group(1) artists = match.group(1)
artists = split_artists(artists) artists = split_artists(artists, with_and=True)
return artists return artists
@@ -284,3 +280,54 @@ def get_random_str(length=5):
Generates a random string of length `length`. Generates a random string of length `length`.
""" """
return "".join(random.choices(string.ascii_letters + string.digits, k=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))
+1 -1
View File
@@ -18,7 +18,6 @@ from app.utils import background, get_home_res_path, get_ip, is_windows
werkzeug = logging.getLogger("werkzeug") werkzeug = logging.getLogger("werkzeug")
werkzeug.setLevel(logging.ERROR) werkzeug.setLevel(logging.ERROR)
class Variables: class Variables:
FLASK_PORT = 1970 FLASK_PORT = 1970
FLASK_HOST = "localhost" FLASK_HOST = "localhost"
@@ -180,6 +179,7 @@ if __name__ == "__main__":
log_info() log_info()
run_bg_checks() run_bg_checks()
start_watchdog() start_watchdog()
app.run( app.run(
debug=True, debug=True,
threaded=True, threaded=True,
Generated
+34 -7
View File
@@ -340,14 +340,14 @@ tornado = ["tornado (>=0.2)"]
[[package]] [[package]]
name = "hypothesis" name = "hypothesis"
version = "6.65.0" version = "6.65.1"
description = "A library for property-based testing" description = "A library for property-based testing"
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "hypothesis-6.65.0-py3-none-any.whl", hash = "sha256:24e3219b0b181414c06bb7a62649a6edb471f148d25c9c9687f47505b0f50b1c"}, {file = "hypothesis-6.65.1-py3-none-any.whl", hash = "sha256:4b7ae16db09151d17e5feebea07f4f84693cc1573c25e280bc92e619df24182b"},
{file = "hypothesis-6.65.0.tar.gz", hash = "sha256:d25914dd4008b0292d116ac315f01f6691c5460c494a0291c01d96f4bc17fe68"}, {file = "hypothesis-6.65.1.tar.gz", hash = "sha256:fb9757f4b556fc73c2eaa2c1b7d39d0184c75e4cb77dadaf6fa59373838bd629"},
] ]
[package.dependencies] [package.dependencies]
@@ -602,14 +602,14 @@ files = [
[[package]] [[package]]
name = "pathspec" name = "pathspec"
version = "0.10.3" version = "0.11.0"
description = "Utility library for gitignore style pattern matching of file paths." description = "Utility library for gitignore style pattern matching of file paths."
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "pathspec-0.10.3-py3-none-any.whl", hash = "sha256:3c95343af8b756205e2aba76e843ba9520a24dd84f68c22b9f93251507509dd6"}, {file = "pathspec-0.11.0-py3-none-any.whl", hash = "sha256:3a66eb970cbac598f9e5ccb5b2cf58930cd8e3ed86d393d541eaf2d8b1705229"},
{file = "pathspec-0.10.3.tar.gz", hash = "sha256:56200de4077d9d0791465aa9095a01d421861e405b5096955051deefd697d6f6"}, {file = "pathspec-0.11.0.tar.gz", hash = "sha256:64d338d4e0914e91c1792321e6907b5a593f1ab1851de7fc269557a21b30ebbc"},
] ]
[[package]] [[package]]
@@ -749,6 +749,33 @@ files = [
dev = ["pre-commit", "tox"] dev = ["pre-commit", "tox"]
testing = ["pytest", "pytest-benchmark"] 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]] [[package]]
name = "pyinstaller" name = "pyinstaller"
version = "5.7.0" version = "5.7.0"
@@ -1261,4 +1288,4 @@ files = [
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = ">=3.10,<3.12" python-versions = ">=3.10,<3.12"
content-hash = "15b3fba920faab237353240b2b3dbb32603744f6a8ff19e77fe6296d5252c2d7" content-hash = "54e3995dc11627cb8d20d27ba6d593e4d6d102698c9bd3c29d5505287d22b9e2"
+2
View File
@@ -17,6 +17,7 @@ tqdm = "^4.64.0"
rapidfuzz = "^2.13.7" rapidfuzz = "^2.13.7"
tinytag = "^1.8.1" tinytag = "^1.8.1"
Unidecode = "^1.3.6" Unidecode = "^1.3.6"
psutil = "^5.9.4"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
pylint = "^2.15.5" pylint = "^2.15.5"
@@ -28,6 +29,7 @@ pyinstaller = "^5.7.0"
version = "^22.6.0" version = "^22.6.0"
allow-prereleases = true allow-prereleases = true
[build-system] [build-system]
requires = ["poetry-core>=1.0.0"] requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api" build-backend = "poetry.core.masonry.api"
+14 -10
View File
@@ -1,33 +1,37 @@
from hypothesis import given
from hypothesis import strategies as st
import app.utils import app.utils
from hypothesis import given, strategies as st from app.utils import parse_feat_from_title
from app.utils import extract_featured_artists_from_title
def test_extract_featured_artists_from_title(): def test_extract_featured_artists_from_title():
test_titles = [ test_titles = [
"Own it (Featuring Ed Sheeran & Stormzy)", "Own it (Featuring Ed Sheeran & Stormzy)",
"Own it (Featuring Ed Sheeran and Stormzy)",
"Autograph (On my line)(Feat. Lil Peep)(Deluxe)", "Autograph (On my line)(Feat. Lil Peep)(Deluxe)",
"Why so sad? (with Juice Wrld, Lil Peep)", "Why so sad? (with Juice Wrld, Lil Peep)",
"Why so sad? (with Juice Wrld/Lil Peep)", "Why so sad? (with Juice Wrld/Lil Peep)",
"Simmer (with Burna Boy)", "Simmer (with Burna Boy)",
"Simmer (without Burna Boy)" "Simmer (without Burna Boy)",
] ]
results = [ results = [
["Ed Sheeran", "Stormzy"], ["Ed Sheeran", "Stormzy"],
['Lil Peep'], ["Ed Sheeran", "Stormzy"],
["Juice Wrld", "Lil Peep"], ["Lil Peep"],
["Juice Wrld", "Lil Peep"], ["Juice Wrld", "Lil Peep"],
["Juice Wrld/Lil Peep"],
["Burna Boy"], ["Burna Boy"],
[] [],
] ]
for title, expected in zip(test_titles, results): 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 === # === HYPOTHESIS GHOSTWRITER TESTS ===
@given(__dir=st.text(), full=st.booleans()) # @given(__dir=st.text(), full=st.booleans())
def test_fuzz_run_fast_scandir(__dir: str, full) -> None: # def test_fuzz_run_fast_scandir(__dir: str, full) -> None:
app.utils.run_fast_scandir(_dir=__dir, full=full) # app.utils.run_fast_scandir(_dir=__dir, full=full)