fix duplicate artist and album color entry in db

+ Remove folder store
+ Reduce fuzzy search score cutoff from 90% to 75%
+ use inheritance to init Artist class
+ misc
This commit is contained in:
geoffrey45 2023-03-26 18:01:26 +03:00
parent 357afeb700
commit 5487dad27b
18 changed files with 102 additions and 333 deletions

View File

@ -8,6 +8,11 @@ api = Blueprint("colors", __name__, url_prefix="/colors")
def get_album_color(albumhash: str): def get_album_color(albumhash: str):
album = Store.get_album_by_hash(albumhash) album = Store.get_album_by_hash(albumhash)
if len(album.colors) > 0:
return {
"color": album.colors[0]
}
return { return {
"color": album.colors[0] "color": ""
} }

View File

@ -8,7 +8,7 @@ from pathlib import Path
from flask import Blueprint, request from flask import Blueprint, request
from app import settings from app import settings
from app.lib.folderslib import GetFilesAndDirs, create_folder from app.lib.folderslib import GetFilesAndDirs, get_folders
from app.db.sqlite.settings import SettingsSQLMethods as db from app.db.sqlite.settings import SettingsSQLMethods as db
from app.utils.wintools import win_replace_slash, is_windows from app.utils.wintools import win_replace_slash, is_windows
@ -30,6 +30,7 @@ def get_folder_tree():
req_dir = "$home" req_dir = "$home"
root_dirs = db.get_root_dirs() root_dirs = db.get_root_dirs()
root_dirs.sort()
try: try:
if req_dir == "$home" and root_dirs[0] == "$home": if req_dir == "$home" and root_dirs[0] == "$home":
@ -38,19 +39,17 @@ def get_folder_tree():
pass pass
if req_dir == "$home": if req_dir == "$home":
folders = [Path(f) for f in root_dirs] folders = get_folders(root_dirs)
return { return {
"folders": [ "folders": folders,
create_folder(str(f)) for f in folders
],
"tracks": [], "tracks": [],
} }
if is_windows(): if is_windows():
# Trailing slash needed when drive letters are passed, # Trailing slash needed when drive letters are passed,
# Remember, the trailing slash is removed in the client. # Remember, the trailing slash is removed in the client.
req_dir = req_dir + "/" req_dir += "/"
else: else:
req_dir = "/" + req_dir + "/" if not req_dir.startswith("/") else req_dir + "/" req_dir = "/" + req_dir + "/" if not req_dir.startswith("/") else req_dir + "/"
@ -66,7 +65,7 @@ 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(all=False) drives = psutil.disk_partitions()
drives = [d.mountpoint for d in drives] drives = [d.mountpoint for d in drives]
if is_win: if is_win:
@ -99,7 +98,7 @@ def list_folders():
} }
if is_win: if is_win:
req_dir = req_dir + "/" req_dir += "/"
else: else:
req_dir = "/" + req_dir + "/" req_dir = "/" + req_dir + "/"
req_dir = str(Path(req_dir).resolve()) req_dir = str(Path(req_dir).resolve())

View File

@ -34,9 +34,9 @@ delete_playlist = PL.delete_playlist
# get_tracks_by_trackhashes = SQLiteTrackMethods.get_tracks_by_trackhashes # get_tracks_by_trackhashes = SQLiteTrackMethods.get_tracks_by_trackhashes
def duplicate_images(images: list): def duplicate_images(images: list):
if len(images) == 1: if len(images) == 1:
images = images * 4 images *= 4
elif len(images) == 2: elif len(images) == 2:
images = images + list(reversed(images)) images += list(reversed(images))
elif len(images) == 3: elif len(images) == 3:
images = images + images[:1] images = images + images[:1]

View File

