Skip to content

Add Track.get_radio_mix and Artist.get_radio_mix to get radio as Mix #330

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions tests/test_artist.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import tidalapi
from tidalapi.exceptions import ObjectNotFound
from tidalapi.mix import MixType

from .cover import verify_image_cover

Expand Down Expand Up @@ -140,6 +141,15 @@ def test_get_radio(session):
assert radio[0].artist.name == artist.name


def test_get_radio_mix(session):
artist = session.artist(3514310)
radio = artist.get_radio_mix()
assert radio.id == "000038b3b74d5ce3a17b43a36d62bb"
assert radio.title == "The Turtles"
assert radio.sub_title == "Artist Radio"
assert radio.mix_type == MixType.artist


def test_artist_image(session):
artist = session.artist(4822757)
verify_image_cover(session, artist, [160, 320, 480, 750])
Expand Down
10 changes: 10 additions & 0 deletions tests/test_media.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
MimeType,
Quality,
)
from tidalapi.mix import MixType

from .cover import verify_image_resolution, verify_video_resolution

Expand Down Expand Up @@ -448,6 +449,15 @@ def test_get_track_radio_limit_100(session):
assert len(similar_tracks) == 100


def test_get_radio_mix(session):
track = session.track(12445712)
radio = track.get_radio_mix()
assert radio.id == "001c2cbc32b5b7c17f8c0aa55d9541"
assert radio.title == "Happy Together"
assert radio.sub_title == "The Turtles"
assert radio.mix_type == MixType.track


def test_get_stream_bts(session):
track = session.track(77646170) # Beck: Sea Change, Track: The Golden Age
# Set session as BTS type (i.e. low_320k/HIGH Quality)
Expand Down
52 changes: 39 additions & 13 deletions tidalapi/artist.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@
import dateutil.parser
from typing_extensions import NoReturn

from tidalapi.exceptions import ObjectNotFound, TooManyRequests
from tidalapi.exceptions import MetadataNotAvailable, ObjectNotFound, TooManyRequests
from tidalapi.types import JsonObj

from . import mix

if TYPE_CHECKING:
from tidalapi.album import Album
from tidalapi.media import Track, Video
Expand Down Expand Up @@ -226,21 +228,45 @@ def get_similar(self) -> List["Artist"]:
),
)

def get_radio(self) -> List["Track"]:
"""Queries TIDAL for the artist radio, which is a mix of tracks that are similar
to what the artist makes.
def get_radio(self, limit: int = 100) -> List["Track"]:
"""Queries TIDAL for the artist radio, i.e. a list of tracks similar to this
artist.

:return: A list of :class:`Tracks <tidalapi.media.Track>`
"""
params = {"limit": 100}
return cast(
List["Track"],
self.request.map_request(
f"artists/{self.id}/radio",
params=params,
parse=self.session.parse_track,
),
)
params = {"limit": limit}

try:
request = self.request.request(
"GET", "artists/%s/radio" % self.id, params=params
)
except ObjectNotFound:
raise MetadataNotAvailable("Track radio not available for this track")
except TooManyRequests:
raise TooManyRequests("Track radio unavailable")
else:
json_obj = request.json()
radio = self.request.map_json(json_obj, parse=self.session.parse_track)
assert isinstance(radio, list)
return cast(List["Track"], radio)

def get_radio_mix(self) -> mix.Mix:
"""Queries TIDAL for the artist radio, i.e. mix of tracks that are similar to
this artist.

:return: A :class:`Mix <tidalapi.mix.Mix>`
:raises: A :class:`exceptions.MetadataNotAvailable` if no track radio mix is available
"""

try:
request = self.request.request("GET", "artists/%s/mix" % self.id)
except ObjectNotFound:
raise MetadataNotAvailable("Artist radio not available for this artist")
except TooManyRequests:
raise TooManyRequests("Artist radio unavailable")
else:
json_obj = request.json()
return self.session.mix(json_obj.get("id"))

def items(self) -> List[NoReturn]:
"""The artist page does not supply any items. This only exists for symmetry with
Expand Down
27 changes: 23 additions & 4 deletions tidalapi/media.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@
)
from tidalapi.types import JsonObj

from . import mix


class Quality(str, Enum):
low_96k: str = "LOW"
Expand Down Expand Up @@ -385,11 +387,11 @@ def lyrics(self) -> "Lyrics":
return cast("Lyrics", lyrics)

def get_track_radio(self, limit: int = 100) -> List["Track"]:
"""Queries TIDAL for the track radio, which is a mix of tracks that are similar
to this track.
"""Queries TIDAL for the track radio mix as a list of tracks similar to this
track.

:return: A list of :class:`Tracks <tidalapi.media.Track>`
:raises: A :class:`exceptions.MetadataNotAvailable` if no track radio is available
:raises: A :class:`exceptions.MetadataNotAvailable` if no track radio mix is available
"""
params = {"limit": limit}

Expand All @@ -400,13 +402,30 @@ def get_track_radio(self, limit: int = 100) -> List["Track"]:
except ObjectNotFound:
raise MetadataNotAvailable("Track radio not available for this track")
except TooManyRequests:
raise TooManyRequests("Track radio unavailable)")
raise TooManyRequests("Track radio unavailable")
else:
json_obj = request.json()
tracks = self.requests.map_json(json_obj, parse=self.session.parse_track)
assert isinstance(tracks, list)
return cast(List["Track"], tracks)

def get_radio_mix(self) -> mix.Mix:
"""Queries TIDAL for the track radio mix of tracks that are similar to this
track.

:return: A :class:`Mix <tidalapi.mix.Mix>`
:raises: A :class:`exceptions.MetadataNotAvailable` if no track radio mix is available
"""
try:
request = self.requests.request("GET", "tracks/%s/mix" % self.id)
except ObjectNotFound:
raise MetadataNotAvailable("Track radio not available for this track")
except TooManyRequests:
raise TooManyRequests("Track radio unavailable")
else:
json_obj = request.json()
return self.session.mix(json_obj.get("id"))

def get_stream(self) -> "Stream":
"""Retrieves the track streaming object, allowing for audio transmission.

Expand Down