Skip to content

Commit

Permalink
feat(Track): add lyrics and subtitles downloading support
Browse files Browse the repository at this point in the history
also adds simple tests
resolves #3
  • Loading branch information
JuniorJPDJ committed May 15, 2021
1 parent 471f8a7 commit 77243e8
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 2 deletions.
34 changes: 33 additions & 1 deletion tests/test_tidal_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# - [ ] Track
# - [x] loading track info
# - [x] downloading tracks
# - [ ] track lyrics (no lyrics support atm)
# - [x] track lyrics
# - [ ] track metadata generation
# - [x] loading album info
# - [x] listing tracks from albums
Expand Down Expand Up @@ -62,6 +62,38 @@ async def test_track_title(sess: TidalSession, id_, artist, title):
assert track.title == title and track.artist_name == artist


@pytest.mark.asyncio
@pytest.mark.parametrize(
"id_, lyrics_len",
(
(22563745, None),
(22563746, 3079),
),
)
async def test_track_lyrics(sess: TidalSession, id_, lyrics_len):
track = await sess.track(id_)
if lyrics_len is None:
assert await track.lyrics() is None
else:
assert len(await track.lyrics()) == lyrics_len


@pytest.mark.asyncio
@pytest.mark.parametrize(
"id_, subtitles_len",
(
(22563745, None),
(22563746, 4193),
),
)
async def test_track_subtitles(sess: TidalSession, id_, subtitles_len):
track = await sess.track(id_)
if subtitles_len is None:
assert await track.subtitles() is None
else:
assert len(await track.subtitles()) == subtitles_len


@pytest.mark.asyncio
@pytest.mark.parametrize(
"id_, artist, title",
Expand Down
45 changes: 44 additions & 1 deletion tidal_async/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from typing import TYPE_CHECKING, AsyncGenerator, Optional, Tuple

import music_service_async_interface as generic
from aiohttp import ClientResponseError

from tidal_async.exceptions import InsufficientAudioQuality
from tidal_async.utils import cacheable, gen_artist, gen_title, id_from_url, snake_to_camel
Expand Down Expand Up @@ -104,10 +105,13 @@ class ArtistType(enum.Enum):
artist = "ARTIST"


# TODO [#3]: Downloading lyrics
class Track(TidalObject, generic.Track):
urlname = "track"

def __init__(self, sess: "TidalSession", dict_, id_field_name="id"):
super().__init__(sess, dict_, id_field_name)
self._lyrics_dict = None

def __repr__(self):
cls = self.__class__
return f"<{cls.__module__}.{cls.__qualname__} ({self.get_id()}): {self.artist_name} - {self.title}>"
Expand Down Expand Up @@ -184,6 +188,45 @@ async def get_file_url(

return manifest["urls"][0]

async def _lyrics(self) -> Optional[dict]:
if self._lyrics_dict is not None:
return self._lyrics_dict

try:
resp = await self.sess.get(
f"/v1/tracks/{self.get_id()}/lyrics", params={"countryCode": self.sess.country_code}
)
except ClientResponseError as e:
if e.status == 404:
return None
else:
raise

self._lyrics_dict = await resp.json()
return self._lyrics_dict

async def lyrics(self) -> Optional[str]:
"""Gets lyrics for track
:return: Lyrics string when available, `None` when not
"""
lyrics_dict = await self._lyrics()
if lyrics_dict is None or "lyrics" not in lyrics_dict:
return None

return lyrics_dict["lyrics"]

async def subtitles(self) -> Optional[str]:
"""Gets subtitles (time-synchronized lyrics) for track
:return: Subtitles string in LRC format when available, `None` when not
"""
lyrics_dict = await self._lyrics()
if lyrics_dict is None or "subtitles" not in lyrics_dict:
return None

return lyrics_dict["subtitles"]

async def get_metadata(self):
# TODO [#22]: Rewrite Track.get_metadata
# - [ ] lyrics
Expand Down

0 comments on commit 77243e8

Please sign in to comment.