# -*- coding: utf-8 -*-
# Copyright (C) 2019-2022 morguldir
# Copyright (C) 2014 Thomas Amland
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
A module containing information and functions related to TIDAL artists.
"""
import copy
from enum import Enum
import dateutil.parser
IMG_URL = "http://images.osl.wimpmusic.com/im/im?w={width}&h={height}&{id_type}={id}"
[docs]class Artist(object):
id = None
name = None
roles = None
role = None
picture = None
user_date_added = None
def __init__(self, session, artist_id):
self.session = session
self.request = self.session.request
self.id = artist_id
if artist_id:
self.request.map_request('artists/%s' % artist_id, parse=self.parse_artist)
[docs] def parse_artist(self, json_obj):
"""
:param json_obj:
:return:
"""
self.id = json_obj['id']
self.name = json_obj['name']
# Artists do not have roles as playlist creators.
self.roles = None
self.role = None
if json_obj.get('type') or json_obj.get('artistTypes'):
roles = []
for role in json_obj.get('artistTypes', [json_obj.get('type')]):
roles.append(Role(role))
self.roles = roles
self.role = roles[0]
self.picture = json_obj.get('picture')
user_date_added = json_obj.get('dateAdded')
self.user_date_added = dateutil.parser.isoparse(user_date_added) if user_date_added else None
return copy.copy(self)
[docs] def parse_artists(self, json_obj):
"""
Parses a TIDAL artist, replaces the current artist object.
Made for use inside of the python tidalapi module.
:param json_obj: Json data returned from api.tidal.com containing an artist
:return: Returns a copy of the original :exc:'Artist': object
"""
return list(map(self.parse_artist, json_obj))
def _get_albums(self, params=None):
return self.request.map_request('artists/%s/albums' % self.id, params, parse=self.session.parse_album)
[docs] def get_albums(self, limit=None, offset=0):
"""
Queries TIDAL for the artists albums.
:return: A list of :class:`Albums<tidalapi.album.Album>`
"""
params = {'limit': limit, 'offset': offset}
return self._get_albums(params)
[docs] def get_albums_ep_singles(self, limit=None, offset=0):
"""
Queries TIDAL for the artists extended plays and singles.
:return: A list of :class:`Albums <tidalapi.album.Album>`
"""
params = {'filter': 'EPSANDSINGLES', 'limit': limit, 'offset': offset}
return self._get_albums(params)
[docs] def get_albums_other(self, limit=None, offset=0):
"""
Queries TIDAL for albums the artist has appeared on as a featured artist.
:return: A list of :class:`Albums <tidalapi.album.Album>`
"""
params = {'filter': 'COMPILATIONS', 'limit': limit, 'offset': offset}
return self._get_albums(params)
[docs] def get_top_tracks(self, limit=None, offset=0):
"""
Queries TIDAL for the artists tracks, sorted by popularity.
:return: A list of :class:`Tracks <tidalapi.media.Track>`
"""
params = {'limit': limit, 'offset': offset}
return self.request.map_request('artists/%s/toptracks' % self.id, params=params, parse=self.session.parse_track)
[docs] def get_videos(self, limit=None, offset=0):
"""
Queries tidal for the artists videos.
:return: A list of :class:`Videos <tidalapi.media.Video>`
"""
params = {'limit': limit, 'offset': offset}
return self.request.map_request('artists/%s/videos' % self.id, params=params, parse=self.session.parse_video)
[docs] def get_bio(self):
"""
Queries TIDAL for the artists biography
:return: A string containing the bio, as well as identifiers to other TIDAL objects inside the bio.
"""
# morguldir: TODO: Add parsing of wimplinks?
return self.request.request('GET', 'artists/%s/bio' % self.id).json()['text']
[docs] def get_similar(self):
"""
Queries TIDAL for similar artists
:return: A list of :class:`Artists <tidalapi.artist.Artist>`
"""
return self.request.map_request('artists/%s/similar' % self.id, parse=self.parse_artist)
[docs] def get_radio(self):
"""
Queries TIDAL for the artist radio, which is a mix of tracks that are similar to what the artist makes.
:return: A list of :class:`Tracks <tidalapi.media.Track>`
"""
params = {'limit': 100}
return self.request.map_request('artists/%s/radio' % self.id, params=params, parse=self.session.parse_track)
[docs] def image(self, dimensions):
"""
A url to an artist picture
:param dimensions: The width and height that you want from the image
:type dimensions: int
:return: A url to the image.
Valid resolutions: 160x160, 320x320, 480x480, 750x750
"""
if dimensions not in [160, 320, 480, 750]:
raise ValueError("Invalid resolution {0} x {0}".format(dimensions))
if not self.picture:
json = self.request.request('get', 'artists/%s' % self.id).json()
self.picture = json.get('picture')
return self.session.config.image_url % (self.picture.replace('-', '/'), dimensions, dimensions)
[docs] def page(self):
"""
Retrieve the artist page as seen on https://listen.tidal.com/artist/$id
:return: A :class:`.Page` containing all the categories from the page, e.g. tracks, influencers and credits
"""
return self.session.page.get("pages/artist", params={"artistId": self.id})
class Role(Enum):
"""
An Enum with different roles an artist can have.
"""
main = 'MAIN'
featured = 'FEATURED'
contributor = 'CONTRIBUTOR'
artist = 'ARTIST'