Skip to content

Commit 4c9baf9

Browse files
authored
Added support for Tidal lyrics and fixed parsing error when preferred quality too high (#736)
* fixed json error when preferred quality not found for track * added tidal lyrics support * improve mp3 lyrics support * handle error when there are no lyrics (404) * change error to warning
1 parent 1aad9f0 commit 4c9baf9

File tree

3 files changed

+22
-2
lines changed

3 files changed

+22
-2
lines changed

streamrip/client/tidal.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import logging
55
import re
66
import time
7+
from json import JSONDecodeError
78

89
import aiohttp
910

@@ -101,6 +102,17 @@ async def get_metadata(self, item_id: str, media_type: str) -> dict:
101102

102103
item["albums"] = album_resp["items"]
103104
item["albums"].extend(ep_resp["items"])
105+
elif media_type == "track":
106+
try:
107+
resp = await self._api_request(f"tracks/{str(item_id)}/lyrics", base="https://listen.tidal.com/v1")
108+
109+
# Use unsynced lyrics for MP3, synced for others (FLAC, OPUS, etc)
110+
if self.global_config.session.conversion.enabled and self.global_config.session.conversion.codec.upper() == "MP3":
111+
item["lyrics"] = resp.get("lyrics") or ''
112+
else:
113+
item["lyrics"] = resp.get("subtitles") or resp.get("lyrics") or ''
114+
except TypeError as e:
115+
logger.warning(f"Failed to get lyrics for {item_id}: {e}")
104116

105117
logger.debug(item)
106118
return item
@@ -140,6 +152,9 @@ async def get_downloadable(self, track_id: str, quality: int):
140152
manifest = json.loads(base64.b64decode(resp["manifest"]).decode("utf-8"))
141153
except KeyError:
142154
raise Exception(resp["userMessage"])
155+
except JSONDecodeError:
156+
logger.warning(f"Failed to get manifest for {track_id}. Retrying with lower quality.")
157+
return await self.get_downloadable(track_id, quality - 1)
143158

144159
logger.debug(manifest)
145160
enc_key = manifest.get("keyId")
@@ -306,7 +321,7 @@ async def _api_post(self, url, data, auth: aiohttp.BasicAuth | None = None) -> d
306321
async with self.session.post(url, data=data, auth=auth) as resp:
307322
return await resp.json()
308323

309-
async def _api_request(self, path: str, params=None) -> dict:
324+
async def _api_request(self, path: str, params=None, base: str = BASE) -> dict:
310325
"""Handle Tidal API requests.
311326
312327
:param path:
@@ -321,7 +336,7 @@ async def _api_request(self, path: str, params=None) -> dict:
321336
params["limit"] = 100
322337

323338
async with self.rate_limiter:
324-
async with self.session.get(f"{BASE}/{path}", params=params) as resp:
339+
async with self.session.get(f"{base}/{path}", params=params) as resp:
325340
if resp.status == 404:
326341
logger.warning("TIDAL: track not found", resp)
327342
raise NonStreamableError("TIDAL: Track not found")

streamrip/metadata/tagger.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ def _attr_from_meta(self, meta: TrackMetadata, attr: str) -> str | None:
183183
"discnumber",
184184
"composer",
185185
"isrc",
186+
"lyrics",
186187
}
187188
if attr in in_trackmetadata:
188189
if attr == "album":

streamrip/metadata/track.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class TrackMetadata:
3232
discnumber: int
3333
composer: str | None
3434
isrc: str | None = None
35+
lyrics: str | None = ""
3536

3637
@classmethod
3738
def from_qobuz(cls, album: AlbumMetadata, resp: dict) -> TrackMetadata | None:
@@ -170,6 +171,8 @@ def from_tidal(cls, album: AlbumMetadata, track) -> TrackMetadata:
170171
else:
171172
artist = track["artist"]["name"]
172173

174+
lyrics = track.get("lyrics", "")
175+
173176
quality_map: dict[str, int] = {
174177
"LOW": 0,
175178
"HIGH": 1,
@@ -209,6 +212,7 @@ def from_tidal(cls, album: AlbumMetadata, track) -> TrackMetadata:
209212
discnumber=discnumber,
210213
composer=None,
211214
isrc=isrc,
215+
lyrics=lyrics
212216
)
213217

214218
@classmethod

0 commit comments

Comments
 (0)