@ -8,7 +8,6 @@ from app.db.sqlite.settings import SettingsSQLMethods as sdb
from app.utils.generators import get_random_str from app.utils.generators import get_random_str
from app.utils.threading import background from app.utils.threading import background
from app.store.folder import FolderStore
from app.store.albums import AlbumStore from app.store.albums import AlbumStore
from app.store.tracks import TrackStore from app.store.tracks import TrackStore
from app.store.artists import ArtistStore from app.store.artists import ArtistStore
@ -27,7 +26,6 @@ def reload_everything():
Reloads all stores using the current database items Reloads all stores using the current database items
""" """
TrackStore.load_all_tracks() TrackStore.load_all_tracks()
FolderStore.process_folders()
AlbumStore.load_albums() AlbumStore.load_albums()
ArtistStore.load_artists() ArtistStore.load_artists()

View File

@ -1,6 +1,6 @@
from sqlite3 import Cursor from sqlite3 import Cursor
from .utils import SQLiteManager, tuple_to_album, tuples_to_albums from .utils import SQLiteManager, tuples_to_albums
class SQLiteAlbumMethods: class SQLiteAlbumMethods:
@ -10,30 +10,15 @@ class SQLiteAlbumMethods:
Inserts one album into the database Inserts one album into the database
""" """
sql = """INSERT INTO albums( sql = """INSERT OR REPLACE INTO albums(
albumhash, albumhash,
colors colors
) VALUES(?,?) ) VALUES(?,?)
""" """
cur.execute(sql, (albumhash, colors)) cur.execute(sql, (albumhash, colors))
return cur.lastrowid return cur.lastrowid
# @classmethod
# def insert_many_albums(cls, albums: list[dict]):
# """
# Takes a generator of albums, and inserts them into the database
# Parameters
# ----------
# albums : Generator
# Generator
# """
# with SQLiteManager() as cur:
# for album in albums:
# cls.insert_one_album(cur, album["albumhash"], album["colors"])
@classmethod @classmethod
def get_all_albums(cls): def get_all_albums(cls):
with SQLiteManager() as cur: with SQLiteManager() as cur:
@ -45,58 +30,6 @@ class SQLiteAlbumMethods:
return [] return []
# @staticmethod
# def get_album_by_id(album_id: int):
# conn = get_sqlite_conn()
# cur = conn.cursor()
# cur.execute("SELECT * FROM albums WHERE id=?", (album_id,))
# album = cur.fetchone()
# conn.close()
# if album is None:
# return None
# return tuple_to_album(album)
@staticmethod
def get_album_by_hash(album_hash: str):
with SQLiteManager() as cur:
cur.execute("SELECT * FROM albums WHERE albumhash=?", (album_hash,))
album = cur.fetchone()
if album is not None:
return tuple_to_album(album)
return None
@classmethod
def get_albums_by_hashes(cls, album_hashes: list):
"""
Gets all the albums with the specified hashes. Returns a generator of albums or an empty list.
"""
with SQLiteManager() as cur:
hashes = ",".join("?" * len(album_hashes))
cur.execute(
f"SELECT * FROM albums WHERE albumhash IN ({hashes})", album_hashes
)
albums = cur.fetchall()
if albums is not None:
return tuples_to_albums(albums)
return []
# @staticmethod
# def update_album_colors(album_hash: str, colors: list[str]):
# sql = "UPDATE albums SET colors=? WHERE albumhash=?"
# colors_str = json.dumps(colors)
# with SQLiteManager() as cur:
# cur.execute(sql, (colors_str, album_hash))
@staticmethod @staticmethod
def get_albums_by_albumartist(albumartist: str): def get_albums_by_albumartist(albumartist: str):
with SQLiteManager() as cur: with SQLiteManager() as cur:
@ -107,17 +40,3 @@ class SQLiteAlbumMethods:
return tuples_to_albums(albums) return tuples_to_albums(albums)
return [] return []
@staticmethod
def get_all_albums_raw():
"""
Returns all the albums in the database, as a list of tuples.
"""
with SQLiteManager() as cur:
cur.execute("SELECT * FROM albums")
albums = cur.fetchall()
if albums is not None:
return albums
return []

View File

@ -3,27 +3,27 @@ Contains methods for reading and writing to the sqlite artists database.
""" """
import json import json
from sqlite3 import Cursor
from .utils import SQLiteManager from .utils import SQLiteManager
class SQLiteArtistMethods: class SQLiteArtistMethods:
@classmethod @staticmethod
def insert_one_artist(cls, artisthash: str, colors: str | list[str]): def insert_one_artist(cur: Cursor, artisthash: str, colors: str | list[str]):
""" """
Inserts a single artist into the database. Inserts a single artist into the database.
""" """
sql = """INSERT INTO artists( sql = """INSERT OR REPLACE INTO artists(
artisthash, artisthash,
colors colors
) VALUES(?,?) ) VALUES(?,?)
""" """
colors = json.dumps(colors) colors = json.dumps(colors)
cur.execute(sql, (artisthash, colors))
with SQLiteManager() as cur: @staticmethod
cur.execute(sql, (artisthash, colors)) def get_all_artists():
@classmethod
def get_all_artists(cls):
""" """
Get all artists from the database and return a generator of Artist objects Get all artists from the database and return a generator of Artist objects
""" """

View File

@ -45,13 +45,15 @@ CREATE TABLE IF NOT EXISTS tracks (
genre text, genre text,
title text NOT NULL, title text NOT NULL,
track integer NOT NULL, track integer NOT NULL,
trackhash text NOT NULL trackhash text NOT NULL,
UNIQUE (filepath)
); );
CREATE TABLE IF NOT EXISTS albums ( CREATE TABLE IF NOT EXISTS albums (
id integer PRIMARY KEY, id integer PRIMARY KEY,
albumhash text NOT NULL, albumhash text NOT NULL,
colors text NOT NULL colors text NOT NULL,
UNIQUE (albumhash)
); );
@ -60,7 +62,8 @@ CREATE TABLE IF NOT EXISTS artists (
id integer PRIMARY KEY, id integer PRIMARY KEY,
artisthash text NOT NULL, artisthash text NOT NULL,
colors text, colors text,
bio text bio text,
UNIQUE (artisthash)
); );
CREATE TABLE IF NOT EXISTS folders ( CREATE TABLE IF NOT EXISTS folders (

View File

@ -52,6 +52,7 @@ class SettingsSQLMethods:
for _dir in dirs: for _dir in dirs:
cur.execute(sql, (_dir,)) cur.execute(sql, (_dir,))
# Not currently used anywhere, to be used later
@staticmethod @staticmethod
def add_excluded_dirs(dirs: list[str]): def add_excluded_dirs(dirs: list[str]):
""" """

View File

@ -82,26 +82,6 @@ class SQLiteTrackMethods:
return None return None
@staticmethod
def get_tracks_by_trackhashes(hashes: list[str]):
"""
Gets all tracks in a list of trackhashes.
Returns a generator of Track objects or an empty list.
"""
sql = "SELECT * FROM tracks WHERE trackhash IN ({})".format(
",".join("?" * len(hashes))
)
with SQLiteManager() as cur:
cur.execute(sql, hashes)
rows = cur.fetchall()
if rows is not None:
return tuples_to_tracks(rows)
return []
@staticmethod @staticmethod
def remove_track_by_filepath(filepath: str): def remove_track_by_filepath(filepath: str):
""" """

View File

@ -1,7 +1,8 @@
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
from pathlib import Path from pathlib import Path
from io import BytesIO from io import BytesIO
from PIL import Image
from PIL import Image, UnidentifiedImageError
import requests import requests
import urllib import urllib
@ -55,7 +56,10 @@ class DownloadImage:
""" """
Downloads the image from the url. Downloads the image from the url.
""" """
return Image.open(BytesIO(requests.get(url, timeout=10).content)) try:
return Image.open(BytesIO(requests.get(url, timeout=10).content))
except UnidentifiedImageError:
return None
@staticmethod @staticmethod
def save_img(img: Image.Image, sm_path: Path, lg_path: Path): def save_img(img: Image.Image, sm_path: Path, lg_path: Path):

View File

@ -12,16 +12,15 @@ from app import settings
from app.db.sqlite.albums import SQLiteAlbumMethods as db from app.db.sqlite.albums import SQLiteAlbumMethods as db
from app.db.sqlite.artists import SQLiteArtistMethods as adb from app.db.sqlite.artists import SQLiteArtistMethods as adb
from app.db.sqlite.utils import SQLiteManager from app.db.sqlite.utils import SQLiteManager
from app.models import Album, Artist
from app.store.artists import ArtistStore from app.store.artists import ArtistStore
from app.store.albums import AlbumStore from app.store.albums import AlbumStore
def get_image_colors(image: str) -> list[str]: def get_image_colors(image: str, count=1) -> list[str]:
"""Extracts 2 of the most dominant colors from an image.""" """Extracts n number of the most dominant colors from an image."""
try: try:
colors = sorted(colorgram.extract(image, 1), key=lambda c: c.hsl.h) colors = sorted(colorgram.extract(image, count), key=lambda c: c.hsl.h)
except OSError: except OSError:
return [] return []
@ -34,6 +33,16 @@ def get_image_colors(image: str) -> list[str]:
return formatted_colors return formatted_colors
def process_color(item_hash: str, is_album=True):
path = settings.Paths.SM_THUMB_PATH if is_album else settings.Paths.ARTIST_IMG_SM_PATH
path = Path(path) / (item_hash + ".webp")
if not path.exists():
return
return get_image_colors(str(path))
class ProcessAlbumColors: class ProcessAlbumColors:
""" """
Extracts the most dominant color from the album art and saves it to the database. Extracts the most dominant color from the album art and saves it to the database.
@ -44,26 +53,22 @@ class ProcessAlbumColors:
with SQLiteManager() as cur: with SQLiteManager() as cur:
for album in tqdm(albums, desc="Processing missing album colors"): for album in tqdm(albums, desc="Processing missing album colors"):
colors = self.process_color(album) sql = "SELECT COUNT(1) FROM albums WHERE albumhash = ?"
cur.execute(sql, (album.albumhash,))
count = cur.fetchone()[0]
if count != 0:
continue
colors = process_color(album.albumhash)
if colors is None: if colors is None:
continue continue
album.set_colors(colors) album.set_colors(colors)
color_str = json.dumps(colors) color_str = json.dumps(colors)
db.insert_one_album(cur, album.albumhash, color_str) db.insert_one_album(cur, album.albumhash, color_str)
@staticmethod
def process_color(album: Album):
path = Path(settings.Paths.SM_THUMB_PATH) / album.image
if not path.exists():
return
colors = get_image_colors(str(path))
return colors
class ProcessArtistColors: class ProcessArtistColors:
""" """
@ -73,27 +78,20 @@ class ProcessArtistColors:
def __init__(self) -> None: def __init__(self) -> None:
all_artists = [a for a in ArtistStore.artists if len(a.colors) == 0] all_artists = [a for a in ArtistStore.artists if len(a.colors) == 0]
for artist in tqdm(all_artists, desc="Processing missing artist colors"): with SQLiteManager() as cur:
self.process_color(artist) for artist in tqdm(all_artists, desc="Processing missing artist colors"):
sql = "SELECT COUNT(1) FROM artists WHERE artisthash = ?"
@staticmethod cur.execute(sql, (artist.artisthash,))
def process_color(artist: Artist): count = cur.fetchone()[0]
path = Path(settings.Paths.ARTIST_IMG_SM_PATH) / artist.image
if not path.exists(): if count != 0:
return continue
colors = get_image_colors(str(path)) colors = process_color(artist.artisthash, is_album=False)
if len(colors) > 0: if colors is None:
adb.insert_one_artist(artisthash=artist.artisthash, colors=colors) continue
ArtistStore.map_artist_color((0, artist.artisthash, json.dumps(colors)))
# TODO: If item color is in db, get it, assign it to the item and continue. artist.set_colors(colors)
# - Format all colors in the format: rgb(123, 123, 123) adb.insert_one_artist(cur, artist.artisthash, colors)
# - Each digit should be 3 digits long.
# - Format all db colors into a master string of the format "-itemhash:colorhash-"
# - Find the item hash using index() and get the color using the index + number, where number
# is the length of the rgb string + 1
# - Assign the color to the item and continue.
# - If the color is not in the db, extract it and add it to the db.

View File

@ -12,7 +12,6 @@ from app.logger import log
from app.models import Album, Artist, Track from app.models import Album, Artist, Track
from app.utils.filesystem import run_fast_scandir from app.utils.filesystem import run_fast_scandir
from app.store.folder import FolderStore
from app.store.albums import AlbumStore from app.store.albums import AlbumStore
from app.store.tracks import TrackStore from app.store.tracks import TrackStore
from app.store.artists import ArtistStore from app.store.artists import ArtistStore
@ -102,7 +101,6 @@ class Populate:
track.is_favorite = track.trackhash in fav_tracks track.is_favorite = track.trackhash in fav_tracks
TrackStore.add_track(track) TrackStore.add_track(track)
FolderStore.add_folder(track.folder)
if not AlbumStore.album_exists(track.albumhash): if not AlbumStore.album_exists(track.albumhash):
AlbumStore.add_album(AlbumStore.create_album(track)) AlbumStore.add_album(AlbumStore.create_album(track))

View File

@ -23,10 +23,10 @@ class Cutoff:
Holds all the default cutoff values. Holds all the default cutoff values.
""" """
tracks: int = 90 tracks: int = 75
albums: int = 90 albums: int = 75
artists: int = 90 artists: int = 75
playlists: int = 90 playlists: int = 75
class Limit: class Limit:
@ -54,7 +54,6 @@ class SearchTracks:
results = process.extract( results = process.extract(
self.query, self.query,
track_titles, track_titles,
scorer=fuzz.WRatio,
score_cutoff=Cutoff.tracks, score_cutoff=Cutoff.tracks,
limit=Limit.tracks, limit=Limit.tracks,
) )
@ -77,7 +76,6 @@ class SearchArtists:
results = process.extract( results = process.extract(
self.query, self.query,
artists, artists,
scorer=fuzz.WRatio,
score_cutoff=Cutoff.artists, score_cutoff=Cutoff.artists,
limit=Limit.artists, limit=Limit.artists,
) )
@ -100,7 +98,6 @@ class SearchAlbums:
results = process.extract( results = process.extract(
self.query, self.query,
albums, albums,
scorer=fuzz.WRatio,
score_cutoff=Cutoff.albums, score_cutoff=Cutoff.albums,
limit=Limit.albums, limit=Limit.albums,
) )
@ -125,7 +122,6 @@ class SearchPlaylists:
results = process.extract( results = process.extract(
self.query, self.query,
playlists, playlists,
scorer=fuzz.WRatio,
score_cutoff=Cutoff.playlists, score_cutoff=Cutoff.playlists,
limit=Limit.playlists, limit=Limit.playlists,
) )
@ -176,7 +172,6 @@ class SearchAll:
results = process.extract( results = process.extract(
query=query, query=query,
choices=items, choices=items,
scorer=fuzz.WRatio,
score_cutoff=Cutoff.tracks, score_cutoff=Cutoff.tracks,
limit=20 limit=20
) )

View File

@ -17,7 +17,6 @@ from app.db.sqlite.tracks import SQLiteManager
from app.db.sqlite.tracks import SQLiteTrackMethods as db from app.db.sqlite.tracks import SQLiteTrackMethods as db
from app.db.sqlite.settings import SettingsSQLMethods as sdb from app.db.sqlite.settings import SettingsSQLMethods as sdb
from app.store.folder import FolderStore
from app.store.tracks import TrackStore from app.store.tracks import TrackStore
from app.store.albums import AlbumStore from app.store.albums import AlbumStore
from app.store.artists import ArtistStore from app.store.artists import ArtistStore
@ -144,8 +143,6 @@ def add_track(filepath: str) -> None:
track = Track(**tags) track = Track(**tags)
TrackStore.add_track(track) TrackStore.add_track(track)
FolderStore.add_folder(track.folder)
if not AlbumStore.album_exists(track.albumhash): if not AlbumStore.album_exists(track.albumhash):
album = AlbumStore.create_album(track) album = AlbumStore.create_album(track)
AlbumStore.add_album(album) AlbumStore.add_album(album)
@ -182,11 +179,6 @@ def remove_track(filepath: str) -> None:
if empty_artist: if empty_artist:
ArtistStore.remove_artist_by_hash(artist.artisthash) ArtistStore.remove_artist_by_hash(artist.artisthash)
empty_folder = FolderStore.is_empty_folder(track.folder)
if empty_folder:
FolderStore.remove_folder(track.folder)
class Handler(PatternMatchingEventHandler): class Handler(PatternMatchingEventHandler):
files_to_process = [] files_to_process = []
@ -204,7 +196,6 @@ class Handler(PatternMatchingEventHandler):
self, self,
patterns=patterns, patterns=patterns,
ignore_directories=True, ignore_directories=True,
case_sensitive=False,
) )
def get_abs_path(self, path: str): def get_abs_path(self, path: str):

View File

@ -5,27 +5,6 @@ from dataclasses import dataclass
from app.utils.hashing import create_hash from app.utils.hashing import create_hash
@dataclass(slots=True)
class Artist:
"""
Artist class
"""
name: str
artisthash: str = ""
image: str = ""
trackcount: int = 0
albumcount: int = 0
duration: int = 0
colors: list[str] = dataclasses.field(default_factory=list)
is_favorite: bool = False
def __post_init__(self):
self.artisthash = create_hash(self.name, decode=True)
self.image = self.artisthash + ".webp"
self.colors = json.loads(str(self.colors))
@dataclass(slots=True) @dataclass(slots=True)
class ArtistMinimal: class ArtistMinimal:
""" """
@ -36,6 +15,29 @@ class ArtistMinimal:
artisthash: str = "" artisthash: str = ""
image: str = "" image: str = ""
def __post_init__(self): def __init__(self, name: str):
self.name = name
self.artisthash = create_hash(self.name, decode=True) self.artisthash = create_hash(self.name, decode=True)
self.image = self.artisthash + ".webp" self.image = self.artisthash + ".webp"
@dataclass(slots=True)
class Artist(ArtistMinimal):
"""
Artist class
"""
trackcount: int = 0
albumcount: int = 0
duration: int = 0
colors: list[str] = dataclasses.field(default_factory=list)
is_favorite: bool = False
def __post_init__(self):
super(Artist, self).__init__(self.name)
self.colors = json.loads(str(self.colors))
def set_colors(self, colors: list[str]):
self.colors = colors
# TODO: Use inheritance to create the classes in this file.

View File

@ -1,7 +1,6 @@
""" """
Prepares the server for use. Prepares the server for use.
""" """
from app.store.folder import FolderStore
from app.setup.files import create_config_dir from app.setup.files import create_config_dir
from app.setup.sqlite import setup_sqlite, run_migrations from app.setup.sqlite import setup_sqlite, run_migrations
@ -16,6 +15,5 @@ def run_setup():
run_migrations() run_migrations()
TrackStore.load_all_tracks() TrackStore.load_all_tracks()
FolderStore.process_folders()
AlbumStore.load_albums() AlbumStore.load_albums()
ArtistStore.load_artists() ArtistStore.load_artists()

View File

@ -1,122 +0,0 @@
"""
In memory store.
"""
from pathlib import Path
from tqdm import tqdm
from app.models import Folder
from app.utils.bisection import UseBisection
from app.utils.hashing import create_folder_hash
from app.lib import folderslib
from .tracks import TrackStore
class FolderStore:
"""
This class holds all tracks in memory and provides methods for
interacting with them.
"""
folders: list[Folder] = []
@classmethod
def check_has_tracks(cls, path: str): # type: ignore
"""
Checks if a folder has tracks.
"""
path_hashes = "".join(f.path_hash for f in cls.folders)
path_hash = create_folder_hash(*Path(path).parts[1:])
return path_hash in path_hashes
@classmethod
def is_empty_folder(cls, path: str):
"""
Checks if a folder has tracks using tracks in the store.
"""
all_folders = set(track.folder for track in TrackStore.tracks)
folder_hashes = "".join(
create_folder_hash(*Path(f).parts[1:]) for f in all_folders
)
path_hash = create_folder_hash(*Path(path).parts[1:])
return path_hash in folder_hashes
@classmethod
def add_folder(cls, path: str):
"""
Adds a folder to the store.
"""
if cls.check_has_tracks(path):
return
folder = folderslib.create_folder(path)
cls.folders.append(folder)
@classmethod
def remove_folder(cls, path: str):
"""
Removes a folder from the store.
"""
for folder in cls.folders:
if folder.path == path:
cls.folders.remove(folder)
break
@classmethod
def process_folders(cls):
"""
Creates a list of folders from the tracks in the store.
"""
cls.folders.clear()
all_folders = [track.folder for track in TrackStore.tracks]
all_folders = set(all_folders)
all_folders = [
folder for folder in all_folders if not cls.check_has_tracks(folder)
]
all_folders = [Path(f) for f in all_folders]
# all_folders = [f for f in all_folders if f.exists()]
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 = folderslib.create_folder(str(path))
cls.folders.append(folder)
@classmethod
def get_folder(cls, path: str): # type: ignore
"""
Returns a folder object by its path.
"""
# TODO: Modify this method to accept a list of paths, sorting is computationally expensive.
folders = sorted(cls.folders, key=lambda x: x.path)
folder = UseBisection(folders, "path", [path])()[0]
if folder is not None:
return folder
has_tracks = cls.check_has_tracks(path)
if not has_tracks:
return None
folder = folderslib.create_folder(path)
cls.folders.append(folder)
return folder
# TODO: Remove this file. it's no longer needed.

View File

@ -9,11 +9,11 @@ python = ">=3.10,<3.12"
Flask = "^2.0.2" Flask = "^2.0.2"
Flask-Cors = "^3.0.10" Flask-Cors = "^3.0.10"
requests = "^2.27.1" requests = "^2.27.1"
watchdog = "^2.2.1" watchdog = "^3.0.0"
gunicorn = "^20.1.0" gunicorn = "^20.1.0"
Pillow = "^9.0.1" Pillow = "^9.0.1"
"colorgram.py" = "^1.2.0" "colorgram.py" = "^1.2.0"
tqdm = "^4.64.0" tqdm = "^4.65.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"
@ -23,7 +23,7 @@ psutil = "^5.9.4"
pylint = "^2.15.5" pylint = "^2.15.5"
pytest = "^7.1.3" pytest = "^7.1.3"
hypothesis = "^6.56.3" hypothesis = "^6.56.3"
pyinstaller = "^5.7.0" pyinstaller = "^5.9.0"
[tool.poetry.dev-dependencies.black] [tool.poetry.dev-dependencies.black]
version = "^22.6.0" version = "^22.6.0"