From 043ce2bdf655f0a89a8e8639a6df127be55f3fbf Mon Sep 17 00:00:00 2001 From: Mandy Schoep <2277717+OhMyMndy@users.noreply.github.com> Date: Fri, 21 Mar 2025 15:10:40 +0100 Subject: [PATCH 1/5] feat: get alternatives --- streamrip/client/deezer.py | 47 +++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/streamrip/client/deezer.py b/streamrip/client/deezer.py index d2642d96..6f3d0ebc 100644 --- a/streamrip/client/deezer.py +++ b/streamrip/client/deezer.py @@ -82,7 +82,7 @@ async def get_track(self, item_id: str) -> dict: logger.error(f"Error fetching album of track {item_id}: {e}") return item - album_metadata["tracks"] = album_tracks["data"] + album_metadata["tracks"] = await self.get_alternatives(album_tracks["data"]) album_metadata["track_total"] = len(album_tracks["data"]) item["album"] = album_metadata @@ -93,16 +93,57 @@ async def get_album(self, item_id: str) -> dict: asyncio.to_thread(self.client.api.get_album, item_id), asyncio.to_thread(self.client.api.get_album_tracks, item_id), ) - album_metadata["tracks"] = album_tracks["data"] + album_metadata["tracks"] = await self.get_alternatives(album_tracks["data"]) album_metadata["track_total"] = len(album_tracks["data"]) return album_metadata + + async def get_alternative(self, track): + item_fallbacks = await asyncio.to_thread( + self.client.gw.get_track_with_fallback, track["id"] + ) + if "FALLBACK" in item_fallbacks: + track["id"] = item_fallbacks["FALLBACK"]["SNG_ID"] + track["readable"] = ( + item_fallbacks["FALLBACK"]["STATUS"] == 1 + ) + else: + if "ALBUM_FALLBACK" in item_fallbacks: + for album_fallback in item_fallbacks['ALBUM_FALLBACK']['data']: + if "RIGHTS" in album_fallback: + if len(album_fallback["RIGHTS"]) > 0: + for alternative_track in self.client.gw.get_album_tracks(album_fallback["ALB_ID"]): + if alternative_track['SNG_TITLE'].lower() == track['title'].lower(): + track["id"] = alternative_track["SNG_ID"] + track["readable"] = ( + alternative_track["STATUS"] == 1 + ) + break + + async def get_alternatives(self, tracks): + for track in tracks: + if not track["readable"]: + try: + retries = 0 + while True: + retries += 1 + if retries > 4: + break + await self.get_alternative(track) + except Exception as e: + logger.error( + f"Error while getting fallbacks for track {track['id']}: {e}" + ) + return tracks + async def get_playlist(self, item_id: str) -> dict: pl_metadata, pl_tracks = await asyncio.gather( asyncio.to_thread(self.client.api.get_playlist, item_id), asyncio.to_thread(self.client.api.get_playlist_tracks, item_id), ) - pl_metadata["tracks"] = pl_tracks["data"] + # pl_metadata["tracks"] = pl_tracks["data"] + + pl_metadata["tracks"] = await self.get_alternatives(pl_tracks["data"]) pl_metadata["track_total"] = len(pl_tracks["data"]) return pl_metadata From 18a5ce0a3386e887a5f72bcdec95a4a7da391667 Mon Sep 17 00:00:00 2001 From: Mandy Schoep <2277717+OhMyMndy@users.noreply.github.com> Date: Fri, 21 Mar 2025 15:10:40 +0100 Subject: [PATCH 2/5] feat: get alternatives --- streamrip/client/deezer.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/streamrip/client/deezer.py b/streamrip/client/deezer.py index 6f3d0ebc..a4245ef2 100644 --- a/streamrip/client/deezer.py +++ b/streamrip/client/deezer.py @@ -141,8 +141,6 @@ async def get_playlist(self, item_id: str) -> dict: asyncio.to_thread(self.client.api.get_playlist, item_id), asyncio.to_thread(self.client.api.get_playlist_tracks, item_id), ) - # pl_metadata["tracks"] = pl_tracks["data"] - pl_metadata["tracks"] = await self.get_alternatives(pl_tracks["data"]) pl_metadata["track_total"] = len(pl_tracks["data"]) return pl_metadata From 2539d182093b15677f0cadc0935a5d68b24e2cd8 Mon Sep 17 00:00:00 2001 From: Mandy Schoep <2277717+OhMyMndy@users.noreply.github.com> Date: Fri, 21 Mar 2025 16:31:36 +0100 Subject: [PATCH 3/5] feat: when album doesn't exist anymore, find track on a different album --- streamrip/client/deezer.py | 98 +++++++++++++++++++++++++++----------- streamrip/media/track.py | 1 + 2 files changed, 71 insertions(+), 28 deletions(-) diff --git a/streamrip/client/deezer.py b/streamrip/client/deezer.py index a4245ef2..d1e04059 100644 --- a/streamrip/client/deezer.py +++ b/streamrip/client/deezer.py @@ -72,28 +72,55 @@ async def get_track(self, item_id: str) -> dict: except Exception as e: raise NonStreamableError(e) - album_id = item["album"]["id"] try: + album_id = item["album"]["id"] album_metadata, album_tracks = await asyncio.gather( asyncio.to_thread(self.client.api.get_album, album_id), asyncio.to_thread(self.client.api.get_album_tracks, album_id), ) + # album_metadata["tracks"] = await self.get_alternative_tracks(album_tracks["data"]) + album_metadata["tracks"] = album_tracks["data"] + album_metadata["track_total"] = len(album_tracks["data"]) + item["album"] = album_metadata except Exception as e: - logger.error(f"Error fetching album of track {item_id}: {e}") - return item + item = await self.find_alternative_track(item) + album_id = item["album"]["id"] + try: + album_metadata, album_tracks = await asyncio.gather( + asyncio.to_thread(self.client.api.get_album, album_id), + asyncio.to_thread(self.client.api.get_album_tracks, album_id), + ) + album_metadata["tracks"] = album_tracks["data"] + album_metadata["track_total"] = len(album_tracks["data"]) + item["album"] = album_metadata + except Exception as e: + logger.error(f"Error fetching album of track {item_id}: {e}") + + + - album_metadata["tracks"] = await self.get_alternatives(album_tracks["data"]) - album_metadata["track_total"] = len(album_tracks["data"]) - item["album"] = album_metadata return item + async def find_alternative_track(self, track): + result = self.client.gw.search(f"{track['title']} - {track['artist']['name']}") + + for found_track in result['TRACK']['data']: + if (found_track["SNG_TITLE"].lower() == track['title'].lower() and + found_track["ARTISTS"][0]["ART_NAME"].lower() == track['artist']['name'].lower()): + track['id'] = found_track["SNG_ID"] + + track['album']["id"] = found_track["ALB_ID"] + + return track + + async def get_album(self, item_id: str) -> dict: album_metadata, album_tracks = await asyncio.gather( asyncio.to_thread(self.client.api.get_album, item_id), asyncio.to_thread(self.client.api.get_album_tracks, item_id), ) - album_metadata["tracks"] = await self.get_alternatives(album_tracks["data"]) + album_metadata["tracks"] = await self.get_alternative_tracks(album_tracks["data"]) album_metadata["track_total"] = len(album_tracks["data"]) return album_metadata @@ -102,25 +129,39 @@ async def get_alternative(self, track): item_fallbacks = await asyncio.to_thread( self.client.gw.get_track_with_fallback, track["id"] ) - if "FALLBACK" in item_fallbacks: - track["id"] = item_fallbacks["FALLBACK"]["SNG_ID"] - track["readable"] = ( - item_fallbacks["FALLBACK"]["STATUS"] == 1 - ) - else: - if "ALBUM_FALLBACK" in item_fallbacks: - for album_fallback in item_fallbacks['ALBUM_FALLBACK']['data']: - if "RIGHTS" in album_fallback: - if len(album_fallback["RIGHTS"]) > 0: - for alternative_track in self.client.gw.get_album_tracks(album_fallback["ALB_ID"]): - if alternative_track['SNG_TITLE'].lower() == track['title'].lower(): - track["id"] = alternative_track["SNG_ID"] - track["readable"] = ( - alternative_track["STATUS"] == 1 - ) - break - - async def get_alternatives(self, tracks): + + + if "ALBUM_FALLBACK" in item_fallbacks: + for album_fallback in item_fallbacks['ALBUM_FALLBACK']['data']: + if "RIGHTS" in album_fallback and len(album_fallback["RIGHTS"]) > 0: + for alternative_track in self.client.gw.get_album_tracks(album_fallback["ALB_ID"]): + if alternative_track['SNG_TITLE'].lower() == track['title'].lower(): + track["id"] = alternative_track["SNG_ID"] + track["album"]["id"] = alternative_track["ALB_ID"] + + track["readable"] = len(alternative_track["RIGHTS"]) > 0 + return track + + + while True: + if "FALLBACK" in item_fallbacks: + item_fallbacks = item_fallbacks['FALLBACK'] + track["id"] = item_fallbacks["SNG_ID"] + track["album"]["id"] = item_fallbacks["ALB_ID"] + track["readable"] = len(item_fallbacks["RIGHTS"]) > 0 + else: + break + + + + if not track['readable']: + raise NonStreamableError(f"Track {track} not readable") + + return track + + + + async def get_alternative_tracks(self, tracks): for track in tracks: if not track["readable"]: try: @@ -129,7 +170,8 @@ async def get_alternatives(self, tracks): retries += 1 if retries > 4: break - await self.get_alternative(track) + # todo: add it to tracks[index] instead + track = await self.get_alternative(track) except Exception as e: logger.error( f"Error while getting fallbacks for track {track['id']}: {e}" @@ -141,7 +183,7 @@ async def get_playlist(self, item_id: str) -> dict: asyncio.to_thread(self.client.api.get_playlist, item_id), asyncio.to_thread(self.client.api.get_playlist_tracks, item_id), ) - pl_metadata["tracks"] = await self.get_alternatives(pl_tracks["data"]) + pl_metadata["tracks"] = await self.get_alternative_tracks(pl_tracks["data"]) pl_metadata["track_total"] = len(pl_tracks["data"]) return pl_metadata diff --git a/streamrip/media/track.py b/streamrip/media/track.py index b09cfa13..13c78441 100644 --- a/streamrip/media/track.py +++ b/streamrip/media/track.py @@ -65,6 +65,7 @@ async def download(self): try: await self.downloadable.download(self.download_path, callback) except Exception as e: + # TODO: vremya logger.error( f"Persistent error downloading track '{self.meta.title}', skipping: {e}" ) From b8db6a62c34a61b82df755029374f6be50bdbb8c Mon Sep 17 00:00:00 2001 From: Mandy Schoep <2277717+OhMyMndy@users.noreply.github.com> Date: Fri, 21 Mar 2025 16:44:40 +0100 Subject: [PATCH 4/5] fix: if there is no album somehow, create an empty dict. --- streamrip/client/deezer.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/streamrip/client/deezer.py b/streamrip/client/deezer.py index d1e04059..7db4be90 100644 --- a/streamrip/client/deezer.py +++ b/streamrip/client/deezer.py @@ -130,7 +130,8 @@ async def get_alternative(self, track): self.client.gw.get_track_with_fallback, track["id"] ) - + if not "album" in track: + track["album"] = {} if "ALBUM_FALLBACK" in item_fallbacks: for album_fallback in item_fallbacks['ALBUM_FALLBACK']['data']: if "RIGHTS" in album_fallback and len(album_fallback["RIGHTS"]) > 0: From bb935f38ee40bf7528079780c09d7a1033cc4032 Mon Sep 17 00:00:00 2001 From: Mandy Schoep <2277717+OhMyMndy@users.noreply.github.com> Date: Fri, 21 Mar 2025 16:47:40 +0100 Subject: [PATCH 5/5] fix: return first alternative matching track, instead of last --- streamrip/client/deezer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/streamrip/client/deezer.py b/streamrip/client/deezer.py index 7db4be90..388c2257 100644 --- a/streamrip/client/deezer.py +++ b/streamrip/client/deezer.py @@ -111,6 +111,7 @@ async def find_alternative_track(self, track): track['id'] = found_track["SNG_ID"] track['album']["id"] = found_track["ALB_ID"] + return track return track