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
This commit is contained in:
mungai-njoroge 2023-09-04 11:01:03 +03:00
parent 5ff3e5d28a
commit 83bbe69550
9 changed files with 93 additions and 54 deletions

View File

@ -3,6 +3,7 @@ Contains all the artist(s) routes.
""" """
import random import random
from collections import deque from collections import deque
from datetime import datetime
from flask import Blueprint, request 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.albums import AlbumStore
from app.store.artists import ArtistStore from app.store.artists import ArtistStore
from app.store.tracks import TrackStore from app.store.tracks import TrackStore
from app.utils.bisection import UseBisection
from app.utils.remove_duplicates import remove_duplicates from app.utils.remove_duplicates import remove_duplicates
api = Blueprint("artist", __name__, url_prefix="/") api = Blueprint("artist", __name__, url_prefix="/")
@ -115,17 +117,12 @@ class ArtistsCache:
""" """
entry = [a for a in cls.artists if a.artisthash == artisthash][0] 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] 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 entry.albums_fetched = True
@classmethod @classmethod
@ -222,15 +219,45 @@ def get_artist(artisthash: str):
if t.genre is not None: if t.genre is not None:
genres = genres.union(t.genre) 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 { return {
"artist": artist, "artist": artist,
"tracks": serialize_tracks(tracks[:limit]), "tracks": serialize_tracks(tracks[:limit]),
"genres": list(genres), "genres": genres,
} }
@api.route("/artist/<artisthash>/albums", methods=["GET"]) @api.route("/artist/<artisthash>/albums", methods=["GET"])
def get_artist_albums(artisthash: str): 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") limit = request.args.get("limit")
if limit is None: if limit is None:

View File

@ -133,12 +133,14 @@ class SettingsSQLMethods:
def load_settings(): def load_settings():
s = SettingsSQLMethods.get_all_settings() s = SettingsSQLMethods.get_all_settings()
# artist separators try:
db_separators: str = s[0] db_separators: str = s[0]
db_separators = db_separators.replace(" ", "") db_separators = db_separators.replace(" ", "")
separators = db_separators.split(",") separators = db_separators.split(",")
separators = set(separators)
except IndexError:
separators = {";", "/"}
separators = set(separators)
SessionVars.ARTIST_SEPARATORS = separators SessionVars.ARTIST_SEPARATORS = separators
# boolean settings # boolean settings

View File

@ -29,6 +29,7 @@ class AlbumVersionEnum(Enum):
ARCHIVE_EDITION = ("archive",) ARCHIVE_EDITION = ("archive",)
Acoustic = ("acoustic",) Acoustic = ("acoustic",)
instrumental = ("instrumental",)
DOUBLE_DISC = ("double disc", "double disk") DOUBLE_DISC = ("double disc", "double disk")
SUMMER_EDITION = ("summer",) SUMMER_EDITION = ("summer",)
@ -56,3 +57,7 @@ class AlbumVersionEnum(Enum):
REISSUE = ("reissue",) REISSUE = ("reissue",)
REMASTERED = ("remaster",) REMASTERED = ("remaster",)
TAYLORS_VERSION = ("taylor's version",) TAYLORS_VERSION = ("taylor's version",)
def get_all_keywords():
return "|".join("|".join(i.value) for i in AlbumVersionEnum)

View File

