2222import logging
2323import requests
2424from collections import namedtuple
25- from .models import Artist , Album , Track , Playlist , SearchResult , Category , Role
25+ from .models import Artist , Album , Track , Video , Playlist , SearchResult , Category , Role
2626try :
2727 from urlparse import urljoin
2828except ImportError :
@@ -39,10 +39,15 @@ class Quality(object):
3939 high = 'HIGH'
4040 low = 'LOW'
4141
42+ class videoQuality (object ):
43+ high = 'HIGH'
44+ medium = 'MEDIUM'
45+ low = 'LOW'
4246
4347class Config (object ):
44- def __init__ (self , quality = Quality .high ):
48+ def __init__ (self , quality = Quality .high , videoQuality = videoQuality . high ):
4549 self .quality = quality
50+ self .videoQuality = videoQuality
4651 self .api_location = 'https://api.tidalhifi.com/v1/'
4752 self .api_token = 'BI218mwp9ERZ3PFI' if self .quality == \
4853 Quality .lossless else '4zx46pyr9o8qZNRw' ,
@@ -112,12 +117,25 @@ def get_playlist(self, playlist_id):
112117 def get_playlist_tracks (self , playlist_id ):
113118 return self ._map_request ('playlists/%s/tracks' % playlist_id , ret = 'tracks' )
114119
120+ def get_playlist_videos (self , playlist_id ):
121+ return self ._map_request ('playlists/%s/items' % playlist_id , ret = 'video' )
122+
123+ def get_playlist_items (self , playlist_id ):
124+ return self ._get_items ('playlists/%s/items' % playlist_id , ret = 'items' )
125+
115126 def get_album (self , album_id ):
116127 return self ._map_request ('albums/%s' % album_id , ret = 'album' )
117128
118129 def get_album_tracks (self , album_id ):
119130 return self ._map_request ('albums/%s/tracks' % album_id , ret = 'tracks' )
120131
132+ def get_album_videos (self , album_id ):
133+ items = self ._get_items ('albums/%s/items' % album_id , ret = 'videos' )
134+ return [item for item in items if isinstance (item , Video )]
135+
136+ def get_album_items (self , album_id ):
137+ return self ._get_items ('albums/%s/items' % album_id , ret = 'items' )
138+
121139 def get_artist (self , artist_id ):
122140 return self ._map_request ('artists/%s' % artist_id , ret = 'artist' )
123141
@@ -135,6 +153,9 @@ def get_artist_albums_other(self, artist_id):
135153 def get_artist_top_tracks (self , artist_id ):
136154 return self ._map_request ('artists/%s/toptracks' % artist_id , ret = 'tracks' )
137155
156+ def get_artist_videos (self , artist_id ):
157+ return self ._map_request ('artists/%s/videos' % artist_id , ret = 'videos' )
158+
138159 def get_artist_bio (self , artist_id ):
139160 return self .request ('GET' , 'artists/%s/bio' % artist_id ).json ()['text' ]
140161
@@ -169,6 +190,9 @@ def get_track_radio(self, track_id):
169190 def get_track (self , track_id ):
170191 return self ._map_request ('tracks/%s' % track_id , ret = 'track' )
171192
193+ def get_video (self , video_id ):
194+ return self ._map_request ('videos/%s' % video_id , ret = 'video' )
195+
172196 def _map_request (self , url , params = None , ret = None ):
173197 json_obj = self .request ('GET' , url , params ).json ()
174198 parse = None
@@ -177,9 +201,13 @@ def _map_request(self, url, params=None, ret=None):
177201 elif ret .startswith ('album' ):
178202 parse = _parse_album
179203 elif ret .startswith ('track' ):
180- parse = _parse_track
204+ parse = _parse_media
181205 elif ret .startswith ('user' ):
182206 raise NotImplementedError ()
207+ elif ret .startswith ('video' ):
208+ parse = _parse_media
209+ elif ret .startswith ('item' ):
210+ parse = _parse_media
183211 elif ret .startswith ('playlist' ):
184212 parse = _parse_playlist
185213
@@ -191,11 +219,31 @@ def _map_request(self, url, params=None, ret=None):
191219 else :
192220 return list (map (parse , items ))
193221
222+ def _get_items (self , url , ret = None ):
223+ remaining = 100
224+ offset = 0
225+ while remaining is 100 :
226+ items = self ._map_request (url , params = {'offset' : offset , 'limit' : 100 }, ret = ret )
227+ remaining = len (items )
228+ return items
229+
194230 def get_media_url (self , track_id ):
195231 params = {'soundQuality' : self ._config .quality }
196232 r = self .request ('GET' , 'tracks/%s/streamUrl' % track_id , params )
197233 return r .json ()['url' ]
198234
235+ def get_track_url (self , track_id ):
236+ self .get_media_url (track_id )
237+
238+ def get_video_url (self , video_id ):
239+ params = {
240+ 'urlusagemode' : 'STREAM' ,
241+ 'videoquality' : self ._config .videoQuality ,
242+ 'assetpresentation' : 'FULL'
243+ }
244+ r = self .request ('GET' , 'videos/%s/urlpostpaywall' % video_id , params )
245+ return r .json ()['urls' ][0 ]
246+
199247 def search (self , field , value ):
200248 params = {
201249 'query' : value ,
@@ -265,11 +313,13 @@ def _parse_playlist(json_obj):
265313 }
266314 return Playlist (** kwargs )
267315
268-
269- def _parse_track (json_obj ):
316+ def _parse_media (json_obj ):
270317 artist = _parse_artist (json_obj ['artist' ])
271318 artists = _parse_artists (json_obj ['artists' ])
272- album = _parse_album (json_obj ['album' ], artist , artists )
319+ album = None
320+ if (json_obj ['album' ]):
321+ album = _parse_album (json_obj ['album' ], artist , artists )
322+
273323 kwargs = {
274324 'id' : json_obj ['id' ],
275325 'name' : json_obj ['title' ],
@@ -281,9 +331,13 @@ def _parse_track(json_obj):
281331 'artists' : artists ,
282332 'album' : album ,
283333 'available' : bool (json_obj ['streamReady' ]),
334+ 'type' : json_obj .get ('type' ),
284335 }
285- return Track (** kwargs )
286336
337+ if kwargs ['type' ] == 'Music Video' :
338+ return Video (** kwargs )
339+ else :
340+ return Track (** kwargs )
287341
288342def _parse_genres (json_obj ):
289343 image = "http://resources.wimpmusic.com/images/%s/460x306.jpg" \
@@ -332,7 +386,7 @@ def playlists(self):
332386
333387 def tracks (self ):
334388 r = self ._session .request ('GET' , self ._base_url + '/tracks' )
335- return [_parse_track (item ['item' ]) for item in r .json ()['items' ]]
389+ return [_parse_media (item ['item' ]) for item in r .json ()['items' ]]
336390
337391
338392class User (object ):
0 commit comments