mirror of
https://github.com/tcsenpai/swingmusic.git
synced 2025-06-06 19:25:34 +00:00
feat: exit the Populate function when another one is started
+ add test for the extract_fetured_artists_from_title function
This commit is contained in:
parent
2ba1b0386e
commit
af4221e0c7
@ -4,7 +4,7 @@ from app import settings
|
||||
from app.logger import log
|
||||
from app.lib import populate
|
||||
from app.db.store import Store
|
||||
from app.utils import background
|
||||
from app.utils import background, get_random_str
|
||||
from app.lib.watchdogg import Watcher as WatchDog
|
||||
from app.db.sqlite.settings import SettingsSQLMethods as sdb
|
||||
|
||||
@ -17,21 +17,32 @@ def get_child_dirs(parent: str, children: list[str]):
|
||||
return [_dir for _dir in children if _dir.startswith(parent) and _dir != parent]
|
||||
|
||||
|
||||
@background
|
||||
def rebuild_store(db_dirs: list[str]):
|
||||
def reload_everything():
|
||||
"""
|
||||
Restarts the watchdog and rebuilds the music library.
|
||||
Reloads all stores using the current database items
|
||||
"""
|
||||
|
||||
log.info("Rebuilding library...")
|
||||
Store.remove_tracks_by_dir_except(db_dirs)
|
||||
|
||||
Store.load_all_tracks()
|
||||
Store.process_folders()
|
||||
Store.load_albums()
|
||||
Store.load_artists()
|
||||
|
||||
populate.Populate()
|
||||
|
||||
@background
|
||||
def rebuild_store(db_dirs: list[str]):
|
||||
"""
|
||||
Restarts the watchdog and rebuilds the music library.
|
||||
"""
|
||||
log.info("Rebuilding library...")
|
||||
Store.remove_tracks_by_dir_except(db_dirs)
|
||||
reload_everything()
|
||||
|
||||
key = get_random_str()
|
||||
try:
|
||||
populate.Populate(key=key)
|
||||
except populate.PopulateCancelledError:
|
||||
reload_everything()
|
||||
return
|
||||
|
||||
WatchDog().restart()
|
||||
|
||||
log.info("Rebuilding library... ✅")
|
||||
|
@ -8,7 +8,7 @@ from requests import ReadTimeout
|
||||
from app import utils
|
||||
from app.lib.artistlib import CheckArtistImages
|
||||
from app.lib.colorlib import ProcessAlbumColors, ProcessArtistColors
|
||||
from app.lib.populate import Populate, ProcessTrackThumbnails
|
||||
from app.lib.populate import Populate, ProcessTrackThumbnails, PopulateCancelledError
|
||||
from app.lib.trackslib import validate_tracks
|
||||
from app.logger import log
|
||||
|
||||
@ -23,8 +23,11 @@ def run_periodic_checks():
|
||||
validate_tracks()
|
||||
|
||||
while True:
|
||||
try:
|
||||
Populate(key=utils.get_random_str())
|
||||
except PopulateCancelledError:
|
||||
pass
|
||||
|
||||
Populate()
|
||||
ProcessTrackThumbnails()
|
||||
ProcessAlbumColors()
|
||||
ProcessArtistColors()
|
||||
|
@ -16,6 +16,12 @@ from app.utils import run_fast_scandir
|
||||
get_all_tracks = SQLiteTrackMethods.get_all_tracks
|
||||
insert_many_tracks = SQLiteTrackMethods.insert_many_tracks
|
||||
|
||||
POPULATE_KEY = ""
|
||||
|
||||
|
||||
class PopulateCancelledError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Populate:
|
||||
"""
|
||||
@ -25,7 +31,10 @@ class Populate:
|
||||
also checks if the album art exists in the image path, if not tries to extract it.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
def __init__(self, key: str) -> None:
|
||||
global POPULATE_KEY
|
||||
POPULATE_KEY = key
|
||||
|
||||
tracks = get_all_tracks()
|
||||
tracks = list(tracks)
|
||||
|
||||
@ -54,7 +63,7 @@ class Populate:
|
||||
log.info("All clear, no unread files.")
|
||||
return
|
||||
|
||||
self.tag_untagged(untagged)
|
||||
self.tag_untagged(untagged, key)
|
||||
|
||||
@staticmethod
|
||||
def filter_untagged(tracks: list[Track], files: list[str]):
|
||||
@ -62,7 +71,7 @@ class Populate:
|
||||
return set(files) - set(tagged_files)
|
||||
|
||||
@staticmethod
|
||||
def tag_untagged(untagged: set[str]):
|
||||
def tag_untagged(untagged: set[str], key: str):
|
||||
log.info("Found %s new tracks", len(untagged))
|
||||
tagged_tracks: list[dict] = []
|
||||
tagged_count = 0
|
||||
@ -71,6 +80,9 @@ class Populate:
|
||||
fav_tracks = "-".join([t[1] for t in fav_tracks])
|
||||
|
||||
for file in tqdm(untagged, desc="Reading files"):
|
||||
if POPULATE_KEY != key:
|
||||
raise PopulateCancelledError('Populate key changed')
|
||||
|
||||
tags = get_tags(file)
|
||||
|
||||
if tags is not None:
|
||||
|
@ -40,7 +40,6 @@ class Watcher:
|
||||
while trials < 10:
|
||||
try:
|
||||
dirs = sdb.get_root_dirs()
|
||||
print(dirs)
|
||||
dir_map = [
|
||||
{"original": d, "realpath": os.path.realpath(d)} for d in dirs
|
||||
]
|
||||
|
24
app/utils.py
24
app/utils.py
@ -1,7 +1,9 @@
|
||||
"""
|
||||
This module contains mini functions for the server.
|
||||
"""
|
||||
import random
|
||||
import re
|
||||
import string
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
@ -32,16 +34,19 @@ def background(func):
|
||||
return 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.
|
||||
"""
|
||||
|
||||
if _dir == "":
|
||||
return [], []
|
||||
|
||||
subfolders = []
|
||||
files = []
|
||||
|
||||
try:
|
||||
for _files in os.scandir(__dir):
|
||||
for _files in os.scandir(_dir):
|
||||
if _files.is_dir() and not _files.name.startswith("."):
|
||||
subfolders.append(_files.path)
|
||||
if _files.is_file():
|
||||
@ -54,7 +59,7 @@ def run_fast_scandir(__dir: str, full=False) -> tuple[list[str], list[str]]:
|
||||
sub_dirs, _files = run_fast_scandir(_dir, full=True)
|
||||
subfolders.extend(sub_dirs)
|
||||
files.extend(_files)
|
||||
except PermissionError:
|
||||
except (PermissionError, FileNotFoundError, ValueError):
|
||||
return [], []
|
||||
|
||||
return subfolders, files
|
||||
@ -177,13 +182,11 @@ def get_artists_from_tracks(tracks: list[models.Track]) -> set[str]:
|
||||
def get_albumartists(albums: list[models.Album]) -> set[str]:
|
||||
artists = set()
|
||||
|
||||
# master_artist_list = [a.albumartists for a in albums]
|
||||
for album in albums:
|
||||
albumartists = [a.name for a in album.albumartists] # type: ignore
|
||||
|
||||
artists.update(albumartists)
|
||||
|
||||
# return [models.Artist(a) for a in artists]
|
||||
return artists
|
||||
|
||||
|
||||
@ -231,7 +234,10 @@ 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.
|
||||
"""
|
||||
try:
|
||||
return (CWD / ".." / filename).resolve()
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
|
||||
def get_ip():
|
||||
@ -258,9 +264,6 @@ def split_artists(src: str):
|
||||
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.
|
||||
@ -276,3 +279,8 @@ def extract_featured_artists_from_title(title: str) -> list[str]:
|
||||
return artists
|
||||
|
||||
|
||||
def get_random_str(length=5):
|
||||
"""
|
||||
Generates a random string of length `length`.
|
||||
"""
|
||||
return "".join(random.choices(string.ascii_letters + string.digits, k=length))
|
||||
|
750
poetry.lock
generated
750
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@ -9,21 +9,24 @@ python = ">=3.10,<3.12"
|
||||
Flask = "^2.0.2"
|
||||
Flask-Cors = "^3.0.10"
|
||||
requests = "^2.27.1"
|
||||
watchdog = "^2.2.0"
|
||||
watchdog = "^2.2.1"
|
||||
gunicorn = "^20.1.0"
|
||||
Pillow = "^9.0.1"
|
||||
"colorgram.py" = "^1.2.0"
|
||||
tqdm = "^4.64.0"
|
||||
rapidfuzz = "^2.13.7"
|
||||
tinytag = "^1.8.1"
|
||||
hypothesis = "^6.56.3"
|
||||
pytest = "^7.1.3"
|
||||
Unidecode = "^1.3.6"
|
||||
pyinstaller = "^5.7.0"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
pylint = "^2.15.5"
|
||||
black = {version = "^22.6.0", allow-prereleases = true}
|
||||
pytest = "^7.1.3"
|
||||
hypothesis = "^6.56.3"
|
||||
pyinstaller = "^5.7.0"
|
||||
|
||||
[tool.poetry.dev-dependencies.black]
|
||||
version = "^22.6.0"
|
||||
allow-prereleases = true
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
|
16
start.sh
16
start.sh
@ -5,21 +5,5 @@ gpath=$(poetry run which gunicorn)
|
||||
|
||||
# $pytest # -q
|
||||
|
||||
while getopts ':s' opt; do
|
||||
case $opt in
|
||||
s)
|
||||
echo "Starting image server"
|
||||
cd "./app"
|
||||
"$gpath" -b 0.0.0.0:1971 -w 1 --threads=1 "imgserver:app" &
|
||||
cd ../
|
||||
echo "Done ✅"
|
||||
;;
|
||||
\?)
|
||||
echo "Invalid option: -$OPTARG" >&2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
||||
echo "Starting swing"
|
||||
"$gpath" -b 0.0.0.0:1970 --threads=2 "manage:create_api()"
|
@ -1,18 +0,0 @@
|
||||
from app.utils import extract_featured_artists_from_title
|
||||
|
||||
|
||||
def test_extract_featured_artists_from_title():
|
||||
test_titles = [
|
||||
"Own it (Featuring Ed Sheeran & Stormzy)",
|
||||
"Godzilla (Deluxe)(Feat. Juice Wrld)(Deluxe)",
|
||||
"Simmer (with Burna Boy)",
|
||||
]
|
||||
|
||||
expected_test_artists = [
|
||||
["Ed Sheeran", "Stormzy"],
|
||||
['Juice Wrld'],
|
||||
["Burna Boy"]
|
||||
]
|
||||
|
||||
for title, expected in zip(test_titles, expected_test_artists):
|
||||
assert extract_featured_artists_from_title(title) == expected
|
33
tests/test_utils.py
Normal file
33
tests/test_utils.py
Normal file
@ -0,0 +1,33 @@
|
||||
import app.utils
|
||||
from hypothesis import given, strategies as st
|
||||
from app.utils import extract_featured_artists_from_title
|
||||
|
||||
|
||||
def test_extract_featured_artists_from_title():
|
||||
test_titles = [
|
||||
"Own it (Featuring Ed Sheeran & 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)"
|
||||
]
|
||||
|
||||
results = [
|
||||
["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
|
||||
|
||||
|
||||
# === 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)
|
Loading…
x
Reference in New Issue
Block a user