@ -40,8 +40,12 @@ def extract_thumb(filepath: str, webp_path: str, overwrite=False) -> bool:
ratio = width / height ratio = width / height
img.save(original_img_path, "webp") img.save(original_img_path, "webp")
img.resize((tsize, int(tsize / ratio)), Image.ANTIALIAS).save(lg_img_path, "webp") img.resize((tsize, int(tsize / ratio)), Image.ANTIALIAS).save(
img.resize((sm_tsize, int(sm_tsize / ratio)), Image.ANTIALIAS).save(sm_img_path, "webp") 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): if not overwrite and os.path.exists(sm_img_path):
img_size = os.path.getsize(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.artists = tags.artist
tags.albumartists = tags.albumartist tags.albumartists = tags.albumartist
# sub underscore with space
tags.title = tags.title.replace("_", " ")
tags.album = tags.album.replace("_", " ")
tags = tags.__dict__ tags = tags.__dict__
# delete all tag properties that start with _ (tinytag internals) # delete all tag properties that start with _ (tinytag internals)

View File

@ -173,8 +173,10 @@ class Album:
if ( if (
len(tracks) == 1 len(tracks) == 1
and create_hash(tracks[0].title) and (
== create_hash(self.title) # if they have the same title 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].track == 1
# and tracks[0].disc == 1 # and tracks[0].disc == 1
# TODO: Review -> Are the above commented checks necessary? # TODO: Review -> Are the above commented checks necessary?
@ -191,10 +193,10 @@ class Album:
if self.date: if self.date:
return 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: # if len(dates) == 0:
self.date = 0 # self.date = 0
return # return
self.date = datetime.datetime.fromtimestamp(min(dates)).year self.date = datetime.datetime.fromtimestamp(min(dates)).year

View File

@ -19,6 +19,10 @@ class ArtistMinimal:
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"
# hack to override all the variations from unreleased files (sorry guys!)
if self.artisthash == "5a37d5315e":
self.name = "Juice WRLD"
@dataclass(slots=True) @dataclass(slots=True)
class Artist(ArtistMinimal): class Artist(ArtistMinimal):

View File

@ -64,6 +64,16 @@ class Track:
[a for a in featured if create_hash(a) not in original_lower] [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): if get_flag(SessionVarKeys.REMOVE_PROD):
new_title = remove_prod(new_title) new_title = remove_prod(new_title)
@ -84,12 +94,6 @@ class Track:
if get_flag(SessionVarKeys.MERGE_ALBUM_VERSIONS): if get_flag(SessionVarKeys.MERGE_ALBUM_VERSIONS):
self.recreate_albumhash() 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" self.image = self.albumhash + ".webp"
if self.genre is not None and self.genre != "": if self.genre is not None and self.genre != "":

View File

@ -101,10 +101,11 @@ class AlbumStore:
""" """
Returns an album by its hash. Returns an album by its hash.
""" """
try: for album in cls.albums:
return [a for a in cls.albums if a.albumhash == albumhash][0] if album.albumhash == albumhash:
except IndexError: return album
return None
return None
@classmethod @classmethod
def get_albums_by_hashes(cls, albumhashes: list[str]) -> list[Album]: def get_albums_by_hashes(cls, albumhashes: list[str]) -> list[Album]:
@ -132,14 +133,7 @@ class AlbumStore:
""" """
Count albums for the given artisthash. Count albums for the given artisthash.
""" """
albumartists = [a.albumartists for a in cls.albums] master_string = "-".join(a.albumartists_hashes for a in cls.albums)
artisthashes = []
for artist in albumartists:
artisthashes.extend([a.artisthash for a in artist]) # type: ignore
master_string = "-".join(artisthashes)
return master_string.count(artisthash) return master_string.count(artisthash)
@classmethod @classmethod

View File

@ -1,6 +1,6 @@
import re 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 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. Splits a string of artists into a list of artists.
""" """
separators: set = get_flag(SessionVarKeys.ARTIST_SEPARATORS) separators: set = get_flag(SessionVarKeys.ARTIST_SEPARATORS)
# separators = separators.union({","})
for sep in separators: for sep in separators:
src = src.replace(sep, ",") src = src.replace(sep, ",")
artists = src.split(",") 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): 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 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. Extracts the base album title from a string.
""" """
pattern = re.compile( pattern = re.compile(
r"\s*(\(|\[)[^\)\]]*?(version|remaster|deluxe|edition|expanded|anniversary)[^\)\]]*?(\)|\])$", rf"\s*(\(|\[)[^\)\]]*?({get_all_keywords()})[^\)\]]*?(\)|\])$",
re.IGNORECASE, re.IGNORECASE,
) )
# TODO: Fix "Redundant character escape '\]' in RegExp " # TODO: Fix "Redundant character escape '\]' in RegExp "
@ -205,9 +204,3 @@ def clean_title(title: str) -> str:
rem_2 = remove_hyphen_remasters(title) rem_2 = remove_hyphen_remasters(title)
return rem_1 if len(rem_2) > len(rem_1) else rem_2 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)