From 83bbe69550ebcf8bec5586a85a9615516ce5220a Mon Sep 17 00:00:00 2001 From: mungai-njoroge Date: Mon, 4 Sep 2023 11:01:03 +0300 Subject: [PATCH] show artist decade in genres + assign default artist separators if db is empty + add instrumental to album version + check if album is a single by checking og_title and current title + hard code juice wrld artist name in model + set album aritst to first artist if track has no album artist + rewrite get_base_album_title regex to use existing album versions + misc --- app/api/artist.py | 49 ++++++++++++++++++++++++++++--------- app/db/sqlite/settings.py | 12 +++++---- app/enums/album_versions.py | 5 ++++ app/lib/taglib.py | 12 +++++++-- app/models/album.py | 14 ++++++----- app/models/artist.py | 4 +++ app/models/track.py | 16 +++++++----- app/store/albums.py | 18 +++++--------- app/utils/parsers.py | 17 ++++--------- 9 files changed, 93 insertions(+), 54 deletions(-) diff --git a/app/api/artist.py b/app/api/artist.py index a6e3aaa9..414bb6e2 100644 --- a/app/api/artist.py +++ b/app/api/artist.py @@ -3,6 +3,7 @@ Contains all the artist(s) routes. """ import random from collections import deque +from datetime import datetime from flask import Blueprint, request @@ -14,6 +15,7 @@ from app.serializers.track import serialize_tracks from app.store.albums import AlbumStore from app.store.artists import ArtistStore from app.store.tracks import TrackStore +from app.utils.bisection import UseBisection from app.utils.remove_duplicates import remove_duplicates api = Blueprint("artist", __name__, url_prefix="/") @@ -115,17 +117,12 @@ class ArtistsCache: """ entry = [a for a in cls.artists if a.artisthash == artisthash][0] - albums = [AlbumStore.get_album_by_hash(h) for h in entry.albumhashes] + src_albums = sorted(AlbumStore.albums, key=lambda x: x.albumhash) + albums = UseBisection( + source=src_albums, search_from="albumhash", queries=entry.albumhashes + )() + entry.albums = [album for album in albums if album is not None] - - store_albums = AlbumStore.get_albums_by_artisthash(artisthash) - - all_albums_hash = "-".join([a.albumhash for a in entry.albums]) - - for album in store_albums: - if album.albumhash not in all_albums_hash: - entry.albums.append(album) - entry.albums_fetched = True @classmethod @@ -222,15 +219,45 @@ def get_artist(artisthash: str): if t.genre is not None: genres = genres.union(t.genre) + genres = list(genres) + + min_stamp = min(t.date for t in tracks) + year = datetime.fromtimestamp(min_stamp).year + + # TODO: Find a way to round a number to the nearest lower 10, and just add an "s" to get the decade + + year = int(str(year)[:3]) + + decade = "" + + if year == 196: + decade = "60s" + elif year == 197: + decade = "70s" + elif year == 198: + decade = "80s" + elif year == 199: + decade = "90s" + elif year == 200: + decade = "00s" + elif year == 201: + decade = "10s" + elif year == 202: + decade = "20s" + + if decade: + genres.insert(0, decade) + return { "artist": artist, "tracks": serialize_tracks(tracks[:limit]), - "genres": list(genres), + "genres": genres, } @api.route("/artist//albums", methods=["GET"]) def get_artist_albums(artisthash: str): + # TODO: Remove the artist cache and only process the required albums ie. not all albums. because that means processing all 355 albums for juice wrld while only less than 20 are shown in the artist page. limit = request.args.get("limit") if limit is None: diff --git a/app/db/sqlite/settings.py b/app/db/sqlite/settings.py index 9c1a4909..393fe7dc 100644 --- a/app/db/sqlite/settings.py +++ b/app/db/sqlite/settings.py @@ -133,12 +133,14 @@ class SettingsSQLMethods: def load_settings(): s = SettingsSQLMethods.get_all_settings() - # artist separators - db_separators: str = s[0] - db_separators = db_separators.replace(" ", "") - separators = db_separators.split(",") + try: + db_separators: str = s[0] + db_separators = db_separators.replace(" ", "") + separators = db_separators.split(",") + separators = set(separators) + except IndexError: + separators = {";", "/"} - separators = set(separators) SessionVars.ARTIST_SEPARATORS = separators # boolean settings diff --git a/app/enums/album_versions.py b/app/enums/album_versions.py index 2eeda8ec..a73b128a 100644 --- a/app/enums/album_versions.py +++ b/app/enums/album_versions.py @@ -29,6 +29,7 @@ class AlbumVersionEnum(Enum): ARCHIVE_EDITION = ("archive",) Acoustic = ("acoustic",) + instrumental = ("instrumental",) DOUBLE_DISC = ("double disc", "double disk") SUMMER_EDITION = ("summer",) @@ -56,3 +57,7 @@ class AlbumVersionEnum(Enum): REISSUE = ("reissue",) REMASTERED = ("remaster",) TAYLORS_VERSION = ("taylor's version",) + + +def get_all_keywords(): + return "|".join("|".join(i.value) for i in AlbumVersionEnum) diff --git a/app/lib/taglib.py b/app/lib/taglib.py index 61e92cb8..4083bf45 100644 --- a/app/lib/taglib.py +++ b/app/lib/taglib.py @@ -40,8 +40,12 @@ def extract_thumb(filepath: str, webp_path: str, overwrite=False) -> bool: ratio = width / height img.save(original_img_path, "webp") - img.resize((tsize, int(tsize / ratio)), Image.ANTIALIAS).save(lg_img_path, "webp") - img.resize((sm_tsize, int(sm_tsize / ratio)), Image.ANTIALIAS).save(sm_img_path, "webp") + img.resize((tsize, int(tsize / ratio)), Image.ANTIALIAS).save( + lg_img_path, "webp" + ) + img.resize((sm_tsize, int(sm_tsize / ratio)), Image.ANTIALIAS).save( + sm_img_path, "webp" + ) if not overwrite and os.path.exists(sm_img_path): img_size = os.path.getsize(sm_img_path) @@ -167,6 +171,10 @@ def get_tags(filepath: str): tags.artists = tags.artist tags.albumartists = tags.albumartist + # sub underscore with space + tags.title = tags.title.replace("_", " ") + tags.album = tags.album.replace("_", " ") + tags = tags.__dict__ # delete all tag properties that start with _ (tinytag internals) diff --git a/app/models/album.py b/app/models/album.py index 91606fc3..73c77a62 100644 --- a/app/models/album.py +++ b/app/models/album.py @@ -173,8 +173,10 @@ class Album: if ( len(tracks) == 1 - and create_hash(tracks[0].title) - == create_hash(self.title) # if they have the same title + and ( + create_hash(tracks[0].title) == create_hash(self.title) + or create_hash(tracks[0].title) == create_hash(self.og_title) + ) # if they have the same title # and tracks[0].track == 1 # and tracks[0].disc == 1 # TODO: Review -> Are the above commented checks necessary? @@ -191,10 +193,10 @@ class Album: if self.date: return - dates = {int(t.date) for t in tracks if t.date} + dates = (int(t.date) for t in tracks if t.date) - if len(dates) == 0: - self.date = 0 - return + # if len(dates) == 0: + # self.date = 0 + # return self.date = datetime.datetime.fromtimestamp(min(dates)).year diff --git a/app/models/artist.py b/app/models/artist.py index 16bf580f..3c684c0c 100644 --- a/app/models/artist.py +++ b/app/models/artist.py @@ -19,6 +19,10 @@ class ArtistMinimal: self.artisthash = create_hash(self.name, decode=True) self.image = self.artisthash + ".webp" + # hack to override all the variations from unreleased files (sorry guys!) + if self.artisthash == "5a37d5315e": + self.name = "Juice WRLD" + @dataclass(slots=True) class Artist(ArtistMinimal): diff --git a/app/models/track.py b/app/models/track.py index 8dce12ac..b7e8b2e2 100644 --- a/app/models/track.py +++ b/app/models/track.py @@ -64,6 +64,16 @@ class Track: [a for a in featured if create_hash(a) not in original_lower] ) + self.artist_hashes = "-".join(create_hash(a, decode=True) for a in artists) + self.artists = [ArtistMinimal(a) for a in artists] + + albumartists = split_artists(self.albumartists) + + if not albumartists: + self.albumartists = self.artists[:1] + else: + self.albumartists = [ArtistMinimal(a) for a in albumartists] + if get_flag(SessionVarKeys.REMOVE_PROD): new_title = remove_prod(new_title) @@ -84,12 +94,6 @@ class Track: if get_flag(SessionVarKeys.MERGE_ALBUM_VERSIONS): self.recreate_albumhash() - self.artist_hashes = "-".join(create_hash(a, decode=True) for a in artists) - self.artists = [ArtistMinimal(a) for a in artists] - - albumartists = split_artists(self.albumartists) - self.albumartists = [ArtistMinimal(a) for a in albumartists] - self.image = self.albumhash + ".webp" if self.genre is not None and self.genre != "": diff --git a/app/store/albums.py b/app/store/albums.py index 0bd6be42..7a3c7a52 100644 --- a/app/store/albums.py +++ b/app/store/albums.py @@ -101,10 +101,11 @@ class AlbumStore: """ Returns an album by its hash. """ - try: - return [a for a in cls.albums if a.albumhash == albumhash][0] - except IndexError: - return None + for album in cls.albums: + if album.albumhash == albumhash: + return album + + return None @classmethod def get_albums_by_hashes(cls, albumhashes: list[str]) -> list[Album]: @@ -132,14 +133,7 @@ class AlbumStore: """ Count albums for the given artisthash. """ - albumartists = [a.albumartists for a in cls.albums] - artisthashes = [] - - for artist in albumartists: - artisthashes.extend([a.artisthash for a in artist]) # type: ignore - - master_string = "-".join(artisthashes) - + master_string = "-".join(a.albumartists_hashes for a in cls.albums) return master_string.count(artisthash) @classmethod diff --git a/app/utils/parsers.py b/app/utils/parsers.py index 5c8facaf..6fb30b2f 100644 --- a/app/utils/parsers.py +++ b/app/utils/parsers.py @@ -1,6 +1,6 @@ import re -from app.enums.album_versions import AlbumVersionEnum +from app.enums.album_versions import AlbumVersionEnum, get_all_keywords from app.settings import SessionVarKeys, get_flag @@ -9,14 +9,13 @@ def split_artists(src: str): Splits a string of artists into a list of artists. """ separators: set = get_flag(SessionVarKeys.ARTIST_SEPARATORS) - # separators = separators.union({","}) - for sep in separators: src = src.replace(sep, ",") artists = src.split(",") + artists = [a.strip() for a in artists] - return [a.strip() for a in artists] + return [a for a in artists if a] def parse_artist_from_filename(title: str): @@ -97,12 +96,12 @@ def parse_feat_from_title(title: str) -> tuple[list[str], str]: return artists, new_title -def get_base_album_title(string) -> tuple[str, str | None]: +def get_base_album_title(string: str) -> tuple[str, str | None]: """ Extracts the base album title from a string. """ pattern = re.compile( - r"\s*(\(|\[)[^\)\]]*?(version|remaster|deluxe|edition|expanded|anniversary)[^\)\]]*?(\)|\])$", + rf"\s*(\(|\[)[^\)\]]*?({get_all_keywords()})[^\)\]]*?(\)|\])$", re.IGNORECASE, ) # TODO: Fix "Redundant character escape '\]' in RegExp " @@ -205,9 +204,3 @@ def clean_title(title: str) -> str: rem_2 = remove_hyphen_remasters(title) return rem_1 if len(rem_2) > len(rem_1) else rem_2 - - # if "[" in title or "(" in title: - # return remove_bracketed_remaster(title) - # - # if "-" in title: - # return remove_hyphen_remasters(title)