mirror of
https://github.com/Dvorinka/swingmusic-extended.git
synced 2026-06-03 20:13:02 +00:00
update supported audio files in settings.py
+ add win_replace_slash function to format win path strings + misc
This commit is contained in:
+38
-7
@@ -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
|
||||
|
||||
+25
-17
@@ -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:
|
||||
|
||||
@@ -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]):
|
||||
|
||||
+13
-3
@@ -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)
|
||||
|
||||
+19
-6
@@ -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)
|
||||
|
||||
|
||||
+5
-2
@@ -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)
|
||||
|
||||
|
||||
+31
-9
@@ -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
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
+1
-1
@@ -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])
|
||||
|
||||
|
||||
+1
-1
@@ -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 =====
|
||||
|
||||
+74
-27
@@ -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))
|
||||
|
||||
@@ -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,
|
||||
|
||||
Generated
+34
-7
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
+14
-10
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user