diff --git a/app/api/artist.py b/app/api/artist.py index 0a93000..fb46cf3 100644 --- a/app/api/artist.py +++ b/app/api/artist.py @@ -242,7 +242,7 @@ def get_artist_albums(artisthash: str): albums = [a for a in albums if not a.is_single] return albums - albums = filter(lambda a: artisthash in a.albumartisthash, all_albums) + albums = filter(lambda a: artisthash in a.albumartists_hashes, all_albums) albums = list(albums) albums = remove_EPs_and_singles(albums) @@ -250,7 +250,7 @@ def get_artist_albums(artisthash: str): for c in compilations: albums.remove(c) - appearances = filter(lambda a: artisthash not in a.albumartisthash, all_albums) + appearances = filter(lambda a: artisthash not in a.albumartists_hashes, all_albums) appearances = list(appearances) appearances = remove_EPs_and_singles(appearances) diff --git a/app/lib/searchlib.py b/app/lib/searchlib.py index e2374ca..465ab9b 100644 --- a/app/lib/searchlib.py +++ b/app/lib/searchlib.py @@ -93,7 +93,7 @@ class SearchAlbums: Gets all albums with a given title. """ - albums = [unidecode(a.title).lower() for a in self.albums] + albums = [unidecode(a.og_title).lower() for a in self.albums] results = process.extract( self.query, diff --git a/app/models/album.py b/app/models/album.py index 2c5778f..9794639 100644 --- a/app/models/album.py +++ b/app/models/album.py @@ -4,6 +4,9 @@ from dataclasses import dataclass from .track import Track from .artist import Artist from ..utils.hashing import create_hash +from ..utils.parsers import parse_feat_from_title + +from app.settings import FromFlags @dataclass(slots=True) @@ -16,13 +19,14 @@ class Album: title: str albumartists: list[Artist] - albumartisthash: str = "" + albumartists_hashes: str = "" image: str = "" count: int = 0 duration: int = 0 colors: list[str] = dataclasses.field(default_factory=list) date: str = "" + og_title: str = "" is_soundtrack: bool = False is_compilation: bool = False is_single: bool = False @@ -32,8 +36,20 @@ class Album: genres: list[str] = dataclasses.field(default_factory=list) def __post_init__(self): + self.og_title = self.title self.image = self.albumhash + ".webp" - self.albumartisthash = "-".join(a.artisthash for a in self.albumartists) + + if FromFlags.EXTRACT_FEAT: + featured, self.title = parse_feat_from_title(self.title) + + if len(featured) > 0: + original_lower = "-".join([a.name.lower() for a in self.albumartists]) + self.albumartists.extend([Artist(a) for a in featured if a.lower() not in original_lower]) + + from ..store.tracks import TrackStore + TrackStore.append_track_artists(self.albumhash, featured) + + self.albumartists_hashes = "-".join(a.artisthash for a in self.albumartists) def set_colors(self, colors: list[str]): self.colors = colors diff --git a/app/models/track.py b/app/models/track.py index 0d1acf4..c2ac466 100644 --- a/app/models/track.py +++ b/app/models/track.py @@ -31,12 +31,13 @@ class Track: filetype: str = "" image: str = "" - artist_hashes: list[str] = dataclasses.field(default_factory=list) + artist_hashes: str = "" is_favorite: bool = False og_title: str = "" def __post_init__(self): self.og_title = self.title + if self.artist is not None: artists = split_artists(self.artist) new_title = self.title @@ -55,7 +56,7 @@ class Track: self.title = new_title - self.artist_hashes = [create_hash(a, decode=True) for a in artists] + self.artist_hashes = "-".join(create_hash(a, decode=True) for a in artists) self.artist = [ArtistMinimal(a) for a in artists] albumartists = split_artists(self.albumartist) @@ -72,7 +73,21 @@ class Track: self.recreate_hash() def recreate_hash(self): + """ + Recreates a track hash if the track title was altered + to prevent duplicate tracks having different hashes. + """ if self.og_title == self.title: return self.trackhash = create_hash(", ".join([a.name for a in self.artist]), self.album, self.title) + + def recreate_artists_hash(self): + self.artist_hashes = "-".join(a.artisthash for a in self.artist) + + def add_artists(self, artists: list[str]): + for artist in artists: + if create_hash(artist) not in self.artist_hashes: + self.artist.append(ArtistMinimal(artist)) + + self.recreate_artists_hash() diff --git a/app/store/albums.py b/app/store/albums.py index 743a341..d992008 100644 --- a/app/store/albums.py +++ b/app/store/albums.py @@ -71,7 +71,7 @@ class AlbumStore: Returns N albums by the given albumartist, excluding the specified album. """ - albums = [album for album in cls.albums if artisthash in album.albumartisthash] + albums = [album for album in cls.albums if artisthash in album.albumartists_hashes] albums = [album for album in albums if album.albumhash != exclude] @@ -108,7 +108,7 @@ class AlbumStore: """ Returns all albums by the given artist. """ - return [album for album in cls.albums if artisthash in album.albumartisthash] + return [album for album in cls.albums if artisthash in album.albumartists_hashes] @classmethod def count_albums_by_artisthash(cls, artisthash: str): diff --git a/app/store/tracks.py b/app/store/tracks.py index c781e1b..0b3bd6b 100644 --- a/app/store/tracks.py +++ b/app/store/tracks.py @@ -41,18 +41,6 @@ class TrackStore: cls.tracks.extend(tracks) - @classmethod - def get_tracks_by_trackhashes(cls, trackhashes: list[str]) -> list[Track]: - """ - Returns a list of tracks by their hashes. - """ - - trackhashes = " ".join(trackhashes) - tracks = [track for track in cls.tracks if track.trackhash in trackhashes] - - tracks.sort(key=lambda t: trackhashes.index(t.trackhash)) - return tracks - @classmethod def remove_track_by_filepath(cls, filepath: str): """ @@ -109,6 +97,29 @@ class TrackStore: if track.trackhash == trackhash: track.is_favorite = False + @classmethod + def append_track_artists(cls, albumhash: str, artists: list[str]): + tracks = cls.get_tracks_by_albumhash(albumhash) + + for track in tracks: + track.add_artists(artists) + + # ================================================ + # ================== GETTERS ===================== + # ================================================ + + @classmethod + def get_tracks_by_trackhashes(cls, trackhashes: list[str]) -> list[Track]: + """ + Returns a list of tracks by their hashes. + """ + + trackhashes = " ".join(trackhashes) + tracks = [track for track in cls.tracks if track.trackhash in trackhashes] + + tracks.sort(key=lambda t: trackhashes.index(t.trackhash)) + return tracks + @classmethod def get_tracks_by_filepaths(cls, paths: list[str]) -> list[Track]: """