apis.audio
1_VERSION = 1.4 # Fixes favorite track selection 2 3try 4 import utilities 5 utilities.modify_system_path() 6except 7 pass 8import os 9import requests 10import base64 11import time 12import json 13import random 14import pickle 15from urllib.parse import quote 16 17try 18 from apis import secret_tokens 19 LASTFM_KEY = secret_tokens.LASTFM_KEY 20except 21 print("LAST.FM not available. See Prof Bain or post on edSTEM.") 22 23SPOTIFY_JSON = "spotify_token.json" 24SPOTIFY_CACHE = "spotify_cache.pkl" 25__docformat__ = "google" 26 27__all__ = [ 28 "get_genres", 29 "search_for_artists", 30 "search_for_tracks", 31 "search_for_playlists", 32 "search_for_albums", 33 "get_top_tracks_by_artist", 34 "get_playlist_tracks", 35 "get_playlists_by_user", 36 "get_albums_by_artist", 37 "get_new_releases", 38 "generate_tracks_table", 39 "generate_artists_table", 40 "generate_albums_table", 41 "generate_mixtape" 42] 43 44 45def get_genres(debug = True): 46 """ 47 Asks Spotify for a list of available genres. There are actually 6000+ valid 48 genres on Spotify [all of which can be found here](https://everynoise.com/everynoise1d.html) 49 but for our purposes it might be wise to limit yourself to just these. 50 51 Args: 52 debug (`bool`): whether or not you want the debug messages to be printed. 53 54 Returns: 55 a `list` of `str` representing valid genres. 56 """ 57 return [ 58 "alternative", "ambient", "blues", 59 "chill", "country", "dance", "electronic", "folk", 60 "funk", "happy", "hip-hop", "indie-pop", "jazz", "k-pop", "metal", 61 "new-release", "pop", "punk", "reggae", "rock", 62 "soul", "study", "trance", "work-out", "world-music" 63 ] 64 65 66def search_for_tracks(search_term , debug = True, simplify = True): 67 """ 68 Retrieves a list of Spotify tracks, given the search term passed in. 69 70 Args: 71 search_term (`str`): A search term (for a song), represented as a string. 72 debug (`bool`): Whether or not you want debug text to be printed. 73 simplify (`bool`): Indicates whether you want to simplify the data that is returned. 74 75 Returns: 76 a `list` of tracks. 77 """ 78 url = "https://api.spotify.com/v1/search?q=" + search_term + "&type=track" 79 data = _issue_get_request(url, debug=debug) 80 if not simplify 81 return data 82 return _simplify_tracks(data["tracks"]["items"], debug=debug) 83 84def _search_by_genres(genres , debug = True, simplify=True): 85 """ 86 Retrieves a list of Spotify tracks, given some genre. 87 88 Args: 89 genres (`list`): A list of valid spotify genres 90 debug (`bool`): Whether or not you want debug text to be printed. 91 simplify (`bool`): Indicates whether you want to simplify the data that is returned. 92 93 Returns: 94 a `list` of tracks. 95 """ 96 url = f"https://api.spotify.com/v1/search?q=genre%3A{",".join(genres)}&type=track" 97 data = _issue_get_request(url, debug=debug) 98 if not simplify 99 return data 100 return _simplify_tracks(data["tracks"]["items"], debug=debug) 101 102 103def get_top_tracks_by_artist(artist_id , debug = True, simplify = True): 104 """ 105 Retrieves a list of Spotify "top tracks" by an artist 106 107 Args:a 108 artist_id (`str`): The Spotify id of the artist. 109 debug (`bool`): Whether or not you want debug text to be printed. 110 simplify (`bool`): Indicates whether you want to simplify the data that is returned. 111 112 Returns: 113 a `list` of tracks. 114 """ 115 if len(artist_id) != 22 116 raise TypeError(f"This function expects an Artist ID but you gave it {artist_id}.") 117 118 url = "https://api.spotify.com/v1/artists/" + \ 119 artist_id + "/top-tracks?market=US" 120 data = _issue_get_request(url, debug=debug) 121 if not simplify 122 return data 123 return _simplify_tracks(data["tracks"], debug=debug) 124 125 126def _get_top_tracks_by_artist(artist_id , debug = True, simplify = True): 127 """ 128 Only exists to allow for mock testing of underlying 129 """ 130 return get_top_tracks_by_artist(artist_id, debug, simplify) 131 132 133def get_playlist_tracks(playlist_id , debug = True, simplify = True): 134 """ 135 Retrieves a list of the tracks associated with a playlist_id. 136 137 Args: 138 playlist_id (`str`): The id of the Spotify playlist. 139 debug (`bool`): Whether or not you want debug text to be printed. 140 simplify (`bool`): Whether you want to simplify the data that is returned. 141 142 Returns: 143 a `list` of tracks. 144 """ 145 url = "https://api.spotify.com/v1/playlists/" + playlist_id + "/tracks" 146 data = _issue_get_request(url, debug=debug) 147 if not simplify 148 return data 149 150 def get_track(item): 151 return item["track"] 152 tracks = (map(get_track, data["items"])) 153 return _simplify_tracks(tracks, debug=debug) 154 155 156def search_for_artists(search_term , debug = True, simplify = True): 157 """ 158 Retrieves a list of Spotify artists, given the search term passed in. 159 160 Args: 161 search_term (`str`): A search term (for an artist), represented as a string. 162 debug (`bool`): Whether or not you want debug text to be printed. 163 simplify (`bool`): Indicates whether you want to simplify the data that is returned. 164 165 Returns: 166 a `list` of artists. 167 """ 168 url = "https://api.spotify.com/v1/search?q=" + search_term + "&type=artist&market=US" 169 data = _issue_get_request(url, debug=debug) 170 if not simplify 171 return data 172 return _simplify_artists(data["artists"]["items"], debug=debug) 173 174 175def search_for_albums(search_term , debug = True, simplify = True): 176 """ 177 Retrieves a list of Spotify albums, given the search term passed in. 178 179 Args: 180 search_term (`str`): A search term (for an album), represented as a string. 181 debug (`bool`): Whether or not you want debug text to be printed. 182 simplify (`bool`): Indicates whether you want to simplify the data that is returned. 183 184 Returns: 185 * a `list` of albums. 186 """ 187 url = "https://api.spotify.com/v1/search?q=" + search_term + "&type=album&market=US" 188 data = _issue_get_request(url, debug=debug) 189 if not simplify 190 return data 191 return _simplify_albums(data["albums"]["items"], debug=debug) 192 193 194def search_for_playlists(search_term , debug = True, simplify = True): 195 """ 196 Retrieves a list of Spotify playlists, given the search term passed in. 197 198 Args: 199 search_term (`str`): A search term (for a song), represented as a string. 200 debug (`bool`): Whether or not you want debug text to be printed. 201 simplify (`bool`): Indicates whether you want to simplify the data that is returned. 202 203 Returns: 204 a `list` of playlists. 205 """ 206 url = "https://api.spotify.com/v1/search?q=" + search_term + "&type=playlist" 207 data = _issue_get_request(url, debug=debug) 208 if not simplify 209 return data 210 return _simplify_playlists(data["playlists"]["items"], debug=debug) 211 212 213def get_new_releases(debug = True, simplify = True): 214 """ 215 Retrieves a list of Spotify albums that are newly released. 216 217 Args: 218 debug (`bool`): Whether or not you want debug text to be printed. 219 simplify (`bool`): Indicates whether you want to simplify the data that is returned. 220 221 Returns: 222 * a `list` of albums. 223 """ 224 225 url = f"https://api.spotify.com/v1/search?q=tag:new&type=album&market=US&offset=0" 226 data = _issue_get_request(url, debug=debug) 227 if not simplify 228 return data 229 return _simplify_albums(data["albums"]["items"], debug=debug) 230 231 232def get_playlists_by_user(user_id , debug = True, simplify = True): 233 """ 234 Retrieves a list of Spotify playlists belonging to a particular user. 235 236 Args: 237 user_id (`str`): A valid Spotify user id, represented as a string. 238 debug (`bool`): Whether or not you want debug text to be printed. 239 simplify (`bool`): Indicates whether you want to simplify the data that is returned. 240 241 Returns: 242 a `list` of playlists belonging to the user. 243 """ 244 url = "https://api.spotify.com/v1/users/" + user_id + "/playlists" 245 data = _issue_get_request(url, debug=debug) 246 if not simplify 247 return data 248 return _simplify_playlists(data["items"], debug=debug) 249 250 251def get_albums_by_artist(artist_id, options = [], debug = True, simplify = True): 252 """ 253 Retrieves a list of Spotify albums by a given artist. 254 255 Args: 256 artist_id (`str`): A valid Spotify artist ID 257 options (`list`): A list of special strings to indicate what sort of albums to look for. Valid 258 items are: `"album"`, `"single"`, "`compilation`", and `"appears_on"`. 259 debug (`bool`): Whether or not you want debug text to be printed. 260 simplify (`bool`): Indicates whether you want to simplify the data that is returned. 261 262 Returns: 263 * a `list` of albums. 264 """ 265 266 _verify_artist_ids([artist_id]) 267 268 valid_options = ["album", "single", "compilation", "appears_on"] 269 for option in options 270 if option not in valid_options 271 raise TypeError(f"The options input only accepts the following options: {valid_options}") 272 273 if options 274 url = f"https://api.spotify.com/v1/artists/{artist_id}/albums?include_groups={",".join(options)}&market=US" 275 else 276 url = f"https://api.spotify.com/v1/artists/{artist_id}/albums?market=US" 277 data = _issue_get_request(url, debug=debug) 278 if not simplify 279 return data 280 return _simplify_albums(data["items"], debug=debug) 281 282def _get_several_artists(artist_ids , debug = True, simplify = True): 283 """ 284 Retrieves a bunch of particular artists from Spotify API. 285 286 Args: 287 artist_id (`str`): The id of the Spotify artist. 288 debug (`bool`): Whether or not you want debug text to be printed. 289 290 Returns: 291 a `dictionary` representing the artist 292 """ 293 if artist_ids 294 for i in artist_ids 295 if len(i) != 22 296 raise TypeError(f"This input requires Artist IDs but you gave it {i}.") 297 else 298 raise TypeError("There are no artist ids in this list.") 299 300 301 url = "https://api.spotify.com/v1/artists?ids=" + ",".join(artist_ids) 302 303 if debug 304 print(f"DEBUG - {"_get_several_artists"}:\nHere"s the request we"re going to make.\n{url}\nYou can"t access it in a browser, but you can double check the inputs you gave the function are part of the URL.") 305 306 data = _issue_get_request(url, debug=debug) 307 308 if not simplify 309 return data 310 311 return _simplify_artists(data["artists"], debug=debug) 312 313def _get_several_tracks(track_ids , debug = True, simplify = True): 314 """ 315 Retrieves a bunch of particular tracks from Spotify API. 316 317 Args: 318 artist_id (`str`): The id of the Spotify artist. 319 debug (`bool`): Whether or not you want debug text to be printed. 320 321 Returns: 322 a `dictionary` representing the artist 323 """ 324 if track_ids 325 for i in track_ids 326 if len(i) != 22 327 raise TypeError(f"This input requires Track IDs but you gave it {i}.") 328 else 329 raise TypeError("There are no Track ids in this list.") 330 331 url = "https://api.spotify.com/v1/tracks?ids=" + ",".join(track_ids) + "&market=US" 332 333 print(f"DEBUG - {"_get_several_tracks"}: Here"s the request we"re going to make.\n{url}\nYou can"t access it in a browser, but you can double check the inputs you gave the function are part of the URL.") 334 335 data = _issue_get_request(url, debug=debug) 336 337 if not simplify 338 return data 339 340 return _simplify_tracks(data["tracks"], debug=debug) 341 342 343def generate_mixtape(artist_ids = [], track_ids = [], genres = [], length = 20, debug = True, practice = True, simplify = True): 344 """ 345 Generate a mixtape based off the inputted artists, tracks, and genres. You must provide at least 1 input across artists, tracks, and genres. 346 <mark>Keep in mind it does not accept artist names or track names.</mark> Specifying multiple genres might result in getting zero results.</mark> 347 348 Note: In practice mode, this function will print a warning if inputs are invalid but will still return data. 349 350 Args: 351 artist_ids (`list`): A list of artist ids (list of strings). Example: `[ "06HL4z0CvFAxyc27GXpf02", "3Nrfpe0tUJi4K4DXYWgMUX" ]` 352 track_ids (`list`): A list of track ids. Example: `[ "5ZBeML7Lf3FMEVviTyvi8l", "29U7stRjqHU6rMiS8BfaI9" ]` 353 genres (`list`): A list of genres. Example: `[ "chill" ]` 354 length (`int`): How many tracks to return as part of the mixtape 355 debug (`bool`): Whether or not you want debug text to be printed. 356 practice (`bool`): Whether or not you want real data <mark>ONLY DO THIS IF YOU HAVE SUCCESSFULLY GOTTEN PRACTICE DATA WORKING</mark> 357 358 Returns: 359 * a `list` of tracks (dictionaries) 360 """ 361 362 if not artist_ids and not track_ids and not genres 363 if practice 364 print(f"PRACTICE MODE: Note, no inputs were given.") 365 else 366 raise Exception("Either artist_ids or track_ids or genres required") 367 368 if practice 369 return _simplify_tracks(json.loads(TRACKS_JSON)["tracks"]) 370 371 _verify_artist_ids(artist_ids, practice=practice) 372 _verify_track_ids(track_ids, practice=practice) 373 374 track_list = [] 375 376 for artist in artist_ids 377 track_list += _get_top_tracks_by_artist(artist_id=artist, debug=debug, simplify=simplify) 378 379 if track_ids 380 track_list += _get_several_tracks(track_ids, debug=debug, simplify=simplify) 381 382 if genres 383 track_list += _search_by_genres(genres, debug=debug, simplify=simplify) 384 385 track_list = _remove_duplicate_tracks(track_list) 386 random.shuffle(track_list) 387 388 return track_list[:length] 389 390 391def _remove_duplicate_tracks(track_list): 392 unique_track_ids = [] 393 unique_tracks = [] 394 for track in track_list 395 if track["id"] in unique_track_ids 396 continue 397 else 398 unique_track_ids.append(track["id"]) 399 unique_tracks.append(track) 400 401 return unique_tracks 402 403def _verify_track_ids(track_ids=[], practice = True): 404 for i in track_ids 405 if len(i) != 22 406 if practice 407 print(f"PRACTICE MODE: Note, Track ID "{i}" is invalid.") 408 else 409 raise TypeError(f"This input requires Track IDs but you gave it {i}.") 410 411 412def _verify_artist_ids(artist_ids=[], practice = True): 413 for i in artist_ids 414 if len(i) != 22 415 if practice 416 print(f"PRACTICE MODE: Note, Artist ID "{i}" is invalid.") 417 else 418 raise TypeError(f"This input requires Artist IDs but you gave it {i}.") 419 420 421def get_similar_tracks(artist_ids = [], track_ids = [], genres = [], debug = True, practice = True, simplify = True): 422 """ 423 Spotify"s way of providing recommendations. One or more params is required: 424 artist_ids, track_ids, or genres. Up to 5 seed values may be provided in 425 any combination of seed_artists, seed_tracks and seed_genres. In other words: 426 len(artist_ids) + len(track_ids) + len(genres) between 1 and 5. You MUST provide 427 at least one seed artist or track. 428 429 Args: 430 artist_ids (`list`): A list of artist ids (list of strings). Example: `[ "06HL4z0CvFAxyc27GXpf02", "3Nrfpe0tUJi4K4DXYWgMUX" ]` 431 track_ids (`list`): A list of track ids. Example: `[ "5ZBeML7Lf3FMEVviTyvi8l", "29U7stRjqHU6rMiS8BfaI9" ]` 432 genres (`list`): A list of genres. Example: `[ "chill" ]` 433 debug (`bool`): Whether or not you want debug text to be printed. 434 practice (`bool`): Whether or not you want real data <mark>ONLY DO THIS IF YOU HAVE SUCCESSFULLY GOTTEN PRACTICE DATA WORKING</mark> 435 436 Returns: 437 a `list` of tracks that are similar 438 """ 439 if not artist_ids and not track_ids and not genres 440 raise Exception("Either artist_ids or track_ids or genres required") 441 442 # check if seeds <= 5: 443 random_track = {} 444 random_artist = {} 445 artist_ids = (artist_ids) or [] 446 track_ids = (track_ids) or [] 447 genres = (genres) or [] 448 if len(artist_ids) + len(track_ids) + len(genres) > 5 449 error = "You can only have 5 "seed values" in your recommendations query.\n" + \ 450 "In other words, (len(artist_ids) + len(track_ids) + len(genres)) must be less than or equal to 5." 451 raise Exception(error) 452 453 if artist_ids 454 artist_ids = sorted(artist_ids) 455 for i in artist_ids 456 if len(i) != 22 457 raise TypeError(f"This input requires Artist IDs but you gave it {i}.") 458 459 if not practice 460 artists = _get_several_artists(artist_ids, debug=debug) 461 print(artists) 462 simple_artists = _simplify_artists(artists) 463 464 random_artist = simple_artists[0] 465 466 if track_ids 467 track_ids = sorted(track_ids) 468 for i in track_ids 469 if len(i) != 22 470 raise TypeError(f"This input requires Track IDs but you gave it {i}.") 471 472 if not practice 473 tracks = _get_several_tracks(track_ids, debug=debug) 474 simple_tracks = _simplify_tracks(tracks) 475 476 random_track = simple_tracks[0] 477 478 if genres 479 genres = sorted(genres) 480 # params.append("seed_genres=" + ",".join(genres)) 481 482 if practice 483 return _simplify_tracks(TRACKS_JSON) 484 485 if random_artist and random_track 486 related_artists = _lastfm_get_similar_artists(random_artist["name"], debug=debug) 487 related_artists_tracks = _lastfm_get_top_tracks(related_artists[0]["name"], debug=debug) 488 489 pick_one = random.choice(related_artists_tracks) 490 other_related_tracks = _lastfm_get_similar_tracks(pick_one["name"], debug=debug ) 491 492 related_tracks = _lastfm_get_similar_tracks(random_track["name"], random_track["artists"][0]["name"], debug=debug) 493 494 return other_related_tracks + related_tracks 495 496 elif random_track 497 related_tracks = _lastfm_get_similar_tracks( 498 random_track["name"], random_track["artists"][0]["name"], debug=debug 499 ) 500 return related_tracks 501 502 elif random_artist 503 related_artists = _lastfm_get_similar_artists(random_artist["name"], limit=5, debug=debug) 504 #print(related_artists) 505 related_artists_tracks = _lastfm_get_top_tracks(related_artists[0]["name"], debug=debug) 506 #print(related_artists_tracks) 507 random_track = random.choice(related_artists_tracks) 508 print(random_track) 509 related_tracks = _lastfm_get_similar_tracks( 510 random_track["name"], 511 random_track["artist"]["name"], 512 limit=10, 513 debug=debug, 514 ) 515 516 return related_tracks 517 518 else 519 raise Exception(f"It looks like you either only provided a genre or we didn"t get any data back from the API request. Here"s what you gave: artist_ids: {artist_ids}\ntrack_ids: {track_ids}\ngenres: {genres}") 520 521 522def _lastfm_get_similar_tracks(track_name, artist_name, limit=5, debug = False): 523 524 if not track_name or not artist_name 525 raise Exception("Tried to get a track or artist with no name.") 526 527 url = f"http://ws.audioscrobbler.com/2.0/?method=track.getsimilar&artist={artist_name}&track={track_name}&api_key={LASTFM_KEY}&limit={limit}&format=json" 528 529 if debug 530 print( 531 f"DEBUG - {"_lastfm_get_similar_tracks"}\nHere"s the request we"re going to make.\n{url}\nYou can"t access it in a browser, but you can double check the inputs you gave the function are part of the URL." 532 ) 533 534 url = quote(url, safe="/:?&,=") 535 536 cache_lookup = _check_cache_for(url) 537 538 if not cache_lookup 539 response = requests.get(url, verify=True) 540 json_response = response.json() 541 _save_to_cache(url, json_response) 542 543 else 544 json_response = cache_lookup 545 546 print(json_response) 547 548 list_of_tracks = [] 549 for old_track in json_response["similartracks"]["track"]: 550 spotify_formatted_track = { 551 "id" old_track["mbid"], 552 "name" old_track["name"], 553 "url" old_track["url"], 554 "image_url" [x["#text"] for x in old_track["image"] if x["#text"] == "large"], 555 "artist" { 556 "name" old_track["artist"]["name"], 557 "share_url" old_track["artist"]["url"], 558 "id" old_track["artist"]["mbid"], 559 } 560 } 561 list_of_tracks.append(spotify_formatted_track) 562 563 return list_of_tracks 564 565 566def _lastfm_get_top_tracks(name , limit=5, debug = False): 567 if not name 568 raise Exception("Tried to get an artist with no name. Double check you"re entering a valid artist.") 569 570 url = f"http://ws.audioscrobbler.com/2.0/?method=artist.gettoptracks&artist={name}&api_key={LASTFM_KEY}&limit={limit}&format=json" 571 572 if debug 573 print(f"DEBUG - {"lastfm_get_top_tracks"}\nHere"s the request we"re going to make.\n{url}\nYou can"t access it in a browser, but you can double check the inputs you gave the function are part of the URL.") 574 575 url = quote(url, safe="/:?&,=") 576 577 cache_lookup = _check_cache_for(url) 578 579 if not cache_lookup 580 response = requests.get(url, verify=True) 581 json_response = response.json() 582 _save_to_cache(url, json_response) 583 584 else 585 json_response = cache_lookup 586 587 return json_response["toptracks"]["track"] 588 589 590def _lastfm_get_similar_artists(name , limit=5, debug = False): 591 if not name 592 raise Exception("Tried to get an artist with no name. Double check you"re entering a valid artist.") 593 594 url = f"http://ws.audioscrobbler.com/2.0/?method=artist.getsimilar&artist={name}&limit={limit}&api_key={LASTFM_KEY}&format=json" 595 596 if debug 597 print(f"DEBUG - {"_lastfm_get_similar_artists"}\nHere"s the request we"re going to make.\n{url}\nYou can"t access it in a browser, but you can double check the inputs you gave the function are part of the URL.") 598 599 url = quote(url, safe="/:?&,=") 600 601 cache_lookup = _check_cache_for(url) 602 603 if not cache_lookup 604 response = requests.get(url, verify=True) 605 json_response = response.json() 606 _save_to_cache(url, json_response) 607 608 else 609 json_response = cache_lookup 610 611 artists = json_response.get("similarartists").get("artist") 612 613 return artists 614 615 616def _save_to_cache(url, response): 617 618 if os.path.isfile(SPOTIFY_CACHE): 619 with open(SPOTIFY_CACHE, "rb") as file 620 cache = pickle.load(file) 621 else 622 cache = {} 623 624 cache[url] = response 625 626 with open(SPOTIFY_CACHE, "wb") as file 627 pickle.dump(cache, file) 628 629 630def _check_cache_for(url, debug=False) 631 # Check cache 632 if os.path.isfile(SPOTIFY_CACHE): 633 with open(SPOTIFY_CACHE, "rb") as file 634 cache = pickle.load(file) 635 else 636 cache = {} 637 638 if url in cache 639 if debug 640 print(f"DEBUG - {"_check_cache_for"}: Found previous request! Returning cached result.") 641 return cache[url] 642 else 643 return None 644 645 646############################# 647# Some formatting utilities # 648############################# 649 650def generate_tracks_table(tracks , to_html = False): 651 """ 652 Function that builds a string representation of a list tracks (dictionaries). 653 654 Args: 655 tracks (`list`): List of tracks. 656 to_html (`bool`): If `True` it will generate an HTML version (for an email or web page) and if `False` (default) will generate a string to print in Python. 657 658 Returns: 659 * a `str` that has a table in it for tracks 660 """ 661 662 if to_html 663 return _get_tracklist_table_html(tracks) 664 665 line_width = 95 666 text = "" 667 template = "{0:2} | {1:<22.22} | {2:<30.30} | {3:<30.30}\n" 668 669 # header section: 670 text += "-" * line_width + "\n" 671 text += template.format( 672 "", "Name", "Artist", "Album" 673 ) 674 text += "-" * line_width + "\n" 675 676 # data section: 677 counter = 1 678 for track in tracks 679 text += template.format( 680 counter, 681 track.get("name"), 682 track.get("artist").get("name"), 683 track.get("album").get("name") 684 ) 685 counter += 1 686 text += "-" * line_width + "\n" 687 return text 688 689 690def generate_albums_table(albums , to_html = False): 691 """ 692 Function that builds a string representation of a list of albums (dicitonaries). 693 694 Args: 695 albums (`list`): list of albums (dictionaries). 696 to_html (`bool`): If `True` it will generate an HTML version (for an email or web page) and if `False` (default) will generate a string to print in Python. 697 698 Returns: 699 * a `str` that has a table in it 700 """ 701 702 if to_html 703 return _get_album_table_html(albums) 704 705 line_width = 95 706 text = "" 707 template = "{0:2} | {1:<30.30} | {2:<30.30} | {3:<22.22}\n" 708 709 # header section: 710 text += "-" * line_width + "\n" 711 text += template.format("", "Name", "Primary Artist", "Release Date") 712 text += "-" * line_width + "\n" 713 714 # data section: 715 counter = 1 716 for album in albums 717 text += template.format( 718 counter, 719 album.get("name"), 720 album.get("artist").get("name"), 721 album.get("release_date"), 722 ) 723 counter += 1 724 text += "-" * line_width + "\n" 725 return text 726 727 728def generate_artists_table(artists , to_html=False): 729 """ 730 Makes a nice formatted table of artists. Good for writing to an 731 HTML file or showing on the screen. 732 733 Args: 734 artists (`list`): A list of artists. 735 to_html (`bool`): Whether or not you want the HTML version. 736 737 Returns: 738 a `str` with an HTML table in it. 739 """ 740 if not artists 741 raise TypeError(f"A list of artists is required but you gave us a {type(artists)}: {artists}") 742 743 if to_html 744 return _get_artist_table_html(artists) 745 746 line_width = 118 747 text = "" 748 template = "{0:2} | {1:<22.22} | {2:<30.30} | {3:<60.60}\n" 749 750 # header section: 751 text += "-" * line_width + "\n" 752 text += template.format( 753 "", "Name", "Genres", "URL" 754 ) 755 text += "-" * line_width + "\n" 756 757 # data section: 758 counter = 1 759 for artist in artists 760 text += template.format( 761 counter, 762 artist.get("name"), 763 artist.get("genres"), 764 artist.get("share_url") 765 ) 766 counter += 1 767 text += "-" * line_width + "\n" 768 return text 769 770 771def _get_album_table_html(albums ): 772 template = """ 773 <tr> 774 <td {css}>{name}</td> 775 <td {css}><img src="{image_url}" /></td> 776 <td {css}>{primary_artist}</td> 777 <td {css}>{release_date}</td> 778 <td {css}><a href="{share_url}">Listen on Spotify</a></td> 779 </tr> 780 """ 781 cell_css = ( 782 "style="padding:3px;border-bottom:solid 1px #CCC;border-right:solid 1px #CCC;"" 783 ) 784 table_css = "style="width:100%;border:solid 1px #CCC;border-collapse:collapse;margin-bottom:10px;"" 785 786 rows = [] 787 788 # data section: 789 for album in albums 790 rows.append( 791 template.format( 792 css=cell_css, 793 name=album.get("name"), 794 image_url=album.get("image_url_small"), 795 primary_artist=album.get("artist").get("name"), 796 release_date=album.get("release_date"), 797 share_url=album.get("share_url"), 798 ) 799 ) 800 801 return """ 802 <table {table_css}> 803 <tr> 804 <th {css}>Name</th> 805 <th {css}>Image</th> 806 <th {css}>Primary Artist</th> 807 <th {css}>Release Date</th> 808 <th {css}>More</th> 809 </tr> 810 {rows} 811 </table> 812 """.format( 813 css=cell_css, table_css=table_css, rows="".join(rows) 814 ) 815 816 817def _get_artist_table_html(artists ): 818 template = """ 819 <tr> 820 <td {css}>{name}</td> 821 <td {css}><img src="{image_url}" /></td> 822 <td {css}>{genres}</td> 823 <td {css}><a href="{share_url}">Listen on Spotify</a></td> 824 </tr> 825 """ 826 cell_css = "style="padding:3px;border-bottom:solid 1px #CCC;border-right:solid 1px #CCC;"" 827 table_css = "style="width:100%;border:solid 1px #CCC;border-collapse:collapse;margin-bottom:10px;"" 828 829 rows = [] 830 831 # data section: 832 ["name", "image_url_small", "genres", "share_url"] 833 for artist in artists 834 rows.append( 835 template.format( 836 css=cell_css, 837 name=artist.get("name"), 838 image_url=artist.get("image_url_small"), 839 genres=artist.get("genres"), 840 share_url=artist.get("share_url") 841 ) 842 ) 843 844 return """ 845 <table {table_css}> 846 <tr> 847 <th {css}>Name</th> 848 <th {css}>Image</th> 849 <th {css}>Genres</th> 850 <th {css}>More</th> 851 </tr> 852 {rows} 853 </table> 854 """.format(css=cell_css, table_css=table_css, rows="".join(rows)) 855 856 857def _get_tracklist_table_html(tracks ): 858 """ 859 Makes a nice formatted HTML table of tracks. Good for writing to an 860 HTML file or for sending in an email. 861 862 Args: 863 tracks (`list`): A list of tracks. 864 865 Returns: 866 a `str` with an HTML table in it. 867 """ 868 if not tracks 869 raise TypeError(f"A list of tracks is required but you gave us a {type(tracks)}: {tracks}") 870 871 template = """ 872 <tr> 873 <td {css}>{name}</td> 874 <td {css}><img src="{image_url}" /></td> 875 <td {css}>{artist_name}</td> 876 <td {css}>{album_name}</td> 877 <td {css}><a href="{share_url}">Listen on Spotify</a></td> 878 </tr> 879 """ 880 cell_css = "style="padding:3px;border-bottom:solid 1px #CCC;border-right:solid 1px #CCC;"" 881 table_css = "style="width:100%;border:solid 1px #CCC;border-collapse:collapse;margin-bottom:10px;"" 882 883 rows = [] 884 885 # data section: 886 ["name", "album_image_url_small", "artist_name", "album_name", "share_url"] 887 for track in tracks 888 rows.append( 889 template.format( 890 css=cell_css, 891 name=track.get("name"), 892 image_url=track.get("album").get("image_url_small"), 893 artist_name=track.get("artist").get("name"), 894 album_name=track.get("album").get("name"), 895 share_url=track.get("share_url") 896 ) 897 ) 898 899 return """ 900 <table {table_css}> 901 <tr> 902 <th {css}>Name</th> 903 <th {css}>Image</th> 904 <th {css}>Artist</th> 905 <th {css}>Album</th> 906 <th {css}>More</th> 907 </tr> 908 {rows} 909 </table> 910 """.format(css=cell_css, table_css=table_css, rows="".join(rows)) 911 912 913############################################ 914# Some private, helper functions utilities # 915############################################ 916 917def _generate_authentication_header(backup=False, debug=True): 918 not_cached = True 919 alt_in_use = False 920 shoud_i_remove = False 921 if os.path.isfile(SPOTIFY_JSON): 922 with open(SPOTIFY_JSON) as json_file 923 data = json.load(json_file) 924 if time.time() >= float(data["expires"]): 925 if debug 926 print(f"DEBUG - {"_generate_authentication_header"}:\n Auth token expired, time to refresh!") 927 should_i_remove = True 928 else 929 token = data["token"] 930 not_cached = False 931 932 if shoud_i_remove 933 time.sleep(0.2) 934 os.remove(SPOTIFY_JSON) 935 936 if not_cached 937 if debug 938 print(f"DEBUG - {"_generate_authentication_header"}:\n Generating new Spotify Authentication Token...") 939 from apis import secret_tokens 940 941 current_timestamp = (time.time()) 942 if current_timestamp % 10 < 7 943 alt_in_use = True 944 client_id = secret_tokens.ALT_SPOTIFY_CLIENT_ID 945 client_secret = secret_tokens.ALT_SPOTIFY_CLIENT_SECRET 946 else 947 client_id = secret_tokens.SPOTIFY_CLIENT_ID 948 client_secret = secret_tokens.SPOTIFY_CLIENT_SECRET 949 950 # Step 1 - Authorization 951 auth_url = "https://accounts.spotify.com/api/token" 952 headers = {} 953 data = {} 954 955 # Encode as Base64 956 message = f"{client_id}:{client_secret}" 957 messageBytes = message.encode("ascii") 958 base64Bytes = base64.b64encode(messageBytes) 959 base64Message = base64Bytes.decode("ascii") 960 961 headers["Authorization"] = f"Basic {base64Message}" 962 data["grant_type"] = "client_credentials" 963 964 if not backup 965 try 966 r = requests.post(auth_url, headers=headers, data=data) 967 token = r.json()["access_token"] 968 with open(SPOTIFY_JSON, "w") as outfile 969 json_object = json.dumps({ "token" token, "expires" time.time() + 3540, "alt?"alt_in_use }, indent=4) 970 outfile.write(json_object) 971 972 except 973 if debug 974 print(f"DEBUG - {"_generate_authentication_header"}:\nCouldn"t use either default API Key. Trying backup!") 975 backup = True 976 977 if backup 978 if debug 979 print(f"DEBUG - {"_generate_authentication_header"}:\nUsing backup...") 980 time.sleep(1) 981 if os.path.isfile(SPOTIFY_JSON): 982 os.remove(SPOTIFY_JSON) 983 984 from apis import authentication 985 try 986 token = authentication.get_token( 987 "https://www.apitutor.org/spotify/key") 988 except Exception as e 989 print("ERROR: COULD NOT COMMUNICATE WITH SPOTIFY. I even tried the backup. Did you run the setup tests?") 990 raise Exception(e) 991 992 headers = { 993 "Authorization" "Bearer " + token 994 } 995 996 return headers 997 998def _issue_get_request(url, debug = True, practice = True): 999 """ 1000 Private function. Retrieves data from any Spotify endpoint using the authentication key. 1001 1002 * url (str): The API Endpoint + query parameters. 1003 * debug (bool): Whether or not to print debug messages. 1004 * practice (bool): Whether or not to return real data or practice data 1005 1006 Returns whatever Spotify"s API endpoint gives back. 1007 """ 1008 if debug 1009 print(f"DEBUG - {"_issue_get_request"}:\nHere"s the request we"re going to make.\n", url, "\nYou can"t access it in a browser, but you can double check the inputs you gave the function are part of the URL.") 1010 1011 headers = _generate_authentication_header() 1012 1013 url = quote(url, safe="/:?&,=-") 1014 1015 # Check cache 1016 if os.path.isfile(SPOTIFY_CACHE): 1017 with open(SPOTIFY_CACHE, "rb") as file 1018 cache = pickle.load(file) 1019 else 1020 cache = {} 1021 1022 if url in cache 1023 if debug 1024 print(f"DEBUG - {"_issue_get_request"}:\nFound previous request! Returning cached result.") 1025 return cache[url] 1026 1027 response = requests.get(url, headers=headers, verify=True) 1028 if response.status_code == 429 1029 retry_length = response.headers["Retry-After"] 1030 if debug 1031 print(f"ERROR: Spotify API is overloaded! It asked us to try again in {retry_length} seconds.") 1032 1033 if os.path.isfile(SPOTIFY_JSON): 1034 os.remove(SPOTIFY_JSON) 1035 1036 if debug 1037 print(f"DEBUG - {"_issue_get_request"}:\nWe"re going to try to use the backup...") 1038 headers = _generate_authentication_header(backup=True) 1039 response = requests.get(url, headers=headers, verify=True) 1040 if response.status_code != 200 1041 raise Exception("Uh oh. Couldn"t use the backup either. It"s likely you"ve given this function invalid inputs!") 1042 1043 json_response = response.json() 1044 1045 # Save it in the cache 1046 cache[url] = json_response 1047 1048 with open(SPOTIFY_CACHE, "wb") as file 1049 pickle.dump(cache, file) 1050 1051 return json_response 1052 1053def _simplify_tracks(tracks , debug=True): 1054 """ 1055 Private function. Simplifies the Spotify track data so that each dictionary only contains 1056 a few key features (the original dictionary is quite large). 1057 1058 * tracks (list): The original tracks data structure returned from Spotify. 1059 1060 Returns a list of simplified tracks. 1061 """ 1062 try 1063 tracks[0] 1064 except Exception 1065 return tracks 1066 1067 simplified = [] 1068 for item in tracks 1069 track = { 1070 "id" item["id"], 1071 "name" item["name"], 1072 "preview_url" item["preview_url"], 1073 "share_url" "https://open.spotify.com/track/" + item["id"] 1074 } 1075 try 1076 track["album"] = { 1077 "id" item["album"]["id"], 1078 "name" item["album"]["name"], 1079 "image_url" item["album"]["images"][0]["url"], 1080 "image_url_small" item["album"]["images"][-1]["url"], 1081 "share_url" "https://open.spotify.com/album/" + item["album"]["id"] 1082 } 1083 except Exception 1084 pass 1085 try 1086 artists = item.get("album").get("artists") 1087 artist = artists[0] 1088 track["artist"] = { 1089 "id" artist["id"], 1090 "name" artist["name"], 1091 "share_url" "https://open.spotify.com/artist/" + item["album"]["artists"][0]["id"] 1092 } 1093 except Exception 1094 pass 1095 simplified.append(track) 1096 return simplified 1097 1098 1099def _simplify_artists(artists , debug=True): 1100 """ 1101 Private function. Simplifies the Spotify artist data so that each dictionary only contains 1102 a few key features (the original dictionary is quite large). 1103 1104 * artists (list): The original artists data structure returned from Spotify. 1105 1106 Returns a list of simplified artists. 1107 """ 1108 try 1109 artists[0] 1110 except Exception 1111 return artists 1112 1113 simplified = [] 1114 for item in artists 1115 artist = { 1116 "id" item["id"], 1117 "name" item["name"], 1118 "genres" ", ".join(item["genres"]), 1119 "share_url" "https://open.spotify.com/artist/" + item["id"] 1120 } 1121 try 1122 artist["image_url"] = item["images"][0]["url"] 1123 artist["image_url_small"] = item["images"][-1]["url"] 1124 except Exception 1125 pass 1126 simplified.append(artist) 1127 return simplified 1128 1129def _simplify_albums(albums , debug=True): 1130 """ 1131 Private function. Simplifies the Spotify artist data so that each dictionary only contains 1132 a few key features (the original dictionary is quite large). 1133 1134 * artists (list): The original artists data structure returned from Spotify. 1135 1136 Returns a list of simplified artists. 1137 """ 1138 try 1139 albums[0] 1140 except Exception 1141 return albums 1142 1143 simplified = [] 1144 for item in albums 1145 album = { 1146 "id" item["id"], 1147 "name" item["name"], 1148 "release_date" item["release_date"], 1149 "artist" item["artists"][0], 1150 "share_url" "https://open.spotify.com/album/" + item["id"] 1151 } 1152 try 1153 album["image_url"] = item["images"][0]["url"] 1154 album["image_url_small"] = item["images"][-1]["url"] 1155 except Exception 1156 pass 1157 simplified.append(album) 1158 return simplified 1159 1160def _simplify_playlists(playlists , debug=True): 1161 """ 1162 Private function. Simplifies the Spotify playlist data so that each dictionary only contains 1163 a few key features (the original dictionary is quite large). 1164 1165 * playlists (list): The original playlist data structure returned from Spotify. 1166 1167 Returns a list of simplified playlist entries. 1168 """ 1169 try 1170 simplified = [] 1171 for item in playlists 1172 # Winter 25 - some playlists come back as None 1173 if item is None 1174 continue 1175 simplified.append({ 1176 "id" item["id"], 1177 "name" item["name"], 1178 "owner_display_name" item["owner"]["display_name"], 1179 "owner_id" item["owner"]["id"], 1180 "share_url" "https://open.spotify.com/playlist/" + item["id"], 1181 "num_tracks" item["tracks"]["total"] 1182 }) 1183 return simplified 1184 except Exception as e 1185 raise Exception( 1186 f"The following playlist data structure could not be flattened:\n{playlists}" 1187 ) 1188 1189 1190TRACKS_JSON = """ 1191{ 1192 "tracks": [ 1193 { 1194 "album": { 1195 "album_type": "ALBUM", 1196 "artists": [ 1197 { 1198 "external_urls": { 1199 "spotify": "https://open.spotify.com/artist/6UE7nl9mha6s8z0wFQFIZ2" 1200 }, 1201 "href": "https://api.spotify.com/v1/artists/6UE7nl9mha6s8z0wFQFIZ2", 1202 "id": "6UE7nl9mha6s8z0wFQFIZ2", 1203 "name": "Robyn", 1204 "type": "artist", 1205 "uri": "spotify:artist:6UE7nl9mha6s8z0wFQFIZ2" 1206 } 1207 ], 1208 "external_urls": { 1209 "spotify": "https://open.spotify.com/album/0le9TO3kU69m6iWHTjNs9Y" 1210 }, 1211 "href": "https://api.spotify.com/v1/albums/0le9TO3kU69m6iWHTjNs9Y", 1212 "id": "0le9TO3kU69m6iWHTjNs9Y", 1213 "images": [ 1214 { 1215 "height": 640, 1216 "url": "https://i.scdn.co/image/ab67616d0000b2734689597708c1b0b3dcb9ecaf", 1217 "width": 640 1218 }, 1219 { 1220 "height": 300, 1221 "url": "https://i.scdn.co/image/ab67616d00001e024689597708c1b0b3dcb9ecaf", 1222 "width": 300 1223 }, 1224 { 1225 "height": 64, 1226 "url": "https://i.scdn.co/image/ab67616d000048514689597708c1b0b3dcb9ecaf", 1227 "width": 64 1228 } 1229 ], 1230 "is_playable": true, 1231 "name": "Body Talk", 1232 "release_date": "2010-01-01", 1233 "release_date_precision": "day", 1234 "total_tracks": 15, 1235 "type": "album", 1236 "uri": "spotify:album:0le9TO3kU69m6iWHTjNs9Y" 1237 }, 1238 "artists": [ 1239 { 1240 "external_urls": { 1241 "spotify": "https://open.spotify.com/artist/6UE7nl9mha6s8z0wFQFIZ2" 1242 }, 1243 "href": "https://api.spotify.com/v1/artists/6UE7nl9mha6s8z0wFQFIZ2", 1244 "id": "6UE7nl9mha6s8z0wFQFIZ2", 1245 "name": "Robyn", 1246 "type": "artist", 1247 "uri": "spotify:artist:6UE7nl9mha6s8z0wFQFIZ2" 1248 } 1249 ], 1250 "disc_number": 1, 1251 "duration_ms": 278080, 1252 "explicit": false, 1253 "external_ids": { 1254 "isrc": "SEWKZ1000009" 1255 }, 1256 "external_urls": { 1257 "spotify": "https://open.spotify.com/track/7g13jf3zqlP5S68Voo5v9m" 1258 }, 1259 "href": "https://api.spotify.com/v1/tracks/7g13jf3zqlP5S68Voo5v9m", 1260 "id": "7g13jf3zqlP5S68Voo5v9m", 1261 "is_local": false, 1262 "is_playable": true, 1263 "linked_from": { 1264 "external_urls": { 1265 "spotify": "https://open.spotify.com/track/6aqNCrRA7vs7v6QvRpI50t" 1266 }, 1267 "href": "https://api.spotify.com/v1/tracks/6aqNCrRA7vs7v6QvRpI50t", 1268 "id": "6aqNCrRA7vs7v6QvRpI50t", 1269 "type": "track", 1270 "uri": "spotify:track:6aqNCrRA7vs7v6QvRpI50t" 1271 }, 1272 "name": "Dancing On My Own - Radio Edit", 1273 "popularity": 63, 1274 "preview_url": null, 1275 "track_number": 1, 1276 "type": "track", 1277 "uri": "spotify:track:7g13jf3zqlP5S68Voo5v9m" 1278 }, 1279 { 1280 "album": { 1281 "album_type": "COMPILATION", 1282 "artists": [ 1283 { 1284 "external_urls": { 1285 "spotify": "https://open.spotify.com/artist/0LyfQWJT6nXafLPZqxe9Of" 1286 }, 1287 "href": "https://api.spotify.com/v1/artists/0LyfQWJT6nXafLPZqxe9Of", 1288 "id": "0LyfQWJT6nXafLPZqxe9Of", 1289 "name": "Various Artists", 1290 "type": "artist", 1291 "uri": "spotify:artist:0LyfQWJT6nXafLPZqxe9Of" 1292 }, 1293 { 1294 "external_urls": { 1295 "spotify": "https://open.spotify.com/artist/2dfDjeZroUd3LWmSFrAZCD" 1296 }, 1297 "href": "https://api.spotify.com/v1/artists/2dfDjeZroUd3LWmSFrAZCD", 1298 "id": "2dfDjeZroUd3LWmSFrAZCD", 1299 "name": "David Parry", 1300 "type": "artist", 1301 "uri": "spotify:artist:2dfDjeZroUd3LWmSFrAZCD" 1302 }, 1303 { 1304 "external_urls": { 1305 "spotify": "https://open.spotify.com/artist/3PfJE6ebCbCHeuqO4BfNeA" 1306 }, 1307 "href": "https://api.spotify.com/v1/artists/3PfJE6ebCbCHeuqO4BfNeA", 1308 "id": "3PfJE6ebCbCHeuqO4BfNeA", 1309 "name": "London Philharmonic Orchestra", 1310 "type": "artist", 1311 "uri": "spotify:artist:3PfJE6ebCbCHeuqO4BfNeA" 1312 } 1313 ], 1314 "external_urls": { 1315 "spotify": "https://open.spotify.com/album/0bgjJ99UFbk0yBOzjJl7cq" 1316 }, 1317 "href": "https://api.spotify.com/v1/albums/0bgjJ99UFbk0yBOzjJl7cq", 1318 "id": "0bgjJ99UFbk0yBOzjJl7cq", 1319 "images": [ 1320 { 1321 "height": 640, 1322 "url": "https://i.scdn.co/image/ab67616d0000b27376206bd8de3a477aafd7ba83", 1323 "width": 640 1324 }, 1325 { 1326 "height": 300, 1327 "url": "https://i.scdn.co/image/ab67616d00001e0276206bd8de3a477aafd7ba83", 1328 "width": 300 1329 }, 1330 { 1331 "height": 64, 1332 "url": "https://i.scdn.co/image/ab67616d0000485176206bd8de3a477aafd7ba83", 1333 "width": 64 1334 } 1335 ], 1336 "is_playable": true, 1337 "name": "The 50 Greatest Pieces of Classical Music", 1338 "release_date": "2009-11-23", 1339 "release_date_precision": "day", 1340 "total_tracks": 50, 1341 "type": "album", 1342 "uri": "spotify:album:0bgjJ99UFbk0yBOzjJl7cq" 1343 }, 1344 "artists": [ 1345 { 1346 "external_urls": { 1347 "spotify": "https://open.spotify.com/artist/430byzy0c5bPn5opiu0SRd" 1348 }, 1349 "href": "https://api.spotify.com/v1/artists/430byzy0c5bPn5opiu0SRd", 1350 "id": "430byzy0c5bPn5opiu0SRd", 1351 "name": "Edward Elgar", 1352 "type": "artist", 1353 "uri": "spotify:artist:430byzy0c5bPn5opiu0SRd" 1354 }, 1355 { 1356 "external_urls": { 1357 "spotify": "https://open.spotify.com/artist/2dfDjeZroUd3LWmSFrAZCD" 1358 }, 1359 "href": "https://api.spotify.com/v1/artists/2dfDjeZroUd3LWmSFrAZCD", 1360 "id": "2dfDjeZroUd3LWmSFrAZCD", 1361 "name": "David Parry", 1362 "type": "artist", 1363 "uri": "spotify:artist:2dfDjeZroUd3LWmSFrAZCD" 1364 }, 1365 { 1366 "external_urls": { 1367 "spotify": "https://open.spotify.com/artist/3PfJE6ebCbCHeuqO4BfNeA" 1368 }, 1369 "href": "https://api.spotify.com/v1/artists/3PfJE6ebCbCHeuqO4BfNeA", 1370 "id": "3PfJE6ebCbCHeuqO4BfNeA", 1371 "name": "London Philharmonic Orchestra", 1372 "type": "artist", 1373 "uri": "spotify:artist:3PfJE6ebCbCHeuqO4BfNeA" 1374 } 1375 ], 1376 "disc_number": 1, 1377 "duration_ms": 345666, 1378 "explicit": false, 1379 "external_ids": { 1380 "isrc": "SEWDL9750023" 1381 }, 1382 "external_urls": { 1383 "spotify": "https://open.spotify.com/track/5nsYZFNyMefd50yamWgMfy" 1384 }, 1385 "href": "https://api.spotify.com/v1/tracks/5nsYZFNyMefd50yamWgMfy", 1386 "id": "5nsYZFNyMefd50yamWgMfy", 1387 "is_local": false, 1388 "is_playable": true, 1389 "name": "Pomp and Circumstance, Op. 39: No. 1, March in D Major", 1390 "popularity": 43, 1391 "preview_url": "https://p.scdn.co/mp3-preview/f617411fdc9c52d598ec9d5cde4baf24895598cb?cid=b800bf59070c4b52a8917d2230d25bdc", 1392 "track_number": 23, 1393 "type": "track", 1394 "uri": "spotify:track:5nsYZFNyMefd50yamWgMfy" 1395 }, 1396 { 1397 "album": { 1398 "album_type": "ALBUM", 1399 "artists": [ 1400 { 1401 "external_urls": { 1402 "spotify": "https://open.spotify.com/artist/37mtx80nMDETlbsq2eFCzc" 1403 }, 1404 "href": "https://api.spotify.com/v1/artists/37mtx80nMDETlbsq2eFCzc", 1405 "id": "37mtx80nMDETlbsq2eFCzc", 1406 "name": "Fleur East", 1407 "type": "artist", 1408 "uri": "spotify:artist:37mtx80nMDETlbsq2eFCzc" 1409 } 1410 ], 1411 "external_urls": { 1412 "spotify": "https://open.spotify.com/album/1nFgJpjh2doGfve56uADlm" 1413 }, 1414 "href": "https://api.spotify.com/v1/albums/1nFgJpjh2doGfve56uADlm", 1415 "id": "1nFgJpjh2doGfve56uADlm", 1416 "images": [ 1417 { 1418 "height": 640, 1419 "url": "https://i.scdn.co/image/ab67616d0000b27340ab5a3c8e7e7c8f12cca2bb", 1420 "width": 640 1421 }, 1422 { 1423 "height": 300, 1424 "url": "https://i.scdn.co/image/ab67616d00001e0240ab5a3c8e7e7c8f12cca2bb", 1425 "width": 300 1426 }, 1427 { 1428 "height": 64, 1429 "url": "https://i.scdn.co/image/ab67616d0000485140ab5a3c8e7e7c8f12cca2bb", 1430 "width": 64 1431 } 1432 ], 1433 "is_playable": true, 1434 "name": "Love, Sax & Flashbacks (Track by Track)", 1435 "release_date": "2015-12-03", 1436 "release_date_precision": "day", 1437 "total_tracks": 32, 1438 "type": "album", 1439 "uri": "spotify:album:1nFgJpjh2doGfve56uADlm" 1440 }, 1441 "artists": [ 1442 { 1443 "external_urls": { 1444 "spotify": "https://open.spotify.com/artist/37mtx80nMDETlbsq2eFCzc" 1445 }, 1446 "href": "https://api.spotify.com/v1/artists/37mtx80nMDETlbsq2eFCzc", 1447 "id": "37mtx80nMDETlbsq2eFCzc", 1448 "name": "Fleur East", 1449 "type": "artist", 1450 "uri": "spotify:artist:37mtx80nMDETlbsq2eFCzc" 1451 } 1452 ], 1453 "disc_number": 1, 1454 "duration_ms": 236440, 1455 "explicit": false, 1456 "external_ids": { 1457 "isrc": "GBHMU1500146" 1458 }, 1459 "external_urls": { 1460 "spotify": "https://open.spotify.com/track/2lqgZDZnlXmVZgToWuoC0l" 1461 }, 1462 "href": "https://api.spotify.com/v1/tracks/2lqgZDZnlXmVZgToWuoC0l", 1463 "id": "2lqgZDZnlXmVZgToWuoC0l", 1464 "is_local": false, 1465 "is_playable": true, 1466 "linked_from": { 1467 "external_urls": { 1468 "spotify": "https://open.spotify.com/track/2IFl6p0LoIfIZOn5IaxZOO" 1469 }, 1470 "href": "https://api.spotify.com/v1/tracks/2IFl6p0LoIfIZOn5IaxZOO", 1471 "id": "2IFl6p0LoIfIZOn5IaxZOO", 1472 "type": "track", 1473 "uri": "spotify:track:2IFl6p0LoIfIZOn5IaxZOO" 1474 }, 1475 "name": "Sax", 1476 "popularity": 56, 1477 "preview_url": "https://p.scdn.co/mp3-preview/6d74e1c14810c12a9ccea0c523c740ab98a262fc?cid=b800bf59070c4b52a8917d2230d25bdc", 1478 "track_number": 1, 1479 "type": "track", 1480 "uri": "spotify:track:2lqgZDZnlXmVZgToWuoC0l" 1481 }, 1482 { 1483 "album": { 1484 "album_type": "SINGLE", 1485 "artists": [ 1486 { 1487 "external_urls": { 1488 "spotify": "https://open.spotify.com/artist/1gALaWbNDnwS2ECV09sn2A" 1489 }, 1490 "href": "https://api.spotify.com/v1/artists/1gALaWbNDnwS2ECV09sn2A", 1491 "id": "1gALaWbNDnwS2ECV09sn2A", 1492 "name": "Nightcrawlers", 1493 "type": "artist", 1494 "uri": "spotify:artist:1gALaWbNDnwS2ECV09sn2A" 1495 } 1496 ], 1497 "external_urls": { 1498 "spotify": "https://open.spotify.com/album/5JVyNX3e2hGoOttoe7B8QL" 1499 }, 1500 "href": "https://api.spotify.com/v1/albums/5JVyNX3e2hGoOttoe7B8QL", 1501 "id": "5JVyNX3e2hGoOttoe7B8QL", 1502 "images": [ 1503 { 1504 "height": 640, 1505 "url": "https://i.scdn.co/image/ab67616d0000b2733b1e6a6f5aa7c01b433779fe", 1506 "width": 640 1507 }, 1508 { 1509 "height": 300, 1510 "url": "https://i.scdn.co/image/ab67616d00001e023b1e6a6f5aa7c01b433779fe", 1511 "width": 300 1512 }, 1513 { 1514 "height": 64, 1515 "url": "https://i.scdn.co/image/ab67616d000048513b1e6a6f5aa7c01b433779fe", 1516 "width": 64 1517 } 1518 ], 1519 "is_playable": true, 1520 "name": "Push The Feeling On", 1521 "release_date": "1995", 1522 "release_date_precision": "year", 1523 "total_tracks": 4, 1524 "type": "album", 1525 "uri": "spotify:album:5JVyNX3e2hGoOttoe7B8QL" 1526 }, 1527 "artists": [ 1528 { 1529 "external_urls": { 1530 "spotify": "https://open.spotify.com/artist/1gALaWbNDnwS2ECV09sn2A" 1531 }, 1532 "href": "https://api.spotify.com/v1/artists/1gALaWbNDnwS2ECV09sn2A", 1533 "id": "1gALaWbNDnwS2ECV09sn2A", 1534 "name": "Nightcrawlers", 1535 "type": "artist", 1536 "uri": "spotify:artist:1gALaWbNDnwS2ECV09sn2A" 1537 }, 1538 { 1539 "external_urls": { 1540 "spotify": "https://open.spotify.com/artist/1yqxFtPHKcGcv6SXZNdyT9" 1541 }, 1542 "href": "https://api.spotify.com/v1/artists/1yqxFtPHKcGcv6SXZNdyT9", 1543 "id": "1yqxFtPHKcGcv6SXZNdyT9", 1544 "name": "MK", 1545 "type": "artist", 1546 "uri": "spotify:artist:1yqxFtPHKcGcv6SXZNdyT9" 1547 } 1548 ], 1549 "disc_number": 1, 1550 "duration_ms": 243160, 1551 "explicit": false, 1552 "external_ids": { 1553 "isrc": "GBANY9500081" 1554 }, 1555 "external_urls": { 1556 "spotify": "https://open.spotify.com/track/1EWsVHU4FNAdtN4R8FETag" 1557 }, 1558 "href": "https://api.spotify.com/v1/tracks/1EWsVHU4FNAdtN4R8FETag", 1559 "id": "1EWsVHU4FNAdtN4R8FETag", 1560 "is_local": false, 1561 "is_playable": true, 1562 "name": "Push The Feeling On - Mk Dub Revisited Edit", 1563 "popularity": 69, 1564 "preview_url": null, 1565 "track_number": 1, 1566 "type": "track", 1567 "uri": "spotify:track:1EWsVHU4FNAdtN4R8FETag" 1568 }, 1569 { 1570 "album": { 1571 "album_type": "ALBUM", 1572 "artists": [ 1573 { 1574 "external_urls": { 1575 "spotify": "https://open.spotify.com/artist/7gAppWoH7pcYmphCVTXkzs" 1576 }, 1577 "href": "https://api.spotify.com/v1/artists/7gAppWoH7pcYmphCVTXkzs", 1578 "id": "7gAppWoH7pcYmphCVTXkzs", 1579 "name": "The Vamps", 1580 "type": "artist", 1581 "uri": "spotify:artist:7gAppWoH7pcYmphCVTXkzs" 1582 } 1583 ], 1584 "external_urls": { 1585 "spotify": "https://open.spotify.com/album/69Pj3ce9XFZUi3XuQylLKf" 1586 }, 1587 "href": "https://api.spotify.com/v1/albums/69Pj3ce9XFZUi3XuQylLKf", 1588 "id": "69Pj3ce9XFZUi3XuQylLKf", 1589 "images": [ 1590 { 1591 "height": 640, 1592 "url": "https://i.scdn.co/image/ab67616d0000b273f36a4e2e1687e678f29328cb", 1593 "width": 640 1594 }, 1595 { 1596 "height": 300, 1597 "url": "https://i.scdn.co/image/ab67616d00001e02f36a4e2e1687e678f29328cb", 1598 "width": 300 1599 }, 1600 { 1601 "height": 64, 1602 "url": "https://i.scdn.co/image/ab67616d00004851f36a4e2e1687e678f29328cb", 1603 "width": 64 1604 } 1605 ], 1606 "is_playable": true, 1607 "name": "Night & Day (Night Edition)", 1608 "release_date": "2017-07-14", 1609 "release_date_precision": "day", 1610 "total_tracks": 10, 1611 "type": "album", 1612 "uri": "spotify:album:69Pj3ce9XFZUi3XuQylLKf" 1613 }, 1614 "artists": [ 1615 { 1616 "external_urls": { 1617 "spotify": "https://open.spotify.com/artist/7gAppWoH7pcYmphCVTXkzs" 1618 }, 1619 "href": "https://api.spotify.com/v1/artists/7gAppWoH7pcYmphCVTXkzs", 1620 "id": "7gAppWoH7pcYmphCVTXkzs", 1621 "name": "The Vamps", 1622 "type": "artist", 1623 "uri": "spotify:artist:7gAppWoH7pcYmphCVTXkzs" 1624 }, 1625 { 1626 "external_urls": { 1627 "spotify": "https://open.spotify.com/artist/4YXycRbyyAE0wozTk7QMEq" 1628 }, 1629 "href": "https://api.spotify.com/v1/artists/4YXycRbyyAE0wozTk7QMEq", 1630 "id": "4YXycRbyyAE0wozTk7QMEq", 1631 "name": "Matoma", 1632 "type": "artist", 1633 "uri": "spotify:artist:4YXycRbyyAE0wozTk7QMEq" 1634 } 1635 ], 1636 "disc_number": 1, 1637 "duration_ms": 197640, 1638 "explicit": false, 1639 "external_ids": { 1640 "isrc": "GBUM71605342" 1641 }, 1642 "external_urls": { 1643 "spotify": "https://open.spotify.com/track/0dXNQ8dckG4eYfEtq9zcva" 1644 }, 1645 "href": "https://api.spotify.com/v1/tracks/0dXNQ8dckG4eYfEtq9zcva", 1646 "id": "0dXNQ8dckG4eYfEtq9zcva", 1647 "is_local": false, 1648 "is_playable": true, 1649 "name": "All Night", 1650 "popularity": 72, 1651 "preview_url": null, 1652 "track_number": 2, 1653 "type": "track", 1654 "uri": "spotify:track:0dXNQ8dckG4eYfEtq9zcva" 1655 }, 1656 { 1657 "album": { 1658 "album_type": "ALBUM", 1659 "artists": [ 1660 { 1661 "external_urls": { 1662 "spotify": "https://open.spotify.com/artist/6fxyWrfmjcbj5d12gXeiNV" 1663 }, 1664 "href": "https://api.spotify.com/v1/artists/6fxyWrfmjcbj5d12gXeiNV", 1665 "id": "6fxyWrfmjcbj5d12gXeiNV", 1666 "name": "Denzel Curry", 1667 "type": "artist", 1668 "uri": "spotify:artist:6fxyWrfmjcbj5d12gXeiNV" 1669 } 1670 ], 1671 "external_urls": { 1672 "spotify": "https://open.spotify.com/album/42fyKPanos0Q3woi848ktg" 1673 }, 1674 "href": "https://api.spotify.com/v1/albums/42fyKPanos0Q3woi848ktg", 1675 "id": "42fyKPanos0Q3woi848ktg", 1676 "images": [ 1677 { 1678 "height": 640, 1679 "url": "https://i.scdn.co/image/ab67616d0000b273166442984ba98f0a2dcaea5e", 1680 "width": 640 1681 }, 1682 { 1683 "height": 300, 1684 "url": "https://i.scdn.co/image/ab67616d00001e02166442984ba98f0a2dcaea5e", 1685 "width": 300 1686 }, 1687 { 1688 "height": 64, 1689 "url": "https://i.scdn.co/image/ab67616d00004851166442984ba98f0a2dcaea5e", 1690 "width": 64 1691 } 1692 ], 1693 "is_playable": true, 1694 "name": "Imperial", 1695 "release_date": "2016-10-14", 1696 "release_date_precision": "day", 1697 "total_tracks": 10, 1698 "type": "album", 1699 "uri": "spotify:album:42fyKPanos0Q3woi848ktg" 1700 }, 1701 "artists": [ 1702 { 1703 "external_urls": { 1704 "spotify": "https://open.spotify.com/artist/6fxyWrfmjcbj5d12gXeiNV" 1705 }, 1706 "href": "https://api.spotify.com/v1/artists/6fxyWrfmjcbj5d12gXeiNV", 1707 "id": "6fxyWrfmjcbj5d12gXeiNV", 1708 "name": "Denzel Curry", 1709 "type": "artist", 1710 "uri": "spotify:artist:6fxyWrfmjcbj5d12gXeiNV" 1711 } 1712 ], 1713 "disc_number": 1, 1714 "duration_ms": 247054, 1715 "explicit": true, 1716 "external_ids": { 1717 "isrc": "USC4R1602006" 1718 }, 1719 "external_urls": { 1720 "spotify": "https://open.spotify.com/track/5BSz8sJO2YmKfNVQG7fq2J" 1721 }, 1722 "href": "https://api.spotify.com/v1/tracks/5BSz8sJO2YmKfNVQG7fq2J", 1723 "id": "5BSz8sJO2YmKfNVQG7fq2J", 1724 "is_local": false, 1725 "is_playable": true, 1726 "name": "ULT", 1727 "popularity": 52, 1728 "preview_url": null, 1729 "track_number": 1, 1730 "type": "track", 1731 "uri": "spotify:track:5BSz8sJO2YmKfNVQG7fq2J" 1732 }, 1733 { 1734 "album": { 1735 "album_type": "ALBUM", 1736 "artists": [ 1737 { 1738 "external_urls": { 1739 "spotify": "https://open.spotify.com/artist/2ojlS7imGFiZ8A8tXXGEt7" 1740 }, 1741 "href": "https://api.spotify.com/v1/artists/2ojlS7imGFiZ8A8tXXGEt7", 1742 "id": "2ojlS7imGFiZ8A8tXXGEt7", 1743 "name": "Library Tapes", 1744 "type": "artist", 1745 "uri": "spotify:artist:2ojlS7imGFiZ8A8tXXGEt7" 1746 } 1747 ], 1748 "external_urls": { 1749 "spotify": "https://open.spotify.com/album/43QOTxisI7tVOFZgUIWu4g" 1750 }, 1751 "href": "https://api.spotify.com/v1/albums/43QOTxisI7tVOFZgUIWu4g", 1752 "id": "43QOTxisI7tVOFZgUIWu4g", 1753 "images": [ 1754 { 1755 "height": 640, 1756 "url": "https://i.scdn.co/image/ab67616d0000b2736813c2e10a4fb2a5cac8ebb0", 1757 "width": 640 1758 }, 1759 { 1760 "height": 300, 1761 "url": "https://i.scdn.co/image/ab67616d00001e026813c2e10a4fb2a5cac8ebb0", 1762 "width": 300 1763 }, 1764 { 1765 "height": 64, 1766 "url": "https://i.scdn.co/image/ab67616d000048516813c2e10a4fb2a5cac8ebb0", 1767 "width": 64 1768 } 1769 ], 1770 "is_playable": true, 1771 "name": "Fragment", 1772 "release_date": "2008-06-15", 1773 "release_date_precision": "day", 1774 "total_tracks": 8, 1775 "type": "album", 1776 "uri": "spotify:album:43QOTxisI7tVOFZgUIWu4g" 1777 }, 1778 "artists": [ 1779 { 1780 "external_urls": { 1781 "spotify": "https://open.spotify.com/artist/2ojlS7imGFiZ8A8tXXGEt7" 1782 }, 1783 "href": "https://api.spotify.com/v1/artists/2ojlS7imGFiZ8A8tXXGEt7", 1784 "id": "2ojlS7imGFiZ8A8tXXGEt7", 1785 "name": "Library Tapes", 1786 "type": "artist", 1787 "uri": "spotify:artist:2ojlS7imGFiZ8A8tXXGEt7" 1788 } 1789 ], 1790 "disc_number": 1, 1791 "duration_ms": 240506, 1792 "explicit": false, 1793 "external_ids": { 1794 "isrc": "SEYTP0800202" 1795 }, 1796 "external_urls": { 1797 "spotify": "https://open.spotify.com/track/7vv4tJIsMFm3XnDHikiwIc" 1798 }, 1799 "href": "https://api.spotify.com/v1/tracks/7vv4tJIsMFm3XnDHikiwIc", 1800 "id": "7vv4tJIsMFm3XnDHikiwIc", 1801 "is_local": false, 1802 "is_playable": true, 1803 "linked_from": { 1804 "external_urls": { 1805 "spotify": "https://open.spotify.com/track/7JQfc4yI4R8E4WOUYR7HD9" 1806 }, 1807 "href": "https://api.spotify.com/v1/tracks/7JQfc4yI4R8E4WOUYR7HD9", 1808 "id": "7JQfc4yI4R8E4WOUYR7HD9", 1809 "type": "track", 1810 "uri": "spotify:track:7JQfc4yI4R8E4WOUYR7HD9" 1811 }, 1812 "name": "Fragment II", 1813 "popularity": 44, 1814 "preview_url": null, 1815 "track_number": 2, 1816 "type": "track", 1817 "uri": "spotify:track:7vv4tJIsMFm3XnDHikiwIc" 1818 }, 1819 { 1820 "album": { 1821 "album_type": "ALBUM", 1822 "artists": [ 1823 { 1824 "external_urls": { 1825 "spotify": "https://open.spotify.com/artist/163tK9Wjr9P9DmM0AVK7lm" 1826 }, 1827 "href": "https://api.spotify.com/v1/artists/163tK9Wjr9P9DmM0AVK7lm", 1828 "id": "163tK9Wjr9P9DmM0AVK7lm", 1829 "name": "Lorde", 1830 "type": "artist", 1831 "uri": "spotify:artist:163tK9Wjr9P9DmM0AVK7lm" 1832 } 1833 ], 1834 "external_urls": { 1835 "spotify": "https://open.spotify.com/album/2nPokmAvYy5qYO4rFU7ZDm" 1836 }, 1837 "href": "https://api.spotify.com/v1/albums/2nPokmAvYy5qYO4rFU7ZDm", 1838 "id": "2nPokmAvYy5qYO4rFU7ZDm", 1839 "images": [ 1840 { 1841 "height": 640, 1842 "url": "https://i.scdn.co/image/ab67616d0000b273e19c63f62581ab73a6589382", 1843 "width": 640 1844 }, 1845 { 1846 "height": 300, 1847 "url": "https://i.scdn.co/image/ab67616d00001e02e19c63f62581ab73a6589382", 1848 "width": 300 1849 }, 1850 { 1851 "height": 64, 1852 "url": "https://i.scdn.co/image/ab67616d00004851e19c63f62581ab73a6589382", 1853 "width": 64 1854 } 1855 ], 1856 "is_playable": true, 1857 "name": "Pure Heroine", 1858 "release_date": "2013-01-01", 1859 "release_date_precision": "day", 1860 "total_tracks": 13, 1861 "type": "album", 1862 "uri": "spotify:album:2nPokmAvYy5qYO4rFU7ZDm" 1863 }, 1864 "artists": [ 1865 { 1866 "external_urls": { 1867 "spotify": "https://open.spotify.com/artist/163tK9Wjr9P9DmM0AVK7lm" 1868 }, 1869 "href": "https://api.spotify.com/v1/artists/163tK9Wjr9P9DmM0AVK7lm", 1870 "id": "163tK9Wjr9P9DmM0AVK7lm", 1871 "name": "Lorde", 1872 "type": "artist", 1873 "uri": "spotify:artist:163tK9Wjr9P9DmM0AVK7lm" 1874 } 1875 ], 1876 "disc_number": 1, 1877 "duration_ms": 258969, 1878 "explicit": false, 1879 "external_ids": { 1880 "isrc": "NZUM71300122" 1881 }, 1882 "external_urls": { 1883 "spotify": "https://open.spotify.com/track/2MvvoeRt8NcOXWESkxWn3g" 1884 }, 1885 "href": "https://api.spotify.com/v1/tracks/2MvvoeRt8NcOXWESkxWn3g", 1886 "id": "2MvvoeRt8NcOXWESkxWn3g", 1887 "is_local": false, 1888 "is_playable": true, 1889 "linked_from": { 1890 "external_urls": { 1891 "spotify": "https://open.spotify.com/track/38WyDtxZhxm63jiythwemE" 1892 }, 1893 "href": "https://api.spotify.com/v1/tracks/38WyDtxZhxm63jiythwemE", 1894 "id": "38WyDtxZhxm63jiythwemE", 1895 "type": "track", 1896 "uri": "spotify:track:38WyDtxZhxm63jiythwemE" 1897 }, 1898 "name": "Ribs", 1899 "popularity": 80, 1900 "preview_url": null, 1901 "track_number": 4, 1902 "type": "track", 1903 "uri": "spotify:track:2MvvoeRt8NcOXWESkxWn3g" 1904 }, 1905 { 1906 "album": { 1907 "album_type": "SINGLE", 1908 "artists": [ 1909 { 1910 "external_urls": { 1911 "spotify": "https://open.spotify.com/artist/0lZoBs4Pzo7R89JM9lxwoT" 1912 }, 1913 "href": "https://api.spotify.com/v1/artists/0lZoBs4Pzo7R89JM9lxwoT", 1914 "id": "0lZoBs4Pzo7R89JM9lxwoT", 1915 "name": "Duran Duran", 1916 "type": "artist", 1917 "uri": "spotify:artist:0lZoBs4Pzo7R89JM9lxwoT" 1918 } 1919 ], 1920 "external_urls": { 1921 "spotify": "https://open.spotify.com/album/5NLAqcJTupUe8pUzf6Jaen" 1922 }, 1923 "href": "https://api.spotify.com/v1/albums/5NLAqcJTupUe8pUzf6Jaen", 1924 "id": "5NLAqcJTupUe8pUzf6Jaen", 1925 "images": [ 1926 { 1927 "height": 640, 1928 "url": "https://i.scdn.co/image/ab67616d0000b273b35021533b9088e272ceeb90", 1929 "width": 640 1930 }, 1931 { 1932 "height": 300, 1933 "url": "https://i.scdn.co/image/ab67616d00001e02b35021533b9088e272ceeb90", 1934 "width": 300 1935 }, 1936 { 1937 "height": 64, 1938 "url": "https://i.scdn.co/image/ab67616d00004851b35021533b9088e272ceeb90", 1939 "width": 64 1940 } 1941 ], 1942 "is_playable": true, 1943 "name": "Last Night in the City (feat. Kiesza) [The Remixes]", 1944 "release_date": "2017-03-10", 1945 "release_date_precision": "day", 1946 "total_tracks": 4, 1947 "type": "album", 1948 "uri": "spotify:album:5NLAqcJTupUe8pUzf6Jaen" 1949 }, 1950 "artists": [ 1951 { 1952 "external_urls": { 1953 "spotify": "https://open.spotify.com/artist/0lZoBs4Pzo7R89JM9lxwoT" 1954 }, 1955 "href": "https://api.spotify.com/v1/artists/0lZoBs4Pzo7R89JM9lxwoT", 1956 "id": "0lZoBs4Pzo7R89JM9lxwoT", 1957 "name": "Duran Duran", 1958 "type": "artist", 1959 "uri": "spotify:artist:0lZoBs4Pzo7R89JM9lxwoT" 1960 }, 1961 { 1962 "external_urls": { 1963 "spotify": "https://open.spotify.com/artist/4zxvC7CRGvggq9EWXOpwAo" 1964 }, 1965 "href": "https://api.spotify.com/v1/artists/4zxvC7CRGvggq9EWXOpwAo", 1966 "id": "4zxvC7CRGvggq9EWXOpwAo", 1967 "name": "Kiesza", 1968 "type": "artist", 1969 "uri": "spotify:artist:4zxvC7CRGvggq9EWXOpwAo" 1970 }, 1971 { 1972 "external_urls": { 1973 "spotify": "https://open.spotify.com/artist/5c8rdJfIrtDTnRak9cuzwv" 1974 }, 1975 "href": "https://api.spotify.com/v1/artists/5c8rdJfIrtDTnRak9cuzwv", 1976 "id": "5c8rdJfIrtDTnRak9cuzwv", 1977 "name": "Louis Vivet", 1978 "type": "artist", 1979 "uri": "spotify:artist:5c8rdJfIrtDTnRak9cuzwv" 1980 } 1981 ], 1982 "disc_number": 1, 1983 "duration_ms": 201999, 1984 "explicit": false, 1985 "external_ids": { 1986 "isrc": "USWB11700345" 1987 }, 1988 "external_urls": { 1989 "spotify": "https://open.spotify.com/track/49Ux5DRv82fNGXDO7sIy4y" 1990 }, 1991 "href": "https://api.spotify.com/v1/tracks/49Ux5DRv82fNGXDO7sIy4y", 1992 "id": "49Ux5DRv82fNGXDO7sIy4y", 1993 "is_local": false, 1994 "is_playable": true, 1995 "name": "Last Night in the City (feat. Kiesza) - Louis Vivet Remix", 1996 "popularity": 6, 1997 "preview_url": "https://p.scdn.co/mp3-preview/9aa4da8b4c1b17c48a1f8a6f24058ac4e47fdf7b?cid=b800bf59070c4b52a8917d2230d25bdc", 1998 "track_number": 3, 1999 "type": "track", 2000 "uri": "spotify:track:49Ux5DRv82fNGXDO7sIy4y" 2001 }, 2002 { 2003 "album": { 2004 "album_type": "SINGLE", 2005 "artists": [ 2006 { 2007 "external_urls": { 2008 "spotify": "https://open.spotify.com/artist/65YhYi4Fz5Ibgq7ueev2Rm" 2009 }, 2010 "href": "https://api.spotify.com/v1/artists/65YhYi4Fz5Ibgq7ueev2Rm", 2011 "id": "65YhYi4Fz5Ibgq7ueev2Rm", 2012 "name": "Frederick Delius", 2013 "type": "artist", 2014 "uri": "spotify:artist:65YhYi4Fz5Ibgq7ueev2Rm" 2015 }, 2016 { 2017 "external_urls": { 2018 "spotify": "https://open.spotify.com/artist/5Dl3HXZjG6ZOWT5cV375lk" 2019 }, 2020 "href": "https://api.spotify.com/v1/artists/5Dl3HXZjG6ZOWT5cV375lk", 2021 "id": "5Dl3HXZjG6ZOWT5cV375lk", 2022 "name": "Yo-Yo Ma", 2023 "type": "artist", 2024 "uri": "spotify:artist:5Dl3HXZjG6ZOWT5cV375lk" 2025 }, 2026 { 2027 "external_urls": { 2028 "spotify": "https://open.spotify.com/artist/7JmDqds7Y1LRSWZVM8e0Og" 2029 }, 2030 "href": "https://api.spotify.com/v1/artists/7JmDqds7Y1LRSWZVM8e0Og", 2031 "id": "7JmDqds7Y1LRSWZVM8e0Og", 2032 "name": "Kathryn Stott", 2033 "type": "artist", 2034 "uri": "spotify:artist:7JmDqds7Y1LRSWZVM8e0Og" 2035 } 2036 ], 2037 "external_urls": { 2038 "spotify": "https://open.spotify.com/album/5Srtg5Q52DBLYEAIEQBDyH" 2039 }, 2040 "href": "https://api.spotify.com/v1/albums/5Srtg5Q52DBLYEAIEQBDyH", 2041 "id": "5Srtg5Q52DBLYEAIEQBDyH", 2042 "images": [ 2043 { 2044 "height": 640, 2045 "url": "https://i.scdn.co/image/ab67616d0000b27309516b49c785767fb6193732", 2046 "width": 640 2047 }, 2048 { 2049 "height": 300, 2050 "url": "https://i.scdn.co/image/ab67616d00001e0209516b49c785767fb6193732", 2051 "width": 300 2052 }, 2053 { 2054 "height": 64, 2055 "url": "https://i.scdn.co/image/ab67616d0000485109516b49c785767fb6193732", 2056 "width": 64 2057 } 2058 ], 2059 "is_playable": true, 2060 "name": "Romance for Cello and Piano", 2061 "release_date": "2015-07-24", 2062 "release_date_precision": "day", 2063 "total_tracks": 1, 2064 "type": "album", 2065 "uri": "spotify:album:5Srtg5Q52DBLYEAIEQBDyH" 2066 }, 2067 "artists": [ 2068 { 2069 "external_urls": { 2070 "spotify": "https://open.spotify.com/artist/65YhYi4Fz5Ibgq7ueev2Rm" 2071 }, 2072 "href": "https://api.spotify.com/v1/artists/65YhYi4Fz5Ibgq7ueev2Rm", 2073 "id": "65YhYi4Fz5Ibgq7ueev2Rm", 2074 "name": "Frederick Delius", 2075 "type": "artist", 2076 "uri": "spotify:artist:65YhYi4Fz5Ibgq7ueev2Rm" 2077 }, 2078 { 2079 "external_urls": { 2080 "spotify": "https://open.spotify.com/artist/7JmDqds7Y1LRSWZVM8e0Og" 2081 }, 2082 "href": "https://api.spotify.com/v1/artists/7JmDqds7Y1LRSWZVM8e0Og", 2083 "id": "7JmDqds7Y1LRSWZVM8e0Og", 2084 "name": "Kathryn Stott", 2085 "type": "artist", 2086 "uri": "spotify:artist:7JmDqds7Y1LRSWZVM8e0Og" 2087 }, 2088 { 2089 "external_urls": { 2090 "spotify": "https://open.spotify.com/artist/5Dl3HXZjG6ZOWT5cV375lk" 2091 }, 2092 "href": "https://api.spotify.com/v1/artists/5Dl3HXZjG6ZOWT5cV375lk", 2093 "id": "5Dl3HXZjG6ZOWT5cV375lk", 2094 "name": "Yo-Yo Ma", 2095 "type": "artist", 2096 "uri": "spotify:artist:5Dl3HXZjG6ZOWT5cV375lk" 2097 } 2098 ], 2099 "disc_number": 1, 2100 "duration_ms": 385186, 2101 "explicit": false, 2102 "external_ids": { 2103 "isrc": "USQX91500821" 2104 }, 2105 "external_urls": { 2106 "spotify": "https://open.spotify.com/track/6ufaxWhOW87EXsKh5TSxMp" 2107 }, 2108 "href": "https://api.spotify.com/v1/tracks/6ufaxWhOW87EXsKh5TSxMp", 2109 "id": "6ufaxWhOW87EXsKh5TSxMp", 2110 "is_local": false, 2111 "is_playable": true, 2112 "linked_from": { 2113 "external_urls": { 2114 "spotify": "https://open.spotify.com/track/70nMUxP46mAcjW5iRpRiyN" 2115 }, 2116 "href": "https://api.spotify.com/v1/tracks/70nMUxP46mAcjW5iRpRiyN", 2117 "id": "70nMUxP46mAcjW5iRpRiyN", 2118 "type": "track", 2119 "uri": "spotify:track:70nMUxP46mAcjW5iRpRiyN" 2120 }, 2121 "name": "Romance for Cello and Piano", 2122 "popularity": 33, 2123 "preview_url": "https://p.scdn.co/mp3-preview/59053165aa006169f88b0888a1c2d8e420d5508a?cid=b800bf59070c4b52a8917d2230d25bdc", 2124 "track_number": 11, 2125 "type": "track", 2126 "uri": "spotify:track:6ufaxWhOW87EXsKh5TSxMp" 2127 }, 2128 { 2129 "album": { 2130 "album_type": "SINGLE", 2131 "artists": [ 2132 { 2133 "external_urls": { 2134 "spotify": "https://open.spotify.com/artist/137W8MRPWKqSmrBGDBFSop" 2135 }, 2136 "href": "https://api.spotify.com/v1/artists/137W8MRPWKqSmrBGDBFSop", 2137 "id": "137W8MRPWKqSmrBGDBFSop", 2138 "name": "Wiz Khalifa", 2139 "type": "artist", 2140 "uri": "spotify:artist:137W8MRPWKqSmrBGDBFSop" 2141 } 2142 ], 2143 "external_urls": { 2144 "spotify": "https://open.spotify.com/album/3qv4Hjk70y70MNe9Kb9X8f" 2145 }, 2146 "href": "https://api.spotify.com/v1/albums/3qv4Hjk70y70MNe9Kb9X8f", 2147 "id": "3qv4Hjk70y70MNe9Kb9X8f", 2148 "images": [ 2149 { 2150 "height": 640, 2151 "url": "https://i.scdn.co/image/ab67616d0000b273dfe0748e91928eaf21373018", 2152 "width": 640 2153 }, 2154 { 2155 "height": 300, 2156 "url": "https://i.scdn.co/image/ab67616d00001e02dfe0748e91928eaf21373018", 2157 "width": 300 2158 }, 2159 { 2160 "height": 64, 2161 "url": "https://i.scdn.co/image/ab67616d00004851dfe0748e91928eaf21373018", 2162 "width": 64 2163 } 2164 ], 2165 "is_playable": true, 2166 "name": "Pull Up (feat. Lil Uzi Vert)", 2167 "release_date": "2016-05-25", 2168 "release_date_precision": "day", 2169 "total_tracks": 1, 2170 "type": "album", 2171 "uri": "spotify:album:3qv4Hjk70y70MNe9Kb9X8f" 2172 }, 2173 "artists": [ 2174 { 2175 "external_urls": { 2176 "spotify": "https://open.spotify.com/artist/137W8MRPWKqSmrBGDBFSop" 2177 }, 2178 "href": "https://api.spotify.com/v1/artists/137W8MRPWKqSmrBGDBFSop", 2179 "id": "137W8MRPWKqSmrBGDBFSop", 2180 "name": "Wiz Khalifa", 2181 "type": "artist", 2182 "uri": "spotify:artist:137W8MRPWKqSmrBGDBFSop" 2183 }, 2184 { 2185 "external_urls": { 2186 "spotify": "https://open.spotify.com/artist/4O15NlyKLIASxsJ0PrXPfz" 2187 }, 2188 "href": "https://api.spotify.com/v1/artists/4O15NlyKLIASxsJ0PrXPfz", 2189 "id": "4O15NlyKLIASxsJ0PrXPfz", 2190 "name": "Lil Uzi Vert", 2191 "type": "artist", 2192 "uri": "spotify:artist:4O15NlyKLIASxsJ0PrXPfz" 2193 } 2194 ], 2195 "disc_number": 1, 2196 "duration_ms": 207391, 2197 "explicit": true, 2198 "external_ids": { 2199 "isrc": "USAT21601765" 2200 }, 2201 "external_urls": { 2202 "spotify": "https://open.spotify.com/track/6LTsvaebP6V9tJFvZxqi5M" 2203 }, 2204 "href": "https://api.spotify.com/v1/tracks/6LTsvaebP6V9tJFvZxqi5M", 2205 "id": "6LTsvaebP6V9tJFvZxqi5M", 2206 "is_local": false, 2207 "is_playable": true, 2208 "name": "Pull Up (feat. Lil Uzi Vert)", 2209 "popularity": 50, 2210 "preview_url": "https://p.scdn.co/mp3-preview/644de9f6fa47954ea3b44748b345a8f5f8d3fdd7?cid=b800bf59070c4b52a8917d2230d25bdc", 2211 "track_number": 1, 2212 "type": "track", 2213 "uri": "spotify:track:6LTsvaebP6V9tJFvZxqi5M" 2214 }, 2215 { 2216 "album": { 2217 "album_type": "ALBUM", 2218 "artists": [ 2219 { 2220 "external_urls": { 2221 "spotify": "https://open.spotify.com/artist/2KsP6tYLJlTBvSUxnwlVWa" 2222 }, 2223 "href": "https://api.spotify.com/v1/artists/2KsP6tYLJlTBvSUxnwlVWa", 2224 "id": "2KsP6tYLJlTBvSUxnwlVWa", 2225 "name": "Mike Posner", 2226 "type": "artist", 2227 "uri": "spotify:artist:2KsP6tYLJlTBvSUxnwlVWa" 2228 } 2229 ], 2230 "external_urls": { 2231 "spotify": "https://open.spotify.com/album/6Phl1V5P0sPrWJytXHGFeO" 2232 }, 2233 "href": "https://api.spotify.com/v1/albums/6Phl1V5P0sPrWJytXHGFeO", 2234 "id": "6Phl1V5P0sPrWJytXHGFeO", 2235 "images": [ 2236 { 2237 "height": 640, 2238 "url": "https://i.scdn.co/image/ab67616d0000b2731db27fa11dbafa67857da8f3", 2239 "width": 640 2240 }, 2241 { 2242 "height": 300, 2243 "url": "https://i.scdn.co/image/ab67616d00001e021db27fa11dbafa67857da8f3", 2244 "width": 300 2245 }, 2246 { 2247 "height": 64, 2248 "url": "https://i.scdn.co/image/ab67616d000048511db27fa11dbafa67857da8f3", 2249 "width": 64 2250 } 2251 ], 2252 "is_playable": true, 2253 "name": "At Night, Alone.", 2254 "release_date": "2016-05-06", 2255 "release_date_precision": "day", 2256 "total_tracks": 18, 2257 "type": "album", 2258 "uri": "spotify:album:6Phl1V5P0sPrWJytXHGFeO" 2259 }, 2260 "artists": [ 2261 { 2262 "external_urls": { 2263 "spotify": "https://open.spotify.com/artist/2KsP6tYLJlTBvSUxnwlVWa" 2264 }, 2265 "href": "https://api.spotify.com/v1/artists/2KsP6tYLJlTBvSUxnwlVWa", 2266 "id": "2KsP6tYLJlTBvSUxnwlVWa", 2267 "name": "Mike Posner", 2268 "type": "artist", 2269 "uri": "spotify:artist:2KsP6tYLJlTBvSUxnwlVWa" 2270 }, 2271 { 2272 "external_urls": { 2273 "spotify": "https://open.spotify.com/artist/5iNrZmtVMtYev5M9yoWpEq" 2274 }, 2275 "href": "https://api.spotify.com/v1/artists/5iNrZmtVMtYev5M9yoWpEq", 2276 "id": "5iNrZmtVMtYev5M9yoWpEq", 2277 "name": "Seeb", 2278 "type": "artist", 2279 "uri": "spotify:artist:5iNrZmtVMtYev5M9yoWpEq" 2280 } 2281 ], 2282 "disc_number": 1, 2283 "duration_ms": 197933, 2284 "explicit": true, 2285 "external_ids": { 2286 "isrc": "USUM71509342" 2287 }, 2288 "external_urls": { 2289 "spotify": "https://open.spotify.com/track/0vbtURX4qv1l7besfwmnD8" 2290 }, 2291 "href": "https://api.spotify.com/v1/tracks/0vbtURX4qv1l7besfwmnD8", 2292 "id": "0vbtURX4qv1l7besfwmnD8", 2293 "is_local": false, 2294 "is_playable": true, 2295 "name": "I Took A Pill In Ibiza - Seeb Remix", 2296 "popularity": 83, 2297 "preview_url": null, 2298 "track_number": 13, 2299 "type": "track", 2300 "uri": "spotify:track:0vbtURX4qv1l7besfwmnD8" 2301 }, 2302 { 2303 "album": { 2304 "album_type": "COMPILATION", 2305 "artists": [ 2306 { 2307 "external_urls": { 2308 "spotify": "https://open.spotify.com/artist/0LyfQWJT6nXafLPZqxe9Of" 2309 }, 2310 "href": "https://api.spotify.com/v1/artists/0LyfQWJT6nXafLPZqxe9Of", 2311 "id": "0LyfQWJT6nXafLPZqxe9Of", 2312 "name": "Various Artists", 2313 "type": "artist", 2314 "uri": "spotify:artist:0LyfQWJT6nXafLPZqxe9Of" 2315 } 2316 ], 2317 "external_urls": { 2318 "spotify": "https://open.spotify.com/album/6lJrqUXdCpEINx1fi5EqMa" 2319 }, 2320 "href": "https://api.spotify.com/v1/albums/6lJrqUXdCpEINx1fi5EqMa", 2321 "id": "6lJrqUXdCpEINx1fi5EqMa", 2322 "images": [ 2323 { 2324 "height": 640, 2325 "url": "https://i.scdn.co/image/ab67616d0000b27333d6ad1275ae8ebe156686f5", 2326 "width": 640 2327 }, 2328 { 2329 "height": 300, 2330 "url": "https://i.scdn.co/image/ab67616d00001e0233d6ad1275ae8ebe156686f5", 2331 "width": 300 2332 }, 2333 { 2334 "height": 64, 2335 "url": "https://i.scdn.co/image/ab67616d0000485133d6ad1275ae8ebe156686f5", 2336 "width": 64 2337 } 2338 ], 2339 "is_playable": true, 2340 "name": "The Twilight Saga: Breaking Dawn - Part 2 (Original Motion Picture Soundtrack)", 2341 "release_date": "2012-11-09", 2342 "release_date_precision": "day", 2343 "total_tracks": 14, 2344 "type": "album", 2345 "uri": "spotify:album:6lJrqUXdCpEINx1fi5EqMa" 2346 }, 2347 "artists": [ 2348 { 2349 "external_urls": { 2350 "spotify": "https://open.spotify.com/artist/0SbSDzM4X41hnlURed0fcV" 2351 }, 2352 "href": "https://api.spotify.com/v1/artists/0SbSDzM4X41hnlURed0fcV", 2353 "id": "0SbSDzM4X41hnlURed0fcV", 2354 "name": "Carter Burwell", 2355 "type": "artist", 2356 "uri": "spotify:artist:0SbSDzM4X41hnlURed0fcV" 2357 } 2358 ], 2359 "disc_number": 1, 2360 "duration_ms": 255773, 2361 "explicit": false, 2362 "external_ids": { 2363 "isrc": "USAT21206056" 2364 }, 2365 "external_urls": { 2366 "spotify": "https://open.spotify.com/track/1brH9m4Q2LHTuwxaKoMTn5" 2367 }, 2368 "href": "https://api.spotify.com/v1/tracks/1brH9m4Q2LHTuwxaKoMTn5", 2369 "id": "1brH9m4Q2LHTuwxaKoMTn5", 2370 "is_local": false, 2371 "is_playable": true, 2372 "name": "Plus Que Ma Prope Vie", 2373 "popularity": 48, 2374 "preview_url": "https://p.scdn.co/mp3-preview/585cd6ea1265df63d349dcda19035a27893f50d2?cid=b800bf59070c4b52a8917d2230d25bdc", 2375 "track_number": 14, 2376 "type": "track", 2377 "uri": "spotify:track:1brH9m4Q2LHTuwxaKoMTn5" 2378 }, 2379 { 2380 "album": { 2381 "album_type": "ALBUM", 2382 "artists": [ 2383 { 2384 "external_urls": { 2385 "spotify": "https://open.spotify.com/artist/03r4iKL2g2442PT9n2UKsx" 2386 }, 2387 "href": "https://api.spotify.com/v1/artists/03r4iKL2g2442PT9n2UKsx", 2388 "id": "03r4iKL2g2442PT9n2UKsx", 2389 "name": "Beastie Boys", 2390 "type": "artist", 2391 "uri": "spotify:artist:03r4iKL2g2442PT9n2UKsx" 2392 } 2393 ], 2394 "external_urls": { 2395 "spotify": "https://open.spotify.com/album/11oR0ZuqB3ucZwb5TGbZxb" 2396 }, 2397 "href": "https://api.spotify.com/v1/albums/11oR0ZuqB3ucZwb5TGbZxb", 2398 "id": "11oR0ZuqB3ucZwb5TGbZxb", 2399 "images": [ 2400 { 2401 "height": 640, 2402 "url": "https://i.scdn.co/image/ab67616d0000b273a7ea08ab3914c5fb2084a8ac", 2403 "width": 640 2404 }, 2405 { 2406 "height": 300, 2407 "url": "https://i.scdn.co/image/ab67616d00001e02a7ea08ab3914c5fb2084a8ac", 2408 "width": 300 2409 }, 2410 { 2411 "height": 64, 2412 "url": "https://i.scdn.co/image/ab67616d00004851a7ea08ab3914c5fb2084a8ac", 2413 "width": 64 2414 } 2415 ], 2416 "is_playable": true, 2417 "name": "Licensed To Ill", 2418 "release_date": "1986-11-15", 2419 "release_date_precision": "day", 2420 "total_tracks": 13, 2421 "type": "album", 2422 "uri": "spotify:album:11oR0ZuqB3ucZwb5TGbZxb" 2423 }, 2424 "artists": [ 2425 { 2426 "external_urls": { 2427 "spotify": "https://open.spotify.com/artist/03r4iKL2g2442PT9n2UKsx" 2428 }, 2429 "href": "https://api.spotify.com/v1/artists/03r4iKL2g2442PT9n2UKsx", 2430 "id": "03r4iKL2g2442PT9n2UKsx", 2431 "name": "Beastie Boys", 2432 "type": "artist", 2433 "uri": "spotify:artist:03r4iKL2g2442PT9n2UKsx" 2434 } 2435 ], 2436 "disc_number": 1, 2437 "duration_ms": 157440, 2438 "explicit": false, 2439 "external_ids": { 2440 "isrc": "USDJ28600011" 2441 }, 2442 "external_urls": { 2443 "spotify": "https://open.spotify.com/track/2tY1gxCKslfXLFpFofYmJQ" 2444 }, 2445 "href": "https://api.spotify.com/v1/tracks/2tY1gxCKslfXLFpFofYmJQ", 2446 "id": "2tY1gxCKslfXLFpFofYmJQ", 2447 "is_local": false, 2448 "is_playable": true, 2449 "name": "Brass Monkey", 2450 "popularity": 68, 2451 "preview_url": null, 2452 "track_number": 11, 2453 "type": "track", 2454 "uri": "spotify:track:2tY1gxCKslfXLFpFofYmJQ" 2455 }, 2456 { 2457 "album": { 2458 "album_type": "ALBUM", 2459 "artists": [ 2460 { 2461 "external_urls": { 2462 "spotify": "https://open.spotify.com/artist/5Q81rlcTFh3k6DQJXPdsot" 2463 }, 2464 "href": "https://api.spotify.com/v1/artists/5Q81rlcTFh3k6DQJXPdsot", 2465 "id": "5Q81rlcTFh3k6DQJXPdsot", 2466 "name": "Mura Masa", 2467 "type": "artist", 2468 "uri": "spotify:artist:5Q81rlcTFh3k6DQJXPdsot" 2469 } 2470 ], 2471 "external_urls": { 2472 "spotify": "https://open.spotify.com/album/0NBTBo1qrg554sAj79nEqD" 2473 }, 2474 "href": "https://api.spotify.com/v1/albums/0NBTBo1qrg554sAj79nEqD", 2475 "id": "0NBTBo1qrg554sAj79nEqD", 2476 "images": [ 2477 { 2478 "height": 640, 2479 "url": "https://i.scdn.co/image/ab67616d0000b2736818aa231aa543cf87e1374a", 2480 "width": 640 2481 }, 2482 { 2483 "height": 300, 2484 "url": "https://i.scdn.co/image/ab67616d00001e026818aa231aa543cf87e1374a", 2485 "width": 300 2486 }, 2487 { 2488 "height": 64, 2489 "url": "https://i.scdn.co/image/ab67616d000048516818aa231aa543cf87e1374a", 2490 "width": 64 2491 } 2492 ], 2493 "is_playable": true, 2494 "name": "Mura Masa", 2495 "release_date": "2017-07-14", 2496 "release_date_precision": "day", 2497 "total_tracks": 13, 2498 "type": "album", 2499 "uri": "spotify:album:0NBTBo1qrg554sAj79nEqD" 2500 }, 2501 "artists": [ 2502 { 2503 "external_urls": { 2504 "spotify": "https://open.spotify.com/artist/5Q81rlcTFh3k6DQJXPdsot" 2505 }, 2506 "href": "https://api.spotify.com/v1/artists/5Q81rlcTFh3k6DQJXPdsot", 2507 "id": "5Q81rlcTFh3k6DQJXPdsot", 2508 "name": "Mura Masa", 2509 "type": "artist", 2510 "uri": "spotify:artist:5Q81rlcTFh3k6DQJXPdsot" 2511 }, 2512 { 2513 "external_urls": { 2514 "spotify": "https://open.spotify.com/artist/7pFeBzX627ff0VnN6bxPR4" 2515 }, 2516 "href": "https://api.spotify.com/v1/artists/7pFeBzX627ff0VnN6bxPR4", 2517 "id": "7pFeBzX627ff0VnN6bxPR4", 2518 "name": "Desiigner", 2519 "type": "artist", 2520 "uri": "spotify:artist:7pFeBzX627ff0VnN6bxPR4" 2521 } 2522 ], 2523 "disc_number": 1, 2524 "duration_ms": 164013, 2525 "explicit": true, 2526 "external_ids": { 2527 "isrc": "GBUM71701364" 2528 }, 2529 "external_urls": { 2530 "spotify": "https://open.spotify.com/track/4x8874idDYp8yxgKsyb4xG" 2531 }, 2532 "href": "https://api.spotify.com/v1/tracks/4x8874idDYp8yxgKsyb4xG", 2533 "id": "4x8874idDYp8yxgKsyb4xG", 2534 "is_local": false, 2535 "is_playable": true, 2536 "name": "All Around The World (feat. Desiigner)", 2537 "popularity": 42, 2538 "preview_url": null, 2539 "track_number": 5, 2540 "type": "track", 2541 "uri": "spotify:track:4x8874idDYp8yxgKsyb4xG" 2542 }, 2543 { 2544 "album": { 2545 "album_type": "SINGLE", 2546 "artists": [ 2547 { 2548 "external_urls": { 2549 "spotify": "https://open.spotify.com/artist/61lyPtntblHJvA7FMMhi7E" 2550 }, 2551 "href": "https://api.spotify.com/v1/artists/61lyPtntblHJvA7FMMhi7E", 2552 "id": "61lyPtntblHJvA7FMMhi7E", 2553 "name": "Duke Dumont", 2554 "type": "artist", 2555 "uri": "spotify:artist:61lyPtntblHJvA7FMMhi7E" 2556 } 2557 ], 2558 "external_urls": { 2559 "spotify": "https://open.spotify.com/album/4SQqe6ACemVTNNOcq7Ql4A" 2560 }, 2561 "href": "https://api.spotify.com/v1/albums/4SQqe6ACemVTNNOcq7Ql4A", 2562 "id": "4SQqe6ACemVTNNOcq7Ql4A", 2563 "images": [ 2564 { 2565 "height": 640, 2566 "url": "https://i.scdn.co/image/ab67616d0000b273be165dd0973a0db9607b3938", 2567 "width": 640 2568 }, 2569 { 2570 "height": 300, 2571 "url": "https://i.scdn.co/image/ab67616d00001e02be165dd0973a0db9607b3938", 2572 "width": 300 2573 }, 2574 { 2575 "height": 64, 2576 "url": "https://i.scdn.co/image/ab67616d00004851be165dd0973a0db9607b3938", 2577 "width": 64 2578 } 2579 ], 2580 "is_playable": true, 2581 "name": "The Giver (Reprise)", 2582 "release_date": "2015-03-17", 2583 "release_date_precision": "day", 2584 "total_tracks": 1, 2585 "type": "album", 2586 "uri": "spotify:album:4SQqe6ACemVTNNOcq7Ql4A" 2587 }, 2588 "artists": [ 2589 { 2590 "external_urls": { 2591 "spotify": "https://open.spotify.com/artist/61lyPtntblHJvA7FMMhi7E" 2592 }, 2593 "href": "https://api.spotify.com/v1/artists/61lyPtntblHJvA7FMMhi7E", 2594 "id": "61lyPtntblHJvA7FMMhi7E", 2595 "name": "Duke Dumont", 2596 "type": "artist", 2597 "uri": "spotify:artist:61lyPtntblHJvA7FMMhi7E" 2598 } 2599 ], 2600 "disc_number": 1, 2601 "duration_ms": 195756, 2602 "explicit": false, 2603 "external_ids": { 2604 "isrc": "GBUM71501517" 2605 }, 2606 "external_urls": { 2607 "spotify": "https://open.spotify.com/track/0ccSl4LZ7dksMNmJgkN7NO" 2608 }, 2609 "href": "https://api.spotify.com/v1/tracks/0ccSl4LZ7dksMNmJgkN7NO", 2610 "id": "0ccSl4LZ7dksMNmJgkN7NO", 2611 "is_local": false, 2612 "is_playable": true, 2613 "name": "The Giver (Reprise)", 2614 "popularity": 43, 2615 "preview_url": null, 2616 "track_number": 1, 2617 "type": "track", 2618 "uri": "spotify:track:0ccSl4LZ7dksMNmJgkN7NO" 2619 }, 2620 { 2621 "album": { 2622 "album_type": "ALBUM", 2623 "artists": [ 2624 { 2625 "external_urls": { 2626 "spotify": "https://open.spotify.com/artist/4NHQUGzhtTLFvgF5SZesLK" 2627 }, 2628 "href": "https://api.spotify.com/v1/artists/4NHQUGzhtTLFvgF5SZesLK", 2629 "id": "4NHQUGzhtTLFvgF5SZesLK", 2630 "name": "Tove Lo", 2631 "type": "artist", 2632 "uri": "spotify:artist:4NHQUGzhtTLFvgF5SZesLK" 2633 } 2634 ], 2635 "external_urls": { 2636 "spotify": "https://open.spotify.com/album/1tuekzsMZQOuiMejKP6t2Y" 2637 }, 2638 "href": "https://api.spotify.com/v1/albums/1tuekzsMZQOuiMejKP6t2Y", 2639 "id": "1tuekzsMZQOuiMejKP6t2Y", 2640 "images": [ 2641 { 2642 "height": 640, 2643 "url": "https://i.scdn.co/image/ab67616d0000b2739f0c014998bac13d3181474c", 2644 "width": 640 2645 }, 2646 { 2647 "height": 300, 2648 "url": "https://i.scdn.co/image/ab67616d00001e029f0c014998bac13d3181474c", 2649 "width": 300 2650 }, 2651 { 2652 "height": 64, 2653 "url": "https://i.scdn.co/image/ab67616d000048519f0c014998bac13d3181474c", 2654 "width": 64 2655 } 2656 ], 2657 "is_playable": true, 2658 "name": "Lady Wood", 2659 "release_date": "2016-10-28", 2660 "release_date_precision": "day", 2661 "total_tracks": 12, 2662 "type": "album", 2663 "uri": "spotify:album:1tuekzsMZQOuiMejKP6t2Y" 2664 }, 2665 "artists": [ 2666 { 2667 "external_urls": { 2668 "spotify": "https://open.spotify.com/artist/4NHQUGzhtTLFvgF5SZesLK" 2669 }, 2670 "href": "https://api.spotify.com/v1/artists/4NHQUGzhtTLFvgF5SZesLK", 2671 "id": "4NHQUGzhtTLFvgF5SZesLK", 2672 "name": "Tove Lo", 2673 "type": "artist", 2674 "uri": "spotify:artist:4NHQUGzhtTLFvgF5SZesLK" 2675 } 2676 ], 2677 "disc_number": 1, 2678 "duration_ms": 234474, 2679 "explicit": true, 2680 "external_ids": { 2681 "isrc": "SEUM71601201" 2682 }, 2683 "external_urls": { 2684 "spotify": "https://open.spotify.com/track/073A1FsNWqMxmdcRMeU57t" 2685 }, 2686 "href": "https://api.spotify.com/v1/tracks/073A1FsNWqMxmdcRMeU57t", 2687 "id": "073A1FsNWqMxmdcRMeU57t", 2688 "is_local": false, 2689 "is_playable": true, 2690 "name": "Don’t Talk About It", 2691 "popularity": 39, 2692 "preview_url": null, 2693 "track_number": 8, 2694 "type": "track", 2695 "uri": "spotify:track:073A1FsNWqMxmdcRMeU57t" 2696 }, 2697 { 2698 "album": { 2699 "album_type": "SINGLE", 2700 "artists": [ 2701 { 2702 "external_urls": { 2703 "spotify": "https://open.spotify.com/artist/6y02TEMv71ArWB2qhIaQ5m" 2704 }, 2705 "href": "https://api.spotify.com/v1/artists/6y02TEMv71ArWB2qhIaQ5m", 2706 "id": "6y02TEMv71ArWB2qhIaQ5m", 2707 "name": "Kaiydo", 2708 "type": "artist", 2709 "uri": "spotify:artist:6y02TEMv71ArWB2qhIaQ5m" 2710 } 2711 ], 2712 "external_urls": { 2713 "spotify": "https://open.spotify.com/album/0oeJCHQwiz5dQhMuiFlhhM" 2714 }, 2715 "href": "https://api.spotify.com/v1/albums/0oeJCHQwiz5dQhMuiFlhhM", 2716 "id": "0oeJCHQwiz5dQhMuiFlhhM", 2717 "images": [ 2718 { 2719 "height": 640, 2720 "url": "https://i.scdn.co/image/ab67616d0000b2731927f0378a81361fb80e20d6", 2721 "width": 640 2722 }, 2723 { 2724 "height": 300, 2725 "url": "https://i.scdn.co/image/ab67616d00001e021927f0378a81361fb80e20d6", 2726 "width": 300 2727 }, 2728 { 2729 "height": 64, 2730 "url": "https://i.scdn.co/image/ab67616d000048511927f0378a81361fb80e20d6", 2731 "width": 64 2732 } 2733 ], 2734 "is_playable": true, 2735 "name": "Fruit Punch", 2736 "release_date": "2016-08-12", 2737 "release_date_precision": "day", 2738 "total_tracks": 1, 2739 "type": "album", 2740 "uri": "spotify:album:0oeJCHQwiz5dQhMuiFlhhM" 2741 }, 2742 "artists": [ 2743 { 2744 "external_urls": { 2745 "spotify": "https://open.spotify.com/artist/6y02TEMv71ArWB2qhIaQ5m" 2746 }, 2747 "href": "https://api.spotify.com/v1/artists/6y02TEMv71ArWB2qhIaQ5m", 2748 "id": "6y02TEMv71ArWB2qhIaQ5m", 2749 "name": "Kaiydo", 2750 "type": "artist", 2751 "uri": "spotify:artist:6y02TEMv71ArWB2qhIaQ5m" 2752 } 2753 ], 2754 "disc_number": 1, 2755 "duration_ms": 221622, 2756 "explicit": true, 2757 "external_ids": { 2758 "isrc": "TCACR1606159" 2759 }, 2760 "external_urls": { 2761 "spotify": "https://open.spotify.com/track/0lpRp9KzzMbC0w1uiL7H7f" 2762 }, 2763 "href": "https://api.spotify.com/v1/tracks/0lpRp9KzzMbC0w1uiL7H7f", 2764 "id": "0lpRp9KzzMbC0w1uiL7H7f", 2765 "is_local": false, 2766 "is_playable": true, 2767 "name": "Fruit Punch", 2768 "popularity": 48, 2769 "preview_url": "https://p.scdn.co/mp3-preview/62b43baeb1993236dfc7e78557996514f64a3d9f?cid=b800bf59070c4b52a8917d2230d25bdc", 2770 "track_number": 1, 2771 "type": "track", 2772 "uri": "spotify:track:0lpRp9KzzMbC0w1uiL7H7f" 2773 }, 2774 { 2775 "album": { 2776 "album_type": "SINGLE", 2777 "artists": [ 2778 { 2779 "external_urls": { 2780 "spotify": "https://open.spotify.com/artist/21dooacK2WGBB5amYvKyfM" 2781 }, 2782 "href": "https://api.spotify.com/v1/artists/21dooacK2WGBB5amYvKyfM", 2783 "id": "21dooacK2WGBB5amYvKyfM", 2784 "name": "Smokepurpp", 2785 "type": "artist", 2786 "uri": "spotify:artist:21dooacK2WGBB5amYvKyfM" 2787 } 2788 ], 2789 "external_urls": { 2790 "spotify": "https://open.spotify.com/album/2TeGNWZYaj9yeM0mjiO8zL" 2791 }, 2792 "href": "https://api.spotify.com/v1/albums/2TeGNWZYaj9yeM0mjiO8zL", 2793 "id": "2TeGNWZYaj9yeM0mjiO8zL", 2794 "images": [ 2795 { 2796 "height": 640, 2797 "url": "https://i.scdn.co/image/ab67616d0000b273069c2ee3bae4205c1fed2981", 2798 "width": 640 2799 }, 2800 { 2801 "height": 300, 2802 "url": "https://i.scdn.co/image/ab67616d00001e02069c2ee3bae4205c1fed2981", 2803 "width": 300 2804 }, 2805 { 2806 "height": 64, 2807 "url": "https://i.scdn.co/image/ab67616d00004851069c2ee3bae4205c1fed2981", 2808 "width": 64 2809 } 2810 ], 2811 "is_playable": true, 2812 "name": "Nephew (feat. Lil Pump)", 2813 "release_date": "2018-07-27", 2814 "release_date_precision": "day", 2815 "total_tracks": 1, 2816 "type": "album", 2817 "uri": "spotify:album:2TeGNWZYaj9yeM0mjiO8zL" 2818 }, 2819 "artists": [ 2820 { 2821 "external_urls": { 2822 "spotify": "https://open.spotify.com/artist/21dooacK2WGBB5amYvKyfM" 2823 }, 2824 "href": "https://api.spotify.com/v1/artists/21dooacK2WGBB5amYvKyfM", 2825 "id": "21dooacK2WGBB5amYvKyfM", 2826 "name": "Smokepurpp", 2827 "type": "artist", 2828 "uri": "spotify:artist:21dooacK2WGBB5amYvKyfM" 2829 }, 2830 { 2831 "external_urls": { 2832 "spotify": "https://open.spotify.com/artist/3wyVrVrFCkukjdVIdirGVY" 2833 }, 2834 "href": "https://api.spotify.com/v1/artists/3wyVrVrFCkukjdVIdirGVY", 2835 "id": "3wyVrVrFCkukjdVIdirGVY", 2836 "name": "Lil Pump", 2837 "type": "artist", 2838 "uri": "spotify:artist:3wyVrVrFCkukjdVIdirGVY" 2839 } 2840 ], 2841 "disc_number": 1, 2842 "duration_ms": 202962, 2843 "explicit": true, 2844 "external_ids": { 2845 "isrc": "USUM71810095" 2846 }, 2847 "external_urls": { 2848 "spotify": "https://open.spotify.com/track/67KvputYgCRghvDQYj5FMq" 2849 }, 2850 "href": "https://api.spotify.com/v1/tracks/67KvputYgCRghvDQYj5FMq", 2851 "id": "67KvputYgCRghvDQYj5FMq", 2852 "is_local": false, 2853 "is_playable": true, 2854 "linked_from": { 2855 "external_urls": { 2856 "spotify": "https://open.spotify.com/track/0ibOcbkp2XG46Do8jcy0bL" 2857 }, 2858 "href": "https://api.spotify.com/v1/tracks/0ibOcbkp2XG46Do8jcy0bL", 2859 "id": "0ibOcbkp2XG46Do8jcy0bL", 2860 "type": "track", 2861 "uri": "spotify:track:0ibOcbkp2XG46Do8jcy0bL" 2862 }, 2863 "name": "Nephew (feat. Lil Pump)", 2864 "popularity": 50, 2865 "preview_url": "https://p.scdn.co/mp3-preview/bc901043dd4bcb1c43f6e7b1642e2d9d93b9539a?cid=b800bf59070c4b52a8917d2230d25bdc", 2866 "track_number": 1, 2867 "type": "track", 2868 "uri": "spotify:track:67KvputYgCRghvDQYj5FMq" 2869 }, 2870 { 2871 "album": { 2872 "album_type": "ALBUM", 2873 "artists": [ 2874 { 2875 "external_urls": { 2876 "spotify": "https://open.spotify.com/artist/5xtqw2B8z8JGfDYi2eAZHI" 2877 }, 2878 "href": "https://api.spotify.com/v1/artists/5xtqw2B8z8JGfDYi2eAZHI", 2879 "id": "5xtqw2B8z8JGfDYi2eAZHI", 2880 "name": "Sonique", 2881 "type": "artist", 2882 "uri": "spotify:artist:5xtqw2B8z8JGfDYi2eAZHI" 2883 } 2884 ], 2885 "external_urls": { 2886 "spotify": "https://open.spotify.com/album/4LX27S3cszKEJW84BGa1Ff" 2887 }, 2888 "href": "https://api.spotify.com/v1/albums/4LX27S3cszKEJW84BGa1Ff", 2889 "id": "4LX27S3cszKEJW84BGa1Ff", 2890 "images": [ 2891 { 2892 "height": 640, 2893 "url": "https://i.scdn.co/image/ab67616d0000b2732b30281b18d808c60135837c", 2894 "width": 640 2895 }, 2896 { 2897 "height": 300, 2898 "url": "https://i.scdn.co/image/ab67616d00001e022b30281b18d808c60135837c", 2899 "width": 300 2900 }, 2901 { 2902 "height": 64, 2903 "url": "https://i.scdn.co/image/ab67616d000048512b30281b18d808c60135837c", 2904 "width": 64 2905 } 2906 ], 2907 "is_playable": true, 2908 "name": "Hear My Cry", 2909 "release_date": "2000-08-24", 2910 "release_date_precision": "day", 2911 "total_tracks": 13, 2912 "type": "album", 2913 "uri": "spotify:album:4LX27S3cszKEJW84BGa1Ff" 2914 }, 2915 "artists": [ 2916 { 2917 "external_urls": { 2918 "spotify": "https://open.spotify.com/artist/5xtqw2B8z8JGfDYi2eAZHI" 2919 }, 2920 "href": "https://api.spotify.com/v1/artists/5xtqw2B8z8JGfDYi2eAZHI", 2921 "id": "5xtqw2B8z8JGfDYi2eAZHI", 2922 "name": "Sonique", 2923 "type": "artist", 2924 "uri": "spotify:artist:5xtqw2B8z8JGfDYi2eAZHI" 2925 } 2926 ], 2927 "disc_number": 1, 2928 "duration_ms": 240866, 2929 "explicit": false, 2930 "external_ids": { 2931 "isrc": "USUR10300602" 2932 }, 2933 "external_urls": { 2934 "spotify": "https://open.spotify.com/track/4Y8q64VnhD0vFYy9g2WFpi" 2935 }, 2936 "href": "https://api.spotify.com/v1/tracks/4Y8q64VnhD0vFYy9g2WFpi", 2937 "id": "4Y8q64VnhD0vFYy9g2WFpi", 2938 "is_local": false, 2939 "is_playable": true, 2940 "name": "It Feels So Good", 2941 "popularity": 67, 2942 "preview_url": "https://p.scdn.co/mp3-preview/028068f83d3d988b1661c03e93ab0651f6dd5957?cid=b800bf59070c4b52a8917d2230d25bdc", 2943 "track_number": 1, 2944 "type": "track", 2945 "uri": "spotify:track:4Y8q64VnhD0vFYy9g2WFpi" 2946 } 2947 ], 2948 "seeds": [ 2949 { 2950 "initialPoolSize": 378, 2951 "afterFilteringSize": 378, 2952 "afterRelinkingSize": 378, 2953 "id": "4NHQUGzhtTLFvgF5SZesLK", 2954 "type": "ARTIST", 2955 "href": "https://api.spotify.com/v1/artists/4NHQUGzhtTLFvgF5SZesLK" 2956 }, 2957 { 2958 "initialPoolSize": 400, 2959 "afterFilteringSize": 400, 2960 "afterRelinkingSize": 400, 2961 "id": "0c6xIDDpzE81m2q797ordA", 2962 "type": "TRACK", 2963 "href": "https://api.spotify.com/v1/tracks/0c6xIDDpzE81m2q797ordA" 2964 }, 2965 { 2966 "initialPoolSize": 404, 2967 "afterFilteringSize": 404, 2968 "afterRelinkingSize": 404, 2969 "id": "classical", 2970 "type": "GENRE", 2971 "href": null 2972 }, 2973 { 2974 "initialPoolSize": 369, 2975 "afterFilteringSize": 369, 2976 "afterRelinkingSize": 369, 2977 "id": "hip-hop", 2978 "type": "GENRE", 2979 "href": null 2980 } 2981 ] 2982} 2983"""
46def get_genres(debug = True): 47 """ 48 Asks Spotify for a list of available genres. There are actually 6000+ valid 49 genres on Spotify [all of which can be found here](https://everynoise.com/everynoise1d.html) 50 but for our purposes it might be wise to limit yourself to just these. 51 52 Args: 53 debug (`bool`): whether or not you want the debug messages to be printed. 54 55 Returns: 56 a `list` of `str` representing valid genres. 57 """ 58 return [ 59 "alternative", "ambient", "blues", 60 "chill", "country", "dance", "electronic", "folk", 61 "funk", "happy", "hip-hop", "indie-pop", "jazz", "k-pop", "metal", 62 "new-release", "pop", "punk", "reggae", "rock", 63 "soul", "study", "trance", "work-out", "world-music" 64 ]
Asks Spotify for a list of available genres. There are actually 6000+ valid genres on Spotify all of which can be found here but for our purposes it might be wise to limit yourself to just these.
Arguments:
- debug (
bool
): whether or not you want the debug messages to be printed.
Returns:
a
list
ofstr
representing valid genres.
157def search_for_artists(search_term , debug = True, simplify = True): 158 """ 159 Retrieves a list of Spotify artists, given the search term passed in. 160 161 Args: 162 search_term (`str`): A search term (for an artist), represented as a string. 163 debug (`bool`): Whether or not you want debug text to be printed. 164 simplify (`bool`): Indicates whether you want to simplify the data that is returned. 165 166 Returns: 167 a `list` of artists. 168 """ 169 url = "https://api.spotify.com/v1/search?q=" + search_term + "&type=artist&market=US" 170 data = _issue_get_request(url, debug=debug) 171 if not simplify 172 return data 173 return _simplify_artists(data["artists"]["items"], debug=debug)
Retrieves a list of Spotify artists, given the search term passed in.
Arguments:
- search_term (
str
): A search term (for an artist), represented as a string. - debug (
bool
): Whether or not you want debug text to be printed. - simplify (
bool
): Indicates whether you want to simplify the data that is returned.
Returns:
a
list
of artists.
67def search_for_tracks(search_term , debug = True, simplify = True): 68 """ 69 Retrieves a list of Spotify tracks, given the search term passed in. 70 71 Args: 72 search_term (`str`): A search term (for a song), represented as a string. 73 debug (`bool`): Whether or not you want debug text to be printed. 74 simplify (`bool`): Indicates whether you want to simplify the data that is returned. 75 76 Returns: 77 a `list` of tracks. 78 """ 79 url = "https://api.spotify.com/v1/search?q=" + search_term + "&type=track" 80 data = _issue_get_request(url, debug=debug) 81 if not simplify 82 return data 83 return _simplify_tracks(data["tracks"]["items"], debug=debug)
Retrieves a list of Spotify tracks, given the search term passed in.
Arguments:
- search_term (
str
): A search term (for a song), represented as a string. - debug (
bool
): Whether or not you want debug text to be printed. - simplify (
bool
): Indicates whether you want to simplify the data that is returned.
Returns:
a
list
of tracks.
195def search_for_playlists(search_term , debug = True, simplify = True): 196 """ 197 Retrieves a list of Spotify playlists, given the search term passed in. 198 199 Args: 200 search_term (`str`): A search term (for a song), represented as a string. 201 debug (`bool`): Whether or not you want debug text to be printed. 202 simplify (`bool`): Indicates whether you want to simplify the data that is returned. 203 204 Returns: 205 a `list` of playlists. 206 """ 207 url = "https://api.spotify.com/v1/search?q=" + search_term + "&type=playlist" 208 data = _issue_get_request(url, debug=debug) 209 if not simplify 210 return data 211 return _simplify_playlists(data["playlists"]["items"], debug=debug)
Retrieves a list of Spotify playlists, given the search term passed in.
Arguments:
- search_term (
str
): A search term (for a song), represented as a string. - debug (
bool
): Whether or not you want debug text to be printed. - simplify (
bool
): Indicates whether you want to simplify the data that is returned.
Returns:
a
list
of playlists.
176def search_for_albums(search_term , debug = True, simplify = True): 177 """ 178 Retrieves a list of Spotify albums, given the search term passed in. 179 180 Args: 181 search_term (`str`): A search term (for an album), represented as a string. 182 debug (`bool`): Whether or not you want debug text to be printed. 183 simplify (`bool`): Indicates whether you want to simplify the data that is returned. 184 185 Returns: 186 * a `list` of albums. 187 """ 188 url = "https://api.spotify.com/v1/search?q=" + search_term + "&type=album&market=US" 189 data = _issue_get_request(url, debug=debug) 190 if not simplify 191 return data 192 return _simplify_albums(data["albums"]["items"], debug=debug)
Retrieves a list of Spotify albums, given the search term passed in.
Arguments:
- search_term (
str
): A search term (for an album), represented as a string. - debug (
bool
): Whether or not you want debug text to be printed. - simplify (
bool
): Indicates whether you want to simplify the data that is returned.
Returns:
- a
list
of albums.
104def get_top_tracks_by_artist(artist_id , debug = True, simplify = True): 105 """ 106 Retrieves a list of Spotify "top tracks" by an artist 107 108 Args:a 109 artist_id (`str`): The Spotify id of the artist. 110 debug (`bool`): Whether or not you want debug text to be printed. 111 simplify (`bool`): Indicates whether you want to simplify the data that is returned. 112 113 Returns: 114 a `list` of tracks. 115 """ 116 if len(artist_id) != 22 117 raise TypeError(f"This function expects an Artist ID but you gave it {artist_id}.") 118 119 url = "https://api.spotify.com/v1/artists/" + \ 120 artist_id + "/top-tracks?market=US" 121 data = _issue_get_request(url, debug=debug) 122 if not simplify 123 return data 124 return _simplify_tracks(data["tracks"], debug=debug)
Retrieves a list of Spotify "top tracks" by an artist
Args:a
artist_id (str
): The Spotify id of the artist.
debug (bool
): Whether or not you want debug text to be printed.
simplify (bool
): Indicates whether you want to simplify the data that is returned.
Returns:
a
list
of tracks.
134def get_playlist_tracks(playlist_id , debug = True, simplify = True): 135 """ 136 Retrieves a list of the tracks associated with a playlist_id. 137 138 Args: 139 playlist_id (`str`): The id of the Spotify playlist. 140 debug (`bool`): Whether or not you want debug text to be printed. 141 simplify (`bool`): Whether you want to simplify the data that is returned. 142 143 Returns: 144 a `list` of tracks. 145 """ 146 url = "https://api.spotify.com/v1/playlists/" + playlist_id + "/tracks" 147 data = _issue_get_request(url, debug=debug) 148 if not simplify 149 return data 150 151 def get_track(item): 152 return item["track"] 153 tracks = (map(get_track, data["items"])) 154 return _simplify_tracks(tracks, debug=debug)
Retrieves a list of the tracks associated with a playlist_id.
Arguments:
- playlist_id (
str
): The id of the Spotify playlist. - debug (
bool
): Whether or not you want debug text to be printed. - simplify (
bool
): Whether you want to simplify the data that is returned.
Returns:
a
list
of tracks.
233def get_playlists_by_user(user_id , debug = True, simplify = True): 234 """ 235 Retrieves a list of Spotify playlists belonging to a particular user. 236 237 Args: 238 user_id (`str`): A valid Spotify user id, represented as a string. 239 debug (`bool`): Whether or not you want debug text to be printed. 240 simplify (`bool`): Indicates whether you want to simplify the data that is returned. 241 242 Returns: 243 a `list` of playlists belonging to the user. 244 """ 245 url = "https://api.spotify.com/v1/users/" + user_id + "/playlists" 246 data = _issue_get_request(url, debug=debug) 247 if not simplify 248 return data 249 return _simplify_playlists(data["items"], debug=debug)
Retrieves a list of Spotify playlists belonging to a particular user.
Arguments:
- user_id (
str
): A valid Spotify user id, represented as a string. - debug (
bool
): Whether or not you want debug text to be printed. - simplify (
bool
): Indicates whether you want to simplify the data that is returned.
Returns:
a
list
of playlists belonging to the user.
252def get_albums_by_artist(artist_id, options = [], debug = True, simplify = True): 253 """ 254 Retrieves a list of Spotify albums by a given artist. 255 256 Args: 257 artist_id (`str`): A valid Spotify artist ID 258 options (`list`): A list of special strings to indicate what sort of albums to look for. Valid 259 items are: `"album"`, `"single"`, "`compilation`", and `"appears_on"`. 260 debug (`bool`): Whether or not you want debug text to be printed. 261 simplify (`bool`): Indicates whether you want to simplify the data that is returned. 262 263 Returns: 264 * a `list` of albums. 265 """ 266 267 _verify_artist_ids([artist_id]) 268 269 valid_options = ["album", "single", "compilation", "appears_on"] 270 for option in options 271 if option not in valid_options 272 raise TypeError(f"The options input only accepts the following options: {valid_options}") 273 274 if options 275 url = f"https://api.spotify.com/v1/artists/{artist_id}/albums?include_groups={",".join(options)}&market=US" 276 else 277 url = f"https://api.spotify.com/v1/artists/{artist_id}/albums?market=US" 278 data = _issue_get_request(url, debug=debug) 279 if not simplify 280 return data 281 return _simplify_albums(data["items"], debug=debug)
Retrieves a list of Spotify albums by a given artist.
Arguments:
- artist_id (
str
): A valid Spotify artist ID - options (
list
): A list of special strings to indicate what sort of albums to look for. Valid items are:"album"
,"single"
, "compilation
", and"appears_on"
. - debug (
bool
): Whether or not you want debug text to be printed. - simplify (
bool
): Indicates whether you want to simplify the data that is returned.
Returns:
- a
list
of albums.
214def get_new_releases(debug = True, simplify = True): 215 """ 216 Retrieves a list of Spotify albums that are newly released. 217 218 Args: 219 debug (`bool`): Whether or not you want debug text to be printed. 220 simplify (`bool`): Indicates whether you want to simplify the data that is returned. 221 222 Returns: 223 * a `list` of albums. 224 """ 225 226 url = f"https://api.spotify.com/v1/search?q=tag:new&type=album&market=US&offset=0" 227 data = _issue_get_request(url, debug=debug) 228 if not simplify 229 return data 230 return _simplify_albums(data["albums"]["items"], debug=debug)
Retrieves a list of Spotify albums that are newly released.
Arguments:
- debug (
bool
): Whether or not you want debug text to be printed. - simplify (
bool
): Indicates whether you want to simplify the data that is returned.
Returns:
- a
list
of albums.
651def generate_tracks_table(tracks , to_html = False): 652 """ 653 Function that builds a string representation of a list tracks (dictionaries). 654 655 Args: 656 tracks (`list`): List of tracks. 657 to_html (`bool`): If `True` it will generate an HTML version (for an email or web page) and if `False` (default) will generate a string to print in Python. 658 659 Returns: 660 * a `str` that has a table in it for tracks 661 """ 662 663 if to_html 664 return _get_tracklist_table_html(tracks) 665 666 line_width = 95 667 text = "" 668 template = "{0:2} | {1:<22.22} | {2:<30.30} | {3:<30.30}\n" 669 670 # header section: 671 text += "-" * line_width + "\n" 672 text += template.format( 673 "", "Name", "Artist", "Album" 674 ) 675 text += "-" * line_width + "\n" 676 677 # data section: 678 counter = 1 679 for track in tracks 680 text += template.format( 681 counter, 682 track.get("name"), 683 track.get("artist").get("name"), 684 track.get("album").get("name") 685 ) 686 counter += 1 687 text += "-" * line_width + "\n" 688 return text
Function that builds a string representation of a list tracks (dictionaries).
Arguments:
- tracks (
list
): List of tracks. - to_html (
bool
): IfTrue
it will generate an HTML version (for an email or web page) and ifFalse
(default) will generate a string to print in Python.
Returns:
- a
str
that has a table in it for tracks
729def generate_artists_table(artists , to_html=False): 730 """ 731 Makes a nice formatted table of artists. Good for writing to an 732 HTML file or showing on the screen. 733 734 Args: 735 artists (`list`): A list of artists. 736 to_html (`bool`): Whether or not you want the HTML version. 737 738 Returns: 739 a `str` with an HTML table in it. 740 """ 741 if not artists 742 raise TypeError(f"A list of artists is required but you gave us a {type(artists)}: {artists}") 743 744 if to_html 745 return _get_artist_table_html(artists) 746 747 line_width = 118 748 text = "" 749 template = "{0:2} | {1:<22.22} | {2:<30.30} | {3:<60.60}\n" 750 751 # header section: 752 text += "-" * line_width + "\n" 753 text += template.format( 754 "", "Name", "Genres", "URL" 755 ) 756 text += "-" * line_width + "\n" 757 758 # data section: 759 counter = 1 760 for artist in artists 761 text += template.format( 762 counter, 763 artist.get("name"), 764 artist.get("genres"), 765 artist.get("share_url") 766 ) 767 counter += 1 768 text += "-" * line_width + "\n" 769 return text
Makes a nice formatted table of artists. Good for writing to an HTML file or showing on the screen.
Arguments:
- artists (
list
): A list of artists. - to_html (
bool
): Whether or not you want the HTML version.
Returns:
a
str
with an HTML table in it.
691def generate_albums_table(albums , to_html = False): 692 """ 693 Function that builds a string representation of a list of albums (dicitonaries). 694 695 Args: 696 albums (`list`): list of albums (dictionaries). 697 to_html (`bool`): If `True` it will generate an HTML version (for an email or web page) and if `False` (default) will generate a string to print in Python. 698 699 Returns: 700 * a `str` that has a table in it 701 """ 702 703 if to_html 704 return _get_album_table_html(albums) 705 706 line_width = 95 707 text = "" 708 template = "{0:2} | {1:<30.30} | {2:<30.30} | {3:<22.22}\n" 709 710 # header section: 711 text += "-" * line_width + "\n" 712 text += template.format("", "Name", "Primary Artist", "Release Date") 713 text += "-" * line_width + "\n" 714 715 # data section: 716 counter = 1 717 for album in albums 718 text += template.format( 719 counter, 720 album.get("name"), 721 album.get("artist").get("name"), 722 album.get("release_date"), 723 ) 724 counter += 1 725 text += "-" * line_width + "\n" 726 return text
Function that builds a string representation of a list of albums (dicitonaries).
Arguments:
- albums (
list
): list of albums (dictionaries). - to_html (
bool
): IfTrue
it will generate an HTML version (for an email or web page) and ifFalse
(default) will generate a string to print in Python.
Returns:
- a
str
that has a table in it
344def generate_mixtape(artist_ids = [], track_ids = [], genres = [], length = 20, debug = True, practice = True, simplify = True): 345 """ 346 Generate a mixtape based off the inputted artists, tracks, and genres. You must provide at least 1 input across artists, tracks, and genres. 347 <mark>Keep in mind it does not accept artist names or track names.</mark> Specifying multiple genres might result in getting zero results.</mark> 348 349 Note: In practice mode, this function will print a warning if inputs are invalid but will still return data. 350 351 Args: 352 artist_ids (`list`): A list of artist ids (list of strings). Example: `[ "06HL4z0CvFAxyc27GXpf02", "3Nrfpe0tUJi4K4DXYWgMUX" ]` 353 track_ids (`list`): A list of track ids. Example: `[ "5ZBeML7Lf3FMEVviTyvi8l", "29U7stRjqHU6rMiS8BfaI9" ]` 354 genres (`list`): A list of genres. Example: `[ "chill" ]` 355 length (`int`): How many tracks to return as part of the mixtape 356 debug (`bool`): Whether or not you want debug text to be printed. 357 practice (`bool`): Whether or not you want real data <mark>ONLY DO THIS IF YOU HAVE SUCCESSFULLY GOTTEN PRACTICE DATA WORKING</mark> 358 359 Returns: 360 * a `list` of tracks (dictionaries) 361 """ 362 363 if not artist_ids and not track_ids and not genres 364 if practice 365 print(f"PRACTICE MODE: Note, no inputs were given.") 366 else 367 raise Exception("Either artist_ids or track_ids or genres required") 368 369 if practice 370 return _simplify_tracks(json.loads(TRACKS_JSON)["tracks"]) 371 372 _verify_artist_ids(artist_ids, practice=practice) 373 _verify_track_ids(track_ids, practice=practice) 374 375 track_list = [] 376 377 for artist in artist_ids 378 track_list += _get_top_tracks_by_artist(artist_id=artist, debug=debug, simplify=simplify) 379 380 if track_ids 381 track_list += _get_several_tracks(track_ids, debug=debug, simplify=simplify) 382 383 if genres 384 track_list += _search_by_genres(genres, debug=debug, simplify=simplify) 385 386 track_list = _remove_duplicate_tracks(track_list) 387 random.shuffle(track_list) 388 389 return track_list[:length]
Generate a mixtape based off the inputted artists, tracks, and genres. You must provide at least 1 input across artists, tracks, and genres. Keep in mind it does not accept artist names or track names. Specifying multiple genres might result in getting zero results.
Note: In practice mode, this function will print a warning if inputs are invalid but will still return data.
Arguments:
- artist_ids (
list
): A list of artist ids (list of strings). Example:[ '06HL4z0CvFAxyc27GXpf02', '3Nrfpe0tUJi4K4DXYWgMUX' ]
- track_ids (
list
): A list of track ids. Example:[ '5ZBeML7Lf3FMEVviTyvi8l', '29U7stRjqHU6rMiS8BfaI9' ]
- genres (
list
): A list of genres. Example:[ 'chill' ]
- length (
int
): How many tracks to return as part of the mixtape - debug (
bool
): Whether or not you want debug text to be printed. - practice (
bool
): Whether or not you want real data ONLY DO THIS IF YOU HAVE SUCCESSFULLY GOTTEN PRACTICE DATA WORKING
Returns:
- a
list
of tracks (dictionaries)