apis.audio
1VERSION = 2026.1 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 shoud_i_remove = False 920 if os.path.isfile(SPOTIFY_JSON): 921 with open(SPOTIFY_JSON) as json_file 922 data = json.load(json_file) 923 if time.time() >= float(data["expires"]): 924 if debug 925 print(f"DEBUG - {"_generate_authentication_header"}:\n Auth token expired, time to refresh!") 926 should_i_remove = True 927 else 928 token = data["token"] 929 not_cached = False 930 931 if shoud_i_remove 932 time.sleep(0.2) 933 os.remove(SPOTIFY_JSON) 934 935 if not_cached 936 if debug 937 print(f"DEBUG - {"_generate_authentication_header"}:\n Generating new Spotify Authentication Token...") 938 from apis import secret_tokens 939 940 client_id = secret_tokens.SPOTIFY_CLIENT_ID 941 client_secret = secret_tokens.SPOTIFY_CLIENT_SECRET 942 943 # Step 1 - Authorization 944 auth_url = "https://accounts.spotify.com/api/token" 945 headers = {} 946 data = {} 947 948 # Encode as Base64 949 message = f"{client_id}:{client_secret}" 950 messageBytes = message.encode("ascii") 951 base64Bytes = base64.b64encode(messageBytes) 952 base64Message = base64Bytes.decode("ascii") 953 954 headers["Authorization"] = f"Basic {base64Message}" 955 data["grant_type"] = "client_credentials" 956 957 if not backup 958 try 959 r = requests.post(auth_url, headers=headers, data=data) 960 token = r.json()["access_token"] 961 with open(SPOTIFY_JSON, "w") as outfile 962 json_object = json.dumps({ "token" token, "expires" time.time() + 3540}, indent=4) 963 outfile.write(json_object) 964 965 except Exception as e 966 if debug 967 print(f"DEBUG - {"_generate_authentication_header"}:\nCouldn"t use either default API Key. Trying backup!") 968 raise Exception(e) 969 970 headers = { 971 "Authorization" "Bearer " + token 972 } 973 974 return headers 975 976def _issue_get_request(url, debug = True, practice = True): 977 """ 978 Private function. Retrieves data from any Spotify endpoint using the authentication key. 979 980 * url (str): The API Endpoint + query parameters. 981 * debug (bool): Whether or not to print debug messages. 982 * practice (bool): Whether or not to return real data or practice data 983 984 Returns whatever Spotify"s API endpoint gives back. 985 """ 986 if debug 987 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.") 988 989 headers = _generate_authentication_header() 990 991 url = quote(url, safe="/:?&,=-") 992 993 # Check cache 994 if os.path.isfile(SPOTIFY_CACHE): 995 with open(SPOTIFY_CACHE, "rb") as file 996 cache = pickle.load(file) 997 else 998 cache = {} 999 1000 if url in cache 1001 if debug 1002 print(f"DEBUG - {"_issue_get_request"}:\nFound previous request! Returning cached result.") 1003 return cache[url] 1004 1005 response = requests.get(url, headers=headers, verify=True) 1006 if response.status_code == 429 1007 retry_length = response.headers["Retry-After"] 1008 if debug 1009 print(f"ERROR: Spotify API is overloaded! It asked us to try again in {retry_length} seconds.") 1010 1011 if os.path.isfile(SPOTIFY_JSON): 1012 os.remove(SPOTIFY_JSON) 1013 1014 if debug 1015 print(f"DEBUG - {"_issue_get_request"}:\nWe"re going to try to use the backup...") 1016 headers = _generate_authentication_header(backup=True) 1017 response = requests.get(url, headers=headers, verify=True) 1018 if response.status_code != 200 1019 raise Exception("Uh oh. Couldn"t use the backup either. It"s likely you"ve given this function invalid inputs!") 1020 1021 json_response = response.json() 1022 1023 # Save it in the cache 1024 cache[url] = json_response 1025 1026 with open(SPOTIFY_CACHE, "wb") as file 1027 pickle.dump(cache, file) 1028 1029 return json_response 1030 1031def _simplify_tracks(tracks , debug=True): 1032 """ 1033 Private function. Simplifies the Spotify track data so that each dictionary only contains 1034 a few key features (the original dictionary is quite large). 1035 1036 * tracks (list): The original tracks data structure returned from Spotify. 1037 1038 Returns a list of simplified tracks. 1039 """ 1040 try 1041 tracks[0] 1042 except Exception 1043 return tracks 1044 1045 simplified = [] 1046 for item in tracks 1047 track = { 1048 "id" item["id"], 1049 "name" item["name"], 1050 "preview_url" item["preview_url"], 1051 "share_url" "https://open.spotify.com/track/" + item["id"] 1052 } 1053 try 1054 track["album"] = { 1055 "id" item["album"]["id"], 1056 "name" item["album"]["name"], 1057 "image_url" item["album"]["images"][0]["url"], 1058 "image_url_small" item["album"]["images"][-1]["url"], 1059 "share_url" "https://open.spotify.com/album/" + item["album"]["id"] 1060 } 1061 except Exception 1062 pass 1063 try 1064 artists = item.get("album").get("artists") 1065 artist = artists[0] 1066 track["artist"] = { 1067 "id" artist["id"], 1068 "name" artist["name"], 1069 "share_url" "https://open.spotify.com/artist/" + item["album"]["artists"][0]["id"] 1070 } 1071 except Exception 1072 pass 1073 simplified.append(track) 1074 return simplified 1075 1076 1077def _simplify_artists(artists , debug=True): 1078 """ 1079 Private function. Simplifies the Spotify artist data so that each dictionary only contains 1080 a few key features (the original dictionary is quite large). 1081 1082 * artists (list): The original artists data structure returned from Spotify. 1083 1084 Returns a list of simplified artists. 1085 """ 1086 try 1087 artists[0] 1088 except Exception 1089 return artists 1090 1091 simplified = [] 1092 for item in artists 1093 artist = { 1094 "id" item["id"], 1095 "name" item["name"], 1096 "genres" ", ".join(item["genres"]), 1097 "share_url" "https://open.spotify.com/artist/" + item["id"] 1098 } 1099 try 1100 artist["image_url"] = item["images"][0]["url"] 1101 artist["image_url_small"] = item["images"][-1]["url"] 1102 except Exception 1103 pass 1104 simplified.append(artist) 1105 return simplified 1106 1107def _simplify_albums(albums , debug=True): 1108 """ 1109 Private function. Simplifies the Spotify artist data so that each dictionary only contains 1110 a few key features (the original dictionary is quite large). 1111 1112 * artists (list): The original artists data structure returned from Spotify. 1113 1114 Returns a list of simplified artists. 1115 """ 1116 try 1117 albums[0] 1118 except Exception 1119 return albums 1120 1121 simplified = [] 1122 for item in albums 1123 album = { 1124 "id" item["id"], 1125 "name" item["name"], 1126 "release_date" item["release_date"], 1127 "artist" item["artists"][0], 1128 "share_url" "https://open.spotify.com/album/" + item["id"] 1129 } 1130 try 1131 album["image_url"] = item["images"][0]["url"] 1132 album["image_url_small"] = item["images"][-1]["url"] 1133 except Exception 1134 pass 1135 simplified.append(album) 1136 return simplified 1137 1138def _simplify_playlists(playlists , debug=True): 1139 """ 1140 Private function. Simplifies the Spotify playlist data so that each dictionary only contains 1141 a few key features (the original dictionary is quite large). 1142 1143 * playlists (list): The original playlist data structure returned from Spotify. 1144 1145 Returns a list of simplified playlist entries. 1146 """ 1147 try 1148 simplified = [] 1149 for item in playlists 1150 # Winter 25 - some playlists come back as None 1151 if item is None 1152 continue 1153 simplified.append({ 1154 "id" item["id"], 1155 "name" item["name"], 1156 "owner_display_name" item["owner"]["display_name"], 1157 "owner_id" item["owner"]["id"], 1158 "share_url" "https://open.spotify.com/playlist/" + item["id"], 1159 "num_tracks" item["tracks"]["total"] 1160 }) 1161 return simplified 1162 except Exception as e 1163 raise Exception( 1164 f"The following playlist data structure could not be flattened:\n{playlists}" 1165 ) 1166 1167 1168TRACKS_JSON = """ 1169{ 1170 "tracks": [ 1171 { 1172 "album": { 1173 "album_type": "ALBUM", 1174 "artists": [ 1175 { 1176 "external_urls": { 1177 "spotify": "https://open.spotify.com/artist/6UE7nl9mha6s8z0wFQFIZ2" 1178 }, 1179 "href": "https://api.spotify.com/v1/artists/6UE7nl9mha6s8z0wFQFIZ2", 1180 "id": "6UE7nl9mha6s8z0wFQFIZ2", 1181 "name": "Robyn", 1182 "type": "artist", 1183 "uri": "spotify:artist:6UE7nl9mha6s8z0wFQFIZ2" 1184 } 1185 ], 1186 "external_urls": { 1187 "spotify": "https://open.spotify.com/album/0le9TO3kU69m6iWHTjNs9Y" 1188 }, 1189 "href": "https://api.spotify.com/v1/albums/0le9TO3kU69m6iWHTjNs9Y", 1190 "id": "0le9TO3kU69m6iWHTjNs9Y", 1191 "images": [ 1192 { 1193 "height": 640, 1194 "url": "https://i.scdn.co/image/ab67616d0000b2734689597708c1b0b3dcb9ecaf", 1195 "width": 640 1196 }, 1197 { 1198 "height": 300, 1199 "url": "https://i.scdn.co/image/ab67616d00001e024689597708c1b0b3dcb9ecaf", 1200 "width": 300 1201 }, 1202 { 1203 "height": 64, 1204 "url": "https://i.scdn.co/image/ab67616d000048514689597708c1b0b3dcb9ecaf", 1205 "width": 64 1206 } 1207 ], 1208 "is_playable": true, 1209 "name": "Body Talk", 1210 "release_date": "2010-01-01", 1211 "release_date_precision": "day", 1212 "total_tracks": 15, 1213 "type": "album", 1214 "uri": "spotify:album:0le9TO3kU69m6iWHTjNs9Y" 1215 }, 1216 "artists": [ 1217 { 1218 "external_urls": { 1219 "spotify": "https://open.spotify.com/artist/6UE7nl9mha6s8z0wFQFIZ2" 1220 }, 1221 "href": "https://api.spotify.com/v1/artists/6UE7nl9mha6s8z0wFQFIZ2", 1222 "id": "6UE7nl9mha6s8z0wFQFIZ2", 1223 "name": "Robyn", 1224 "type": "artist", 1225 "uri": "spotify:artist:6UE7nl9mha6s8z0wFQFIZ2" 1226 } 1227 ], 1228 "disc_number": 1, 1229 "duration_ms": 278080, 1230 "explicit": false, 1231 "external_ids": { 1232 "isrc": "SEWKZ1000009" 1233 }, 1234 "external_urls": { 1235 "spotify": "https://open.spotify.com/track/7g13jf3zqlP5S68Voo5v9m" 1236 }, 1237 "href": "https://api.spotify.com/v1/tracks/7g13jf3zqlP5S68Voo5v9m", 1238 "id": "7g13jf3zqlP5S68Voo5v9m", 1239 "is_local": false, 1240 "is_playable": true, 1241 "linked_from": { 1242 "external_urls": { 1243 "spotify": "https://open.spotify.com/track/6aqNCrRA7vs7v6QvRpI50t" 1244 }, 1245 "href": "https://api.spotify.com/v1/tracks/6aqNCrRA7vs7v6QvRpI50t", 1246 "id": "6aqNCrRA7vs7v6QvRpI50t", 1247 "type": "track", 1248 "uri": "spotify:track:6aqNCrRA7vs7v6QvRpI50t" 1249 }, 1250 "name": "Dancing On My Own - Radio Edit", 1251 "popularity": 63, 1252 "preview_url": null, 1253 "track_number": 1, 1254 "type": "track", 1255 "uri": "spotify:track:7g13jf3zqlP5S68Voo5v9m" 1256 }, 1257 { 1258 "album": { 1259 "album_type": "COMPILATION", 1260 "artists": [ 1261 { 1262 "external_urls": { 1263 "spotify": "https://open.spotify.com/artist/0LyfQWJT6nXafLPZqxe9Of" 1264 }, 1265 "href": "https://api.spotify.com/v1/artists/0LyfQWJT6nXafLPZqxe9Of", 1266 "id": "0LyfQWJT6nXafLPZqxe9Of", 1267 "name": "Various Artists", 1268 "type": "artist", 1269 "uri": "spotify:artist:0LyfQWJT6nXafLPZqxe9Of" 1270 }, 1271 { 1272 "external_urls": { 1273 "spotify": "https://open.spotify.com/artist/2dfDjeZroUd3LWmSFrAZCD" 1274 }, 1275 "href": "https://api.spotify.com/v1/artists/2dfDjeZroUd3LWmSFrAZCD", 1276 "id": "2dfDjeZroUd3LWmSFrAZCD", 1277 "name": "David Parry", 1278 "type": "artist", 1279 "uri": "spotify:artist:2dfDjeZroUd3LWmSFrAZCD" 1280 }, 1281 { 1282 "external_urls": { 1283 "spotify": "https://open.spotify.com/artist/3PfJE6ebCbCHeuqO4BfNeA" 1284 }, 1285 "href": "https://api.spotify.com/v1/artists/3PfJE6ebCbCHeuqO4BfNeA", 1286 "id": "3PfJE6ebCbCHeuqO4BfNeA", 1287 "name": "London Philharmonic Orchestra", 1288 "type": "artist", 1289 "uri": "spotify:artist:3PfJE6ebCbCHeuqO4BfNeA" 1290 } 1291 ], 1292 "external_urls": { 1293 "spotify": "https://open.spotify.com/album/0bgjJ99UFbk0yBOzjJl7cq" 1294 }, 1295 "href": "https://api.spotify.com/v1/albums/0bgjJ99UFbk0yBOzjJl7cq", 1296 "id": "0bgjJ99UFbk0yBOzjJl7cq", 1297 "images": [ 1298 { 1299 "height": 640, 1300 "url": "https://i.scdn.co/image/ab67616d0000b27376206bd8de3a477aafd7ba83", 1301 "width": 640 1302 }, 1303 { 1304 "height": 300, 1305 "url": "https://i.scdn.co/image/ab67616d00001e0276206bd8de3a477aafd7ba83", 1306 "width": 300 1307 }, 1308 { 1309 "height": 64, 1310 "url": "https://i.scdn.co/image/ab67616d0000485176206bd8de3a477aafd7ba83", 1311 "width": 64 1312 } 1313 ], 1314 "is_playable": true, 1315 "name": "The 50 Greatest Pieces of Classical Music", 1316 "release_date": "2009-11-23", 1317 "release_date_precision": "day", 1318 "total_tracks": 50, 1319 "type": "album", 1320 "uri": "spotify:album:0bgjJ99UFbk0yBOzjJl7cq" 1321 }, 1322 "artists": [ 1323 { 1324 "external_urls": { 1325 "spotify": "https://open.spotify.com/artist/430byzy0c5bPn5opiu0SRd" 1326 }, 1327 "href": "https://api.spotify.com/v1/artists/430byzy0c5bPn5opiu0SRd", 1328 "id": "430byzy0c5bPn5opiu0SRd", 1329 "name": "Edward Elgar", 1330 "type": "artist", 1331 "uri": "spotify:artist:430byzy0c5bPn5opiu0SRd" 1332 }, 1333 { 1334 "external_urls": { 1335 "spotify": "https://open.spotify.com/artist/2dfDjeZroUd3LWmSFrAZCD" 1336 }, 1337 "href": "https://api.spotify.com/v1/artists/2dfDjeZroUd3LWmSFrAZCD", 1338 "id": "2dfDjeZroUd3LWmSFrAZCD", 1339 "name": "David Parry", 1340 "type": "artist", 1341 "uri": "spotify:artist:2dfDjeZroUd3LWmSFrAZCD" 1342 }, 1343 { 1344 "external_urls": { 1345 "spotify": "https://open.spotify.com/artist/3PfJE6ebCbCHeuqO4BfNeA" 1346 }, 1347 "href": "https://api.spotify.com/v1/artists/3PfJE6ebCbCHeuqO4BfNeA", 1348 "id": "3PfJE6ebCbCHeuqO4BfNeA", 1349 "name": "London Philharmonic Orchestra", 1350 "type": "artist", 1351 "uri": "spotify:artist:3PfJE6ebCbCHeuqO4BfNeA" 1352 } 1353 ], 1354 "disc_number": 1, 1355 "duration_ms": 345666, 1356 "explicit": false, 1357 "external_ids": { 1358 "isrc": "SEWDL9750023" 1359 }, 1360 "external_urls": { 1361 "spotify": "https://open.spotify.com/track/5nsYZFNyMefd50yamWgMfy" 1362 }, 1363 "href": "https://api.spotify.com/v1/tracks/5nsYZFNyMefd50yamWgMfy", 1364 "id": "5nsYZFNyMefd50yamWgMfy", 1365 "is_local": false, 1366 "is_playable": true, 1367 "name": "Pomp and Circumstance, Op. 39: No. 1, March in D Major", 1368 "popularity": 43, 1369 "preview_url": "https://p.scdn.co/mp3-preview/f617411fdc9c52d598ec9d5cde4baf24895598cb?cid=b800bf59070c4b52a8917d2230d25bdc", 1370 "track_number": 23, 1371 "type": "track", 1372 "uri": "spotify:track:5nsYZFNyMefd50yamWgMfy" 1373 }, 1374 { 1375 "album": { 1376 "album_type": "ALBUM", 1377 "artists": [ 1378 { 1379 "external_urls": { 1380 "spotify": "https://open.spotify.com/artist/37mtx80nMDETlbsq2eFCzc" 1381 }, 1382 "href": "https://api.spotify.com/v1/artists/37mtx80nMDETlbsq2eFCzc", 1383 "id": "37mtx80nMDETlbsq2eFCzc", 1384 "name": "Fleur East", 1385 "type": "artist", 1386 "uri": "spotify:artist:37mtx80nMDETlbsq2eFCzc" 1387 } 1388 ], 1389 "external_urls": { 1390 "spotify": "https://open.spotify.com/album/1nFgJpjh2doGfve56uADlm" 1391 }, 1392 "href": "https://api.spotify.com/v1/albums/1nFgJpjh2doGfve56uADlm", 1393 "id": "1nFgJpjh2doGfve56uADlm", 1394 "images": [ 1395 { 1396 "height": 640, 1397 "url": "https://i.scdn.co/image/ab67616d0000b27340ab5a3c8e7e7c8f12cca2bb", 1398 "width": 640 1399 }, 1400 { 1401 "height": 300, 1402 "url": "https://i.scdn.co/image/ab67616d00001e0240ab5a3c8e7e7c8f12cca2bb", 1403 "width": 300 1404 }, 1405 { 1406 "height": 64, 1407 "url": "https://i.scdn.co/image/ab67616d0000485140ab5a3c8e7e7c8f12cca2bb", 1408 "width": 64 1409 } 1410 ], 1411 "is_playable": true, 1412 "name": "Love, Sax & Flashbacks (Track by Track)", 1413 "release_date": "2015-12-03", 1414 "release_date_precision": "day", 1415 "total_tracks": 32, 1416 "type": "album", 1417 "uri": "spotify:album:1nFgJpjh2doGfve56uADlm" 1418 }, 1419 "artists": [ 1420 { 1421 "external_urls": { 1422 "spotify": "https://open.spotify.com/artist/37mtx80nMDETlbsq2eFCzc" 1423 }, 1424 "href": "https://api.spotify.com/v1/artists/37mtx80nMDETlbsq2eFCzc", 1425 "id": "37mtx80nMDETlbsq2eFCzc", 1426 "name": "Fleur East", 1427 "type": "artist", 1428 "uri": "spotify:artist:37mtx80nMDETlbsq2eFCzc" 1429 } 1430 ], 1431 "disc_number": 1, 1432 "duration_ms": 236440, 1433 "explicit": false, 1434 "external_ids": { 1435 "isrc": "GBHMU1500146" 1436 }, 1437 "external_urls": { 1438 "spotify": "https://open.spotify.com/track/2lqgZDZnlXmVZgToWuoC0l" 1439 }, 1440 "href": "https://api.spotify.com/v1/tracks/2lqgZDZnlXmVZgToWuoC0l", 1441 "id": "2lqgZDZnlXmVZgToWuoC0l", 1442 "is_local": false, 1443 "is_playable": true, 1444 "linked_from": { 1445 "external_urls": { 1446 "spotify": "https://open.spotify.com/track/2IFl6p0LoIfIZOn5IaxZOO" 1447 }, 1448 "href": "https://api.spotify.com/v1/tracks/2IFl6p0LoIfIZOn5IaxZOO", 1449 "id": "2IFl6p0LoIfIZOn5IaxZOO", 1450 "type": "track", 1451 "uri": "spotify:track:2IFl6p0LoIfIZOn5IaxZOO" 1452 }, 1453 "name": "Sax", 1454 "popularity": 56, 1455 "preview_url": "https://p.scdn.co/mp3-preview/6d74e1c14810c12a9ccea0c523c740ab98a262fc?cid=b800bf59070c4b52a8917d2230d25bdc", 1456 "track_number": 1, 1457 "type": "track", 1458 "uri": "spotify:track:2lqgZDZnlXmVZgToWuoC0l" 1459 }, 1460 { 1461 "album": { 1462 "album_type": "SINGLE", 1463 "artists": [ 1464 { 1465 "external_urls": { 1466 "spotify": "https://open.spotify.com/artist/1gALaWbNDnwS2ECV09sn2A" 1467 }, 1468 "href": "https://api.spotify.com/v1/artists/1gALaWbNDnwS2ECV09sn2A", 1469 "id": "1gALaWbNDnwS2ECV09sn2A", 1470 "name": "Nightcrawlers", 1471 "type": "artist", 1472 "uri": "spotify:artist:1gALaWbNDnwS2ECV09sn2A" 1473 } 1474 ], 1475 "external_urls": { 1476 "spotify": "https://open.spotify.com/album/5JVyNX3e2hGoOttoe7B8QL" 1477 }, 1478 "href": "https://api.spotify.com/v1/albums/5JVyNX3e2hGoOttoe7B8QL", 1479 "id": "5JVyNX3e2hGoOttoe7B8QL", 1480 "images": [ 1481 { 1482 "height": 640, 1483 "url": "https://i.scdn.co/image/ab67616d0000b2733b1e6a6f5aa7c01b433779fe", 1484 "width": 640 1485 }, 1486 { 1487 "height": 300, 1488 "url": "https://i.scdn.co/image/ab67616d00001e023b1e6a6f5aa7c01b433779fe", 1489 "width": 300 1490 }, 1491 { 1492 "height": 64, 1493 "url": "https://i.scdn.co/image/ab67616d000048513b1e6a6f5aa7c01b433779fe", 1494 "width": 64 1495 } 1496 ], 1497 "is_playable": true, 1498 "name": "Push The Feeling On", 1499 "release_date": "1995", 1500 "release_date_precision": "year", 1501 "total_tracks": 4, 1502 "type": "album", 1503 "uri": "spotify:album:5JVyNX3e2hGoOttoe7B8QL" 1504 }, 1505 "artists": [ 1506 { 1507 "external_urls": { 1508 "spotify": "https://open.spotify.com/artist/1gALaWbNDnwS2ECV09sn2A" 1509 }, 1510 "href": "https://api.spotify.com/v1/artists/1gALaWbNDnwS2ECV09sn2A", 1511 "id": "1gALaWbNDnwS2ECV09sn2A", 1512 "name": "Nightcrawlers", 1513 "type": "artist", 1514 "uri": "spotify:artist:1gALaWbNDnwS2ECV09sn2A" 1515 }, 1516 { 1517 "external_urls": { 1518 "spotify": "https://open.spotify.com/artist/1yqxFtPHKcGcv6SXZNdyT9" 1519 }, 1520 "href": "https://api.spotify.com/v1/artists/1yqxFtPHKcGcv6SXZNdyT9", 1521 "id": "1yqxFtPHKcGcv6SXZNdyT9", 1522 "name": "MK", 1523 "type": "artist", 1524 "uri": "spotify:artist:1yqxFtPHKcGcv6SXZNdyT9" 1525 } 1526 ], 1527 "disc_number": 1, 1528 "duration_ms": 243160, 1529 "explicit": false, 1530 "external_ids": { 1531 "isrc": "GBANY9500081" 1532 }, 1533 "external_urls": { 1534 "spotify": "https://open.spotify.com/track/1EWsVHU4FNAdtN4R8FETag" 1535 }, 1536 "href": "https://api.spotify.com/v1/tracks/1EWsVHU4FNAdtN4R8FETag", 1537 "id": "1EWsVHU4FNAdtN4R8FETag", 1538 "is_local": false, 1539 "is_playable": true, 1540 "name": "Push The Feeling On - Mk Dub Revisited Edit", 1541 "popularity": 69, 1542 "preview_url": null, 1543 "track_number": 1, 1544 "type": "track", 1545 "uri": "spotify:track:1EWsVHU4FNAdtN4R8FETag" 1546 }, 1547 { 1548 "album": { 1549 "album_type": "ALBUM", 1550 "artists": [ 1551 { 1552 "external_urls": { 1553 "spotify": "https://open.spotify.com/artist/7gAppWoH7pcYmphCVTXkzs" 1554 }, 1555 "href": "https://api.spotify.com/v1/artists/7gAppWoH7pcYmphCVTXkzs", 1556 "id": "7gAppWoH7pcYmphCVTXkzs", 1557 "name": "The Vamps", 1558 "type": "artist", 1559 "uri": "spotify:artist:7gAppWoH7pcYmphCVTXkzs" 1560 } 1561 ], 1562 "external_urls": { 1563 "spotify": "https://open.spotify.com/album/69Pj3ce9XFZUi3XuQylLKf" 1564 }, 1565 "href": "https://api.spotify.com/v1/albums/69Pj3ce9XFZUi3XuQylLKf", 1566 "id": "69Pj3ce9XFZUi3XuQylLKf", 1567 "images": [ 1568 { 1569 "height": 640, 1570 "url": "https://i.scdn.co/image/ab67616d0000b273f36a4e2e1687e678f29328cb", 1571 "width": 640 1572 }, 1573 { 1574 "height": 300, 1575 "url": "https://i.scdn.co/image/ab67616d00001e02f36a4e2e1687e678f29328cb", 1576 "width": 300 1577 }, 1578 { 1579 "height": 64, 1580 "url": "https://i.scdn.co/image/ab67616d00004851f36a4e2e1687e678f29328cb", 1581 "width": 64 1582 } 1583 ], 1584 "is_playable": true, 1585 "name": "Night & Day (Night Edition)", 1586 "release_date": "2017-07-14", 1587 "release_date_precision": "day", 1588 "total_tracks": 10, 1589 "type": "album", 1590 "uri": "spotify:album:69Pj3ce9XFZUi3XuQylLKf" 1591 }, 1592 "artists": [ 1593 { 1594 "external_urls": { 1595 "spotify": "https://open.spotify.com/artist/7gAppWoH7pcYmphCVTXkzs" 1596 }, 1597 "href": "https://api.spotify.com/v1/artists/7gAppWoH7pcYmphCVTXkzs", 1598 "id": "7gAppWoH7pcYmphCVTXkzs", 1599 "name": "The Vamps", 1600 "type": "artist", 1601 "uri": "spotify:artist:7gAppWoH7pcYmphCVTXkzs" 1602 }, 1603 { 1604 "external_urls": { 1605 "spotify": "https://open.spotify.com/artist/4YXycRbyyAE0wozTk7QMEq" 1606 }, 1607 "href": "https://api.spotify.com/v1/artists/4YXycRbyyAE0wozTk7QMEq", 1608 "id": "4YXycRbyyAE0wozTk7QMEq", 1609 "name": "Matoma", 1610 "type": "artist", 1611 "uri": "spotify:artist:4YXycRbyyAE0wozTk7QMEq" 1612 } 1613 ], 1614 "disc_number": 1, 1615 "duration_ms": 197640, 1616 "explicit": false, 1617 "external_ids": { 1618 "isrc": "GBUM71605342" 1619 }, 1620 "external_urls": { 1621 "spotify": "https://open.spotify.com/track/0dXNQ8dckG4eYfEtq9zcva" 1622 }, 1623 "href": "https://api.spotify.com/v1/tracks/0dXNQ8dckG4eYfEtq9zcva", 1624 "id": "0dXNQ8dckG4eYfEtq9zcva", 1625 "is_local": false, 1626 "is_playable": true, 1627 "name": "All Night", 1628 "popularity": 72, 1629 "preview_url": null, 1630 "track_number": 2, 1631 "type": "track", 1632 "uri": "spotify:track:0dXNQ8dckG4eYfEtq9zcva" 1633 }, 1634 { 1635 "album": { 1636 "album_type": "ALBUM", 1637 "artists": [ 1638 { 1639 "external_urls": { 1640 "spotify": "https://open.spotify.com/artist/6fxyWrfmjcbj5d12gXeiNV" 1641 }, 1642 "href": "https://api.spotify.com/v1/artists/6fxyWrfmjcbj5d12gXeiNV", 1643 "id": "6fxyWrfmjcbj5d12gXeiNV", 1644 "name": "Denzel Curry", 1645 "type": "artist", 1646 "uri": "spotify:artist:6fxyWrfmjcbj5d12gXeiNV" 1647 } 1648 ], 1649 "external_urls": { 1650 "spotify": "https://open.spotify.com/album/42fyKPanos0Q3woi848ktg" 1651 }, 1652 "href": "https://api.spotify.com/v1/albums/42fyKPanos0Q3woi848ktg", 1653 "id": "42fyKPanos0Q3woi848ktg", 1654 "images": [ 1655 { 1656 "height": 640, 1657 "url": "https://i.scdn.co/image/ab67616d0000b273166442984ba98f0a2dcaea5e", 1658 "width": 640 1659 }, 1660 { 1661 "height": 300, 1662 "url": "https://i.scdn.co/image/ab67616d00001e02166442984ba98f0a2dcaea5e", 1663 "width": 300 1664 }, 1665 { 1666 "height": 64, 1667 "url": "https://i.scdn.co/image/ab67616d00004851166442984ba98f0a2dcaea5e", 1668 "width": 64 1669 } 1670 ], 1671 "is_playable": true, 1672 "name": "Imperial", 1673 "release_date": "2016-10-14", 1674 "release_date_precision": "day", 1675 "total_tracks": 10, 1676 "type": "album", 1677 "uri": "spotify:album:42fyKPanos0Q3woi848ktg" 1678 }, 1679 "artists": [ 1680 { 1681 "external_urls": { 1682 "spotify": "https://open.spotify.com/artist/6fxyWrfmjcbj5d12gXeiNV" 1683 }, 1684 "href": "https://api.spotify.com/v1/artists/6fxyWrfmjcbj5d12gXeiNV", 1685 "id": "6fxyWrfmjcbj5d12gXeiNV", 1686 "name": "Denzel Curry", 1687 "type": "artist", 1688 "uri": "spotify:artist:6fxyWrfmjcbj5d12gXeiNV" 1689 } 1690 ], 1691 "disc_number": 1, 1692 "duration_ms": 247054, 1693 "explicit": true, 1694 "external_ids": { 1695 "isrc": "USC4R1602006" 1696 }, 1697 "external_urls": { 1698 "spotify": "https://open.spotify.com/track/5BSz8sJO2YmKfNVQG7fq2J" 1699 }, 1700 "href": "https://api.spotify.com/v1/tracks/5BSz8sJO2YmKfNVQG7fq2J", 1701 "id": "5BSz8sJO2YmKfNVQG7fq2J", 1702 "is_local": false, 1703 "is_playable": true, 1704 "name": "ULT", 1705 "popularity": 52, 1706 "preview_url": null, 1707 "track_number": 1, 1708 "type": "track", 1709 "uri": "spotify:track:5BSz8sJO2YmKfNVQG7fq2J" 1710 }, 1711 { 1712 "album": { 1713 "album_type": "ALBUM", 1714 "artists": [ 1715 { 1716 "external_urls": { 1717 "spotify": "https://open.spotify.com/artist/2ojlS7imGFiZ8A8tXXGEt7" 1718 }, 1719 "href": "https://api.spotify.com/v1/artists/2ojlS7imGFiZ8A8tXXGEt7", 1720 "id": "2ojlS7imGFiZ8A8tXXGEt7", 1721 "name": "Library Tapes", 1722 "type": "artist", 1723 "uri": "spotify:artist:2ojlS7imGFiZ8A8tXXGEt7" 1724 } 1725 ], 1726 "external_urls": { 1727 "spotify": "https://open.spotify.com/album/43QOTxisI7tVOFZgUIWu4g" 1728 }, 1729 "href": "https://api.spotify.com/v1/albums/43QOTxisI7tVOFZgUIWu4g", 1730 "id": "43QOTxisI7tVOFZgUIWu4g", 1731 "images": [ 1732 { 1733 "height": 640, 1734 "url": "https://i.scdn.co/image/ab67616d0000b2736813c2e10a4fb2a5cac8ebb0", 1735 "width": 640 1736 }, 1737 { 1738 "height": 300, 1739 "url": "https://i.scdn.co/image/ab67616d00001e026813c2e10a4fb2a5cac8ebb0", 1740 "width": 300 1741 }, 1742 { 1743 "height": 64, 1744 "url": "https://i.scdn.co/image/ab67616d000048516813c2e10a4fb2a5cac8ebb0", 1745 "width": 64 1746 } 1747 ], 1748 "is_playable": true, 1749 "name": "Fragment", 1750 "release_date": "2008-06-15", 1751 "release_date_precision": "day", 1752 "total_tracks": 8, 1753 "type": "album", 1754 "uri": "spotify:album:43QOTxisI7tVOFZgUIWu4g" 1755 }, 1756 "artists": [ 1757 { 1758 "external_urls": { 1759 "spotify": "https://open.spotify.com/artist/2ojlS7imGFiZ8A8tXXGEt7" 1760 }, 1761 "href": "https://api.spotify.com/v1/artists/2ojlS7imGFiZ8A8tXXGEt7", 1762 "id": "2ojlS7imGFiZ8A8tXXGEt7", 1763 "name": "Library Tapes", 1764 "type": "artist", 1765 "uri": "spotify:artist:2ojlS7imGFiZ8A8tXXGEt7" 1766 } 1767 ], 1768 "disc_number": 1, 1769 "duration_ms": 240506, 1770 "explicit": false, 1771 "external_ids": { 1772 "isrc": "SEYTP0800202" 1773 }, 1774 "external_urls": { 1775 "spotify": "https://open.spotify.com/track/7vv4tJIsMFm3XnDHikiwIc" 1776 }, 1777 "href": "https://api.spotify.com/v1/tracks/7vv4tJIsMFm3XnDHikiwIc", 1778 "id": "7vv4tJIsMFm3XnDHikiwIc", 1779 "is_local": false, 1780 "is_playable": true, 1781 "linked_from": { 1782 "external_urls": { 1783 "spotify": "https://open.spotify.com/track/7JQfc4yI4R8E4WOUYR7HD9" 1784 }, 1785 "href": "https://api.spotify.com/v1/tracks/7JQfc4yI4R8E4WOUYR7HD9", 1786 "id": "7JQfc4yI4R8E4WOUYR7HD9", 1787 "type": "track", 1788 "uri": "spotify:track:7JQfc4yI4R8E4WOUYR7HD9" 1789 }, 1790 "name": "Fragment II", 1791 "popularity": 44, 1792 "preview_url": null, 1793 "track_number": 2, 1794 "type": "track", 1795 "uri": "spotify:track:7vv4tJIsMFm3XnDHikiwIc" 1796 }, 1797 { 1798 "album": { 1799 "album_type": "ALBUM", 1800 "artists": [ 1801 { 1802 "external_urls": { 1803 "spotify": "https://open.spotify.com/artist/163tK9Wjr9P9DmM0AVK7lm" 1804 }, 1805 "href": "https://api.spotify.com/v1/artists/163tK9Wjr9P9DmM0AVK7lm", 1806 "id": "163tK9Wjr9P9DmM0AVK7lm", 1807 "name": "Lorde", 1808 "type": "artist", 1809 "uri": "spotify:artist:163tK9Wjr9P9DmM0AVK7lm" 1810 } 1811 ], 1812 "external_urls": { 1813 "spotify": "https://open.spotify.com/album/2nPokmAvYy5qYO4rFU7ZDm" 1814 }, 1815 "href": "https://api.spotify.com/v1/albums/2nPokmAvYy5qYO4rFU7ZDm", 1816 "id": "2nPokmAvYy5qYO4rFU7ZDm", 1817 "images": [ 1818 { 1819 "height": 640, 1820 "url": "https://i.scdn.co/image/ab67616d0000b273e19c63f62581ab73a6589382", 1821 "width": 640 1822 }, 1823 { 1824 "height": 300, 1825 "url": "https://i.scdn.co/image/ab67616d00001e02e19c63f62581ab73a6589382", 1826 "width": 300 1827 }, 1828 { 1829 "height": 64, 1830 "url": "https://i.scdn.co/image/ab67616d00004851e19c63f62581ab73a6589382", 1831 "width": 64 1832 } 1833 ], 1834 "is_playable": true, 1835 "name": "Pure Heroine", 1836 "release_date": "2013-01-01", 1837 "release_date_precision": "day", 1838 "total_tracks": 13, 1839 "type": "album", 1840 "uri": "spotify:album:2nPokmAvYy5qYO4rFU7ZDm" 1841 }, 1842 "artists": [ 1843 { 1844 "external_urls": { 1845 "spotify": "https://open.spotify.com/artist/163tK9Wjr9P9DmM0AVK7lm" 1846 }, 1847 "href": "https://api.spotify.com/v1/artists/163tK9Wjr9P9DmM0AVK7lm", 1848 "id": "163tK9Wjr9P9DmM0AVK7lm", 1849 "name": "Lorde", 1850 "type": "artist", 1851 "uri": "spotify:artist:163tK9Wjr9P9DmM0AVK7lm" 1852 } 1853 ], 1854 "disc_number": 1, 1855 "duration_ms": 258969, 1856 "explicit": false, 1857 "external_ids": { 1858 "isrc": "NZUM71300122" 1859 }, 1860 "external_urls": { 1861 "spotify": "https://open.spotify.com/track/2MvvoeRt8NcOXWESkxWn3g" 1862 }, 1863 "href": "https://api.spotify.com/v1/tracks/2MvvoeRt8NcOXWESkxWn3g", 1864 "id": "2MvvoeRt8NcOXWESkxWn3g", 1865 "is_local": false, 1866 "is_playable": true, 1867 "linked_from": { 1868 "external_urls": { 1869 "spotify": "https://open.spotify.com/track/38WyDtxZhxm63jiythwemE" 1870 }, 1871 "href": "https://api.spotify.com/v1/tracks/38WyDtxZhxm63jiythwemE", 1872 "id": "38WyDtxZhxm63jiythwemE", 1873 "type": "track", 1874 "uri": "spotify:track:38WyDtxZhxm63jiythwemE" 1875 }, 1876 "name": "Ribs", 1877 "popularity": 80, 1878 "preview_url": null, 1879 "track_number": 4, 1880 "type": "track", 1881 "uri": "spotify:track:2MvvoeRt8NcOXWESkxWn3g" 1882 }, 1883 { 1884 "album": { 1885 "album_type": "SINGLE", 1886 "artists": [ 1887 { 1888 "external_urls": { 1889 "spotify": "https://open.spotify.com/artist/0lZoBs4Pzo7R89JM9lxwoT" 1890 }, 1891 "href": "https://api.spotify.com/v1/artists/0lZoBs4Pzo7R89JM9lxwoT", 1892 "id": "0lZoBs4Pzo7R89JM9lxwoT", 1893 "name": "Duran Duran", 1894 "type": "artist", 1895 "uri": "spotify:artist:0lZoBs4Pzo7R89JM9lxwoT" 1896 } 1897 ], 1898 "external_urls": { 1899 "spotify": "https://open.spotify.com/album/5NLAqcJTupUe8pUzf6Jaen" 1900 }, 1901 "href": "https://api.spotify.com/v1/albums/5NLAqcJTupUe8pUzf6Jaen", 1902 "id": "5NLAqcJTupUe8pUzf6Jaen", 1903 "images": [ 1904 { 1905 "height": 640, 1906 "url": "https://i.scdn.co/image/ab67616d0000b273b35021533b9088e272ceeb90", 1907 "width": 640 1908 }, 1909 { 1910 "height": 300, 1911 "url": "https://i.scdn.co/image/ab67616d00001e02b35021533b9088e272ceeb90", 1912 "width": 300 1913 }, 1914 { 1915 "height": 64, 1916 "url": "https://i.scdn.co/image/ab67616d00004851b35021533b9088e272ceeb90", 1917 "width": 64 1918 } 1919 ], 1920 "is_playable": true, 1921 "name": "Last Night in the City (feat. Kiesza) [The Remixes]", 1922 "release_date": "2017-03-10", 1923 "release_date_precision": "day", 1924 "total_tracks": 4, 1925 "type": "album", 1926 "uri": "spotify:album:5NLAqcJTupUe8pUzf6Jaen" 1927 }, 1928 "artists": [ 1929 { 1930 "external_urls": { 1931 "spotify": "https://open.spotify.com/artist/0lZoBs4Pzo7R89JM9lxwoT" 1932 }, 1933 "href": "https://api.spotify.com/v1/artists/0lZoBs4Pzo7R89JM9lxwoT", 1934 "id": "0lZoBs4Pzo7R89JM9lxwoT", 1935 "name": "Duran Duran", 1936 "type": "artist", 1937 "uri": "spotify:artist:0lZoBs4Pzo7R89JM9lxwoT" 1938 }, 1939 { 1940 "external_urls": { 1941 "spotify": "https://open.spotify.com/artist/4zxvC7CRGvggq9EWXOpwAo" 1942 }, 1943 "href": "https://api.spotify.com/v1/artists/4zxvC7CRGvggq9EWXOpwAo", 1944 "id": "4zxvC7CRGvggq9EWXOpwAo", 1945 "name": "Kiesza", 1946 "type": "artist", 1947 "uri": "spotify:artist:4zxvC7CRGvggq9EWXOpwAo" 1948 }, 1949 { 1950 "external_urls": { 1951 "spotify": "https://open.spotify.com/artist/5c8rdJfIrtDTnRak9cuzwv" 1952 }, 1953 "href": "https://api.spotify.com/v1/artists/5c8rdJfIrtDTnRak9cuzwv", 1954 "id": "5c8rdJfIrtDTnRak9cuzwv", 1955 "name": "Louis Vivet", 1956 "type": "artist", 1957 "uri": "spotify:artist:5c8rdJfIrtDTnRak9cuzwv" 1958 } 1959 ], 1960 "disc_number": 1, 1961 "duration_ms": 201999, 1962 "explicit": false, 1963 "external_ids": { 1964 "isrc": "USWB11700345" 1965 }, 1966 "external_urls": { 1967 "spotify": "https://open.spotify.com/track/49Ux5DRv82fNGXDO7sIy4y" 1968 }, 1969 "href": "https://api.spotify.com/v1/tracks/49Ux5DRv82fNGXDO7sIy4y", 1970 "id": "49Ux5DRv82fNGXDO7sIy4y", 1971 "is_local": false, 1972 "is_playable": true, 1973 "name": "Last Night in the City (feat. Kiesza) - Louis Vivet Remix", 1974 "popularity": 6, 1975 "preview_url": "https://p.scdn.co/mp3-preview/9aa4da8b4c1b17c48a1f8a6f24058ac4e47fdf7b?cid=b800bf59070c4b52a8917d2230d25bdc", 1976 "track_number": 3, 1977 "type": "track", 1978 "uri": "spotify:track:49Ux5DRv82fNGXDO7sIy4y" 1979 }, 1980 { 1981 "album": { 1982 "album_type": "SINGLE", 1983 "artists": [ 1984 { 1985 "external_urls": { 1986 "spotify": "https://open.spotify.com/artist/65YhYi4Fz5Ibgq7ueev2Rm" 1987 }, 1988 "href": "https://api.spotify.com/v1/artists/65YhYi4Fz5Ibgq7ueev2Rm", 1989 "id": "65YhYi4Fz5Ibgq7ueev2Rm", 1990 "name": "Frederick Delius", 1991 "type": "artist", 1992 "uri": "spotify:artist:65YhYi4Fz5Ibgq7ueev2Rm" 1993 }, 1994 { 1995 "external_urls": { 1996 "spotify": "https://open.spotify.com/artist/5Dl3HXZjG6ZOWT5cV375lk" 1997 }, 1998 "href": "https://api.spotify.com/v1/artists/5Dl3HXZjG6ZOWT5cV375lk", 1999 "id": "5Dl3HXZjG6ZOWT5cV375lk", 2000 "name": "Yo-Yo Ma", 2001 "type": "artist", 2002 "uri": "spotify:artist:5Dl3HXZjG6ZOWT5cV375lk" 2003 }, 2004 { 2005 "external_urls": { 2006 "spotify": "https://open.spotify.com/artist/7JmDqds7Y1LRSWZVM8e0Og" 2007 }, 2008 "href": "https://api.spotify.com/v1/artists/7JmDqds7Y1LRSWZVM8e0Og", 2009 "id": "7JmDqds7Y1LRSWZVM8e0Og", 2010 "name": "Kathryn Stott", 2011 "type": "artist", 2012 "uri": "spotify:artist:7JmDqds7Y1LRSWZVM8e0Og" 2013 } 2014 ], 2015 "external_urls": { 2016 "spotify": "https://open.spotify.com/album/5Srtg5Q52DBLYEAIEQBDyH" 2017 }, 2018 "href": "https://api.spotify.com/v1/albums/5Srtg5Q52DBLYEAIEQBDyH", 2019 "id": "5Srtg5Q52DBLYEAIEQBDyH", 2020 "images": [ 2021 { 2022 "height": 640, 2023 "url": "https://i.scdn.co/image/ab67616d0000b27309516b49c785767fb6193732", 2024 "width": 640 2025 }, 2026 { 2027 "height": 300, 2028 "url": "https://i.scdn.co/image/ab67616d00001e0209516b49c785767fb6193732", 2029 "width": 300 2030 }, 2031 { 2032 "height": 64, 2033 "url": "https://i.scdn.co/image/ab67616d0000485109516b49c785767fb6193732", 2034 "width": 64 2035 } 2036 ], 2037 "is_playable": true, 2038 "name": "Romance for Cello and Piano", 2039 "release_date": "2015-07-24", 2040 "release_date_precision": "day", 2041 "total_tracks": 1, 2042 "type": "album", 2043 "uri": "spotify:album:5Srtg5Q52DBLYEAIEQBDyH" 2044 }, 2045 "artists": [ 2046 { 2047 "external_urls": { 2048 "spotify": "https://open.spotify.com/artist/65YhYi4Fz5Ibgq7ueev2Rm" 2049 }, 2050 "href": "https://api.spotify.com/v1/artists/65YhYi4Fz5Ibgq7ueev2Rm", 2051 "id": "65YhYi4Fz5Ibgq7ueev2Rm", 2052 "name": "Frederick Delius", 2053 "type": "artist", 2054 "uri": "spotify:artist:65YhYi4Fz5Ibgq7ueev2Rm" 2055 }, 2056 { 2057 "external_urls": { 2058 "spotify": "https://open.spotify.com/artist/7JmDqds7Y1LRSWZVM8e0Og" 2059 }, 2060 "href": "https://api.spotify.com/v1/artists/7JmDqds7Y1LRSWZVM8e0Og", 2061 "id": "7JmDqds7Y1LRSWZVM8e0Og", 2062 "name": "Kathryn Stott", 2063 "type": "artist", 2064 "uri": "spotify:artist:7JmDqds7Y1LRSWZVM8e0Og" 2065 }, 2066 { 2067 "external_urls": { 2068 "spotify": "https://open.spotify.com/artist/5Dl3HXZjG6ZOWT5cV375lk" 2069 }, 2070 "href": "https://api.spotify.com/v1/artists/5Dl3HXZjG6ZOWT5cV375lk", 2071 "id": "5Dl3HXZjG6ZOWT5cV375lk", 2072 "name": "Yo-Yo Ma", 2073 "type": "artist", 2074 "uri": "spotify:artist:5Dl3HXZjG6ZOWT5cV375lk" 2075 } 2076 ], 2077 "disc_number": 1, 2078 "duration_ms": 385186, 2079 "explicit": false, 2080 "external_ids": { 2081 "isrc": "USQX91500821" 2082 }, 2083 "external_urls": { 2084 "spotify": "https://open.spotify.com/track/6ufaxWhOW87EXsKh5TSxMp" 2085 }, 2086 "href": "https://api.spotify.com/v1/tracks/6ufaxWhOW87EXsKh5TSxMp", 2087 "id": "6ufaxWhOW87EXsKh5TSxMp", 2088 "is_local": false, 2089 "is_playable": true, 2090 "linked_from": { 2091 "external_urls": { 2092 "spotify": "https://open.spotify.com/track/70nMUxP46mAcjW5iRpRiyN" 2093 }, 2094 "href": "https://api.spotify.com/v1/tracks/70nMUxP46mAcjW5iRpRiyN", 2095 "id": "70nMUxP46mAcjW5iRpRiyN", 2096 "type": "track", 2097 "uri": "spotify:track:70nMUxP46mAcjW5iRpRiyN" 2098 }, 2099 "name": "Romance for Cello and Piano", 2100 "popularity": 33, 2101 "preview_url": "https://p.scdn.co/mp3-preview/59053165aa006169f88b0888a1c2d8e420d5508a?cid=b800bf59070c4b52a8917d2230d25bdc", 2102 "track_number": 11, 2103 "type": "track", 2104 "uri": "spotify:track:6ufaxWhOW87EXsKh5TSxMp" 2105 }, 2106 { 2107 "album": { 2108 "album_type": "SINGLE", 2109 "artists": [ 2110 { 2111 "external_urls": { 2112 "spotify": "https://open.spotify.com/artist/137W8MRPWKqSmrBGDBFSop" 2113 }, 2114 "href": "https://api.spotify.com/v1/artists/137W8MRPWKqSmrBGDBFSop", 2115 "id": "137W8MRPWKqSmrBGDBFSop", 2116 "name": "Wiz Khalifa", 2117 "type": "artist", 2118 "uri": "spotify:artist:137W8MRPWKqSmrBGDBFSop" 2119 } 2120 ], 2121 "external_urls": { 2122 "spotify": "https://open.spotify.com/album/3qv4Hjk70y70MNe9Kb9X8f" 2123 }, 2124 "href": "https://api.spotify.com/v1/albums/3qv4Hjk70y70MNe9Kb9X8f", 2125 "id": "3qv4Hjk70y70MNe9Kb9X8f", 2126 "images": [ 2127 { 2128 "height": 640, 2129 "url": "https://i.scdn.co/image/ab67616d0000b273dfe0748e91928eaf21373018", 2130 "width": 640 2131 }, 2132 { 2133 "height": 300, 2134 "url": "https://i.scdn.co/image/ab67616d00001e02dfe0748e91928eaf21373018", 2135 "width": 300 2136 }, 2137 { 2138 "height": 64, 2139 "url": "https://i.scdn.co/image/ab67616d00004851dfe0748e91928eaf21373018", 2140 "width": 64 2141 } 2142 ], 2143 "is_playable": true, 2144 "name": "Pull Up (feat. Lil Uzi Vert)", 2145 "release_date": "2016-05-25", 2146 "release_date_precision": "day", 2147 "total_tracks": 1, 2148 "type": "album", 2149 "uri": "spotify:album:3qv4Hjk70y70MNe9Kb9X8f" 2150 }, 2151 "artists": [ 2152 { 2153 "external_urls": { 2154 "spotify": "https://open.spotify.com/artist/137W8MRPWKqSmrBGDBFSop" 2155 }, 2156 "href": "https://api.spotify.com/v1/artists/137W8MRPWKqSmrBGDBFSop", 2157 "id": "137W8MRPWKqSmrBGDBFSop", 2158 "name": "Wiz Khalifa", 2159 "type": "artist", 2160 "uri": "spotify:artist:137W8MRPWKqSmrBGDBFSop" 2161 }, 2162 { 2163 "external_urls": { 2164 "spotify": "https://open.spotify.com/artist/4O15NlyKLIASxsJ0PrXPfz" 2165 }, 2166 "href": "https://api.spotify.com/v1/artists/4O15NlyKLIASxsJ0PrXPfz", 2167 "id": "4O15NlyKLIASxsJ0PrXPfz", 2168 "name": "Lil Uzi Vert", 2169 "type": "artist", 2170 "uri": "spotify:artist:4O15NlyKLIASxsJ0PrXPfz" 2171 } 2172 ], 2173 "disc_number": 1, 2174 "duration_ms": 207391, 2175 "explicit": true, 2176 "external_ids": { 2177 "isrc": "USAT21601765" 2178 }, 2179 "external_urls": { 2180 "spotify": "https://open.spotify.com/track/6LTsvaebP6V9tJFvZxqi5M" 2181 }, 2182 "href": "https://api.spotify.com/v1/tracks/6LTsvaebP6V9tJFvZxqi5M", 2183 "id": "6LTsvaebP6V9tJFvZxqi5M", 2184 "is_local": false, 2185 "is_playable": true, 2186 "name": "Pull Up (feat. Lil Uzi Vert)", 2187 "popularity": 50, 2188 "preview_url": "https://p.scdn.co/mp3-preview/644de9f6fa47954ea3b44748b345a8f5f8d3fdd7?cid=b800bf59070c4b52a8917d2230d25bdc", 2189 "track_number": 1, 2190 "type": "track", 2191 "uri": "spotify:track:6LTsvaebP6V9tJFvZxqi5M" 2192 }, 2193 { 2194 "album": { 2195 "album_type": "ALBUM", 2196 "artists": [ 2197 { 2198 "external_urls": { 2199 "spotify": "https://open.spotify.com/artist/2KsP6tYLJlTBvSUxnwlVWa" 2200 }, 2201 "href": "https://api.spotify.com/v1/artists/2KsP6tYLJlTBvSUxnwlVWa", 2202 "id": "2KsP6tYLJlTBvSUxnwlVWa", 2203 "name": "Mike Posner", 2204 "type": "artist", 2205 "uri": "spotify:artist:2KsP6tYLJlTBvSUxnwlVWa" 2206 } 2207 ], 2208 "external_urls": { 2209 "spotify": "https://open.spotify.com/album/6Phl1V5P0sPrWJytXHGFeO" 2210 }, 2211 "href": "https://api.spotify.com/v1/albums/6Phl1V5P0sPrWJytXHGFeO", 2212 "id": "6Phl1V5P0sPrWJytXHGFeO", 2213 "images": [ 2214 { 2215 "height": 640, 2216 "url": "https://i.scdn.co/image/ab67616d0000b2731db27fa11dbafa67857da8f3", 2217 "width": 640 2218 }, 2219 { 2220 "height": 300, 2221 "url": "https://i.scdn.co/image/ab67616d00001e021db27fa11dbafa67857da8f3", 2222 "width": 300 2223 }, 2224 { 2225 "height": 64, 2226 "url": "https://i.scdn.co/image/ab67616d000048511db27fa11dbafa67857da8f3", 2227 "width": 64 2228 } 2229 ], 2230 "is_playable": true, 2231 "name": "At Night, Alone.", 2232 "release_date": "2016-05-06", 2233 "release_date_precision": "day", 2234 "total_tracks": 18, 2235 "type": "album", 2236 "uri": "spotify:album:6Phl1V5P0sPrWJytXHGFeO" 2237 }, 2238 "artists": [ 2239 { 2240 "external_urls": { 2241 "spotify": "https://open.spotify.com/artist/2KsP6tYLJlTBvSUxnwlVWa" 2242 }, 2243 "href": "https://api.spotify.com/v1/artists/2KsP6tYLJlTBvSUxnwlVWa", 2244 "id": "2KsP6tYLJlTBvSUxnwlVWa", 2245 "name": "Mike Posner", 2246 "type": "artist", 2247 "uri": "spotify:artist:2KsP6tYLJlTBvSUxnwlVWa" 2248 }, 2249 { 2250 "external_urls": { 2251 "spotify": "https://open.spotify.com/artist/5iNrZmtVMtYev5M9yoWpEq" 2252 }, 2253 "href": "https://api.spotify.com/v1/artists/5iNrZmtVMtYev5M9yoWpEq", 2254 "id": "5iNrZmtVMtYev5M9yoWpEq", 2255 "name": "Seeb", 2256 "type": "artist", 2257 "uri": "spotify:artist:5iNrZmtVMtYev5M9yoWpEq" 2258 } 2259 ], 2260 "disc_number": 1, 2261 "duration_ms": 197933, 2262 "explicit": true, 2263 "external_ids": { 2264 "isrc": "USUM71509342" 2265 }, 2266 "external_urls": { 2267 "spotify": "https://open.spotify.com/track/0vbtURX4qv1l7besfwmnD8" 2268 }, 2269 "href": "https://api.spotify.com/v1/tracks/0vbtURX4qv1l7besfwmnD8", 2270 "id": "0vbtURX4qv1l7besfwmnD8", 2271 "is_local": false, 2272 "is_playable": true, 2273 "name": "I Took A Pill In Ibiza - Seeb Remix", 2274 "popularity": 83, 2275 "preview_url": null, 2276 "track_number": 13, 2277 "type": "track", 2278 "uri": "spotify:track:0vbtURX4qv1l7besfwmnD8" 2279 }, 2280 { 2281 "album": { 2282 "album_type": "COMPILATION", 2283 "artists": [ 2284 { 2285 "external_urls": { 2286 "spotify": "https://open.spotify.com/artist/0LyfQWJT6nXafLPZqxe9Of" 2287 }, 2288 "href": "https://api.spotify.com/v1/artists/0LyfQWJT6nXafLPZqxe9Of", 2289 "id": "0LyfQWJT6nXafLPZqxe9Of", 2290 "name": "Various Artists", 2291 "type": "artist", 2292 "uri": "spotify:artist:0LyfQWJT6nXafLPZqxe9Of" 2293 } 2294 ], 2295 "external_urls": { 2296 "spotify": "https://open.spotify.com/album/6lJrqUXdCpEINx1fi5EqMa" 2297 }, 2298 "href": "https://api.spotify.com/v1/albums/6lJrqUXdCpEINx1fi5EqMa", 2299 "id": "6lJrqUXdCpEINx1fi5EqMa", 2300 "images": [ 2301 { 2302 "height": 640, 2303 "url": "https://i.scdn.co/image/ab67616d0000b27333d6ad1275ae8ebe156686f5", 2304 "width": 640 2305 }, 2306 { 2307 "height": 300, 2308 "url": "https://i.scdn.co/image/ab67616d00001e0233d6ad1275ae8ebe156686f5", 2309 "width": 300 2310 }, 2311 { 2312 "height": 64, 2313 "url": "https://i.scdn.co/image/ab67616d0000485133d6ad1275ae8ebe156686f5", 2314 "width": 64 2315 } 2316 ], 2317 "is_playable": true, 2318 "name": "The Twilight Saga: Breaking Dawn - Part 2 (Original Motion Picture Soundtrack)", 2319 "release_date": "2012-11-09", 2320 "release_date_precision": "day", 2321 "total_tracks": 14, 2322 "type": "album", 2323 "uri": "spotify:album:6lJrqUXdCpEINx1fi5EqMa" 2324 }, 2325 "artists": [ 2326 { 2327 "external_urls": { 2328 "spotify": "https://open.spotify.com/artist/0SbSDzM4X41hnlURed0fcV" 2329 }, 2330 "href": "https://api.spotify.com/v1/artists/0SbSDzM4X41hnlURed0fcV", 2331 "id": "0SbSDzM4X41hnlURed0fcV", 2332 "name": "Carter Burwell", 2333 "type": "artist", 2334 "uri": "spotify:artist:0SbSDzM4X41hnlURed0fcV" 2335 } 2336 ], 2337 "disc_number": 1, 2338 "duration_ms": 255773, 2339 "explicit": false, 2340 "external_ids": { 2341 "isrc": "USAT21206056" 2342 }, 2343 "external_urls": { 2344 "spotify": "https://open.spotify.com/track/1brH9m4Q2LHTuwxaKoMTn5" 2345 }, 2346 "href": "https://api.spotify.com/v1/tracks/1brH9m4Q2LHTuwxaKoMTn5", 2347 "id": "1brH9m4Q2LHTuwxaKoMTn5", 2348 "is_local": false, 2349 "is_playable": true, 2350 "name": "Plus Que Ma Prope Vie", 2351 "popularity": 48, 2352 "preview_url": "https://p.scdn.co/mp3-preview/585cd6ea1265df63d349dcda19035a27893f50d2?cid=b800bf59070c4b52a8917d2230d25bdc", 2353 "track_number": 14, 2354 "type": "track", 2355 "uri": "spotify:track:1brH9m4Q2LHTuwxaKoMTn5" 2356 }, 2357 { 2358 "album": { 2359 "album_type": "ALBUM", 2360 "artists": [ 2361 { 2362 "external_urls": { 2363 "spotify": "https://open.spotify.com/artist/03r4iKL2g2442PT9n2UKsx" 2364 }, 2365 "href": "https://api.spotify.com/v1/artists/03r4iKL2g2442PT9n2UKsx", 2366 "id": "03r4iKL2g2442PT9n2UKsx", 2367 "name": "Beastie Boys", 2368 "type": "artist", 2369 "uri": "spotify:artist:03r4iKL2g2442PT9n2UKsx" 2370 } 2371 ], 2372 "external_urls": { 2373 "spotify": "https://open.spotify.com/album/11oR0ZuqB3ucZwb5TGbZxb" 2374 }, 2375 "href": "https://api.spotify.com/v1/albums/11oR0ZuqB3ucZwb5TGbZxb", 2376 "id": "11oR0ZuqB3ucZwb5TGbZxb", 2377 "images": [ 2378 { 2379 "height": 640, 2380 "url": "https://i.scdn.co/image/ab67616d0000b273a7ea08ab3914c5fb2084a8ac", 2381 "width": 640 2382 }, 2383 { 2384 "height": 300, 2385 "url": "https://i.scdn.co/image/ab67616d00001e02a7ea08ab3914c5fb2084a8ac", 2386 "width": 300 2387 }, 2388 { 2389 "height": 64, 2390 "url": "https://i.scdn.co/image/ab67616d00004851a7ea08ab3914c5fb2084a8ac", 2391 "width": 64 2392 } 2393 ], 2394 "is_playable": true, 2395 "name": "Licensed To Ill", 2396 "release_date": "1986-11-15", 2397 "release_date_precision": "day", 2398 "total_tracks": 13, 2399 "type": "album", 2400 "uri": "spotify:album:11oR0ZuqB3ucZwb5TGbZxb" 2401 }, 2402 "artists": [ 2403 { 2404 "external_urls": { 2405 "spotify": "https://open.spotify.com/artist/03r4iKL2g2442PT9n2UKsx" 2406 }, 2407 "href": "https://api.spotify.com/v1/artists/03r4iKL2g2442PT9n2UKsx", 2408 "id": "03r4iKL2g2442PT9n2UKsx", 2409 "name": "Beastie Boys", 2410 "type": "artist", 2411 "uri": "spotify:artist:03r4iKL2g2442PT9n2UKsx" 2412 } 2413 ], 2414 "disc_number": 1, 2415 "duration_ms": 157440, 2416 "explicit": false, 2417 "external_ids": { 2418 "isrc": "USDJ28600011" 2419 }, 2420 "external_urls": { 2421 "spotify": "https://open.spotify.com/track/2tY1gxCKslfXLFpFofYmJQ" 2422 }, 2423 "href": "https://api.spotify.com/v1/tracks/2tY1gxCKslfXLFpFofYmJQ", 2424 "id": "2tY1gxCKslfXLFpFofYmJQ", 2425 "is_local": false, 2426 "is_playable": true, 2427 "name": "Brass Monkey", 2428 "popularity": 68, 2429 "preview_url": null, 2430 "track_number": 11, 2431 "type": "track", 2432 "uri": "spotify:track:2tY1gxCKslfXLFpFofYmJQ" 2433 }, 2434 { 2435 "album": { 2436 "album_type": "ALBUM", 2437 "artists": [ 2438 { 2439 "external_urls": { 2440 "spotify": "https://open.spotify.com/artist/5Q81rlcTFh3k6DQJXPdsot" 2441 }, 2442 "href": "https://api.spotify.com/v1/artists/5Q81rlcTFh3k6DQJXPdsot", 2443 "id": "5Q81rlcTFh3k6DQJXPdsot", 2444 "name": "Mura Masa", 2445 "type": "artist", 2446 "uri": "spotify:artist:5Q81rlcTFh3k6DQJXPdsot" 2447 } 2448 ], 2449 "external_urls": { 2450 "spotify": "https://open.spotify.com/album/0NBTBo1qrg554sAj79nEqD" 2451 }, 2452 "href": "https://api.spotify.com/v1/albums/0NBTBo1qrg554sAj79nEqD", 2453 "id": "0NBTBo1qrg554sAj79nEqD", 2454 "images": [ 2455 { 2456 "height": 640, 2457 "url": "https://i.scdn.co/image/ab67616d0000b2736818aa231aa543cf87e1374a", 2458 "width": 640 2459 }, 2460 { 2461 "height": 300, 2462 "url": "https://i.scdn.co/image/ab67616d00001e026818aa231aa543cf87e1374a", 2463 "width": 300 2464 }, 2465 { 2466 "height": 64, 2467 "url": "https://i.scdn.co/image/ab67616d000048516818aa231aa543cf87e1374a", 2468 "width": 64 2469 } 2470 ], 2471 "is_playable": true, 2472 "name": "Mura Masa", 2473 "release_date": "2017-07-14", 2474 "release_date_precision": "day", 2475 "total_tracks": 13, 2476 "type": "album", 2477 "uri": "spotify:album:0NBTBo1qrg554sAj79nEqD" 2478 }, 2479 "artists": [ 2480 { 2481 "external_urls": { 2482 "spotify": "https://open.spotify.com/artist/5Q81rlcTFh3k6DQJXPdsot" 2483 }, 2484 "href": "https://api.spotify.com/v1/artists/5Q81rlcTFh3k6DQJXPdsot", 2485 "id": "5Q81rlcTFh3k6DQJXPdsot", 2486 "name": "Mura Masa", 2487 "type": "artist", 2488 "uri": "spotify:artist:5Q81rlcTFh3k6DQJXPdsot" 2489 }, 2490 { 2491 "external_urls": { 2492 "spotify": "https://open.spotify.com/artist/7pFeBzX627ff0VnN6bxPR4" 2493 }, 2494 "href": "https://api.spotify.com/v1/artists/7pFeBzX627ff0VnN6bxPR4", 2495 "id": "7pFeBzX627ff0VnN6bxPR4", 2496 "name": "Desiigner", 2497 "type": "artist", 2498 "uri": "spotify:artist:7pFeBzX627ff0VnN6bxPR4" 2499 } 2500 ], 2501 "disc_number": 1, 2502 "duration_ms": 164013, 2503 "explicit": true, 2504 "external_ids": { 2505 "isrc": "GBUM71701364" 2506 }, 2507 "external_urls": { 2508 "spotify": "https://open.spotify.com/track/4x8874idDYp8yxgKsyb4xG" 2509 }, 2510 "href": "https://api.spotify.com/v1/tracks/4x8874idDYp8yxgKsyb4xG", 2511 "id": "4x8874idDYp8yxgKsyb4xG", 2512 "is_local": false, 2513 "is_playable": true, 2514 "name": "All Around The World (feat. Desiigner)", 2515 "popularity": 42, 2516 "preview_url": null, 2517 "track_number": 5, 2518 "type": "track", 2519 "uri": "spotify:track:4x8874idDYp8yxgKsyb4xG" 2520 }, 2521 { 2522 "album": { 2523 "album_type": "SINGLE", 2524 "artists": [ 2525 { 2526 "external_urls": { 2527 "spotify": "https://open.spotify.com/artist/61lyPtntblHJvA7FMMhi7E" 2528 }, 2529 "href": "https://api.spotify.com/v1/artists/61lyPtntblHJvA7FMMhi7E", 2530 "id": "61lyPtntblHJvA7FMMhi7E", 2531 "name": "Duke Dumont", 2532 "type": "artist", 2533 "uri": "spotify:artist:61lyPtntblHJvA7FMMhi7E" 2534 } 2535 ], 2536 "external_urls": { 2537 "spotify": "https://open.spotify.com/album/4SQqe6ACemVTNNOcq7Ql4A" 2538 }, 2539 "href": "https://api.spotify.com/v1/albums/4SQqe6ACemVTNNOcq7Ql4A", 2540 "id": "4SQqe6ACemVTNNOcq7Ql4A", 2541 "images": [ 2542 { 2543 "height": 640, 2544 "url": "https://i.scdn.co/image/ab67616d0000b273be165dd0973a0db9607b3938", 2545 "width": 640 2546 }, 2547 { 2548 "height": 300, 2549 "url": "https://i.scdn.co/image/ab67616d00001e02be165dd0973a0db9607b3938", 2550 "width": 300 2551 }, 2552 { 2553 "height": 64, 2554 "url": "https://i.scdn.co/image/ab67616d00004851be165dd0973a0db9607b3938", 2555 "width": 64 2556 } 2557 ], 2558 "is_playable": true, 2559 "name": "The Giver (Reprise)", 2560 "release_date": "2015-03-17", 2561 "release_date_precision": "day", 2562 "total_tracks": 1, 2563 "type": "album", 2564 "uri": "spotify:album:4SQqe6ACemVTNNOcq7Ql4A" 2565 }, 2566 "artists": [ 2567 { 2568 "external_urls": { 2569 "spotify": "https://open.spotify.com/artist/61lyPtntblHJvA7FMMhi7E" 2570 }, 2571 "href": "https://api.spotify.com/v1/artists/61lyPtntblHJvA7FMMhi7E", 2572 "id": "61lyPtntblHJvA7FMMhi7E", 2573 "name": "Duke Dumont", 2574 "type": "artist", 2575 "uri": "spotify:artist:61lyPtntblHJvA7FMMhi7E" 2576 } 2577 ], 2578 "disc_number": 1, 2579 "duration_ms": 195756, 2580 "explicit": false, 2581 "external_ids": { 2582 "isrc": "GBUM71501517" 2583 }, 2584 "external_urls": { 2585 "spotify": "https://open.spotify.com/track/0ccSl4LZ7dksMNmJgkN7NO" 2586 }, 2587 "href": "https://api.spotify.com/v1/tracks/0ccSl4LZ7dksMNmJgkN7NO", 2588 "id": "0ccSl4LZ7dksMNmJgkN7NO", 2589 "is_local": false, 2590 "is_playable": true, 2591 "name": "The Giver (Reprise)", 2592 "popularity": 43, 2593 "preview_url": null, 2594 "track_number": 1, 2595 "type": "track", 2596 "uri": "spotify:track:0ccSl4LZ7dksMNmJgkN7NO" 2597 }, 2598 { 2599 "album": { 2600 "album_type": "ALBUM", 2601 "artists": [ 2602 { 2603 "external_urls": { 2604 "spotify": "https://open.spotify.com/artist/4NHQUGzhtTLFvgF5SZesLK" 2605 }, 2606 "href": "https://api.spotify.com/v1/artists/4NHQUGzhtTLFvgF5SZesLK", 2607 "id": "4NHQUGzhtTLFvgF5SZesLK", 2608 "name": "Tove Lo", 2609 "type": "artist", 2610 "uri": "spotify:artist:4NHQUGzhtTLFvgF5SZesLK" 2611 } 2612 ], 2613 "external_urls": { 2614 "spotify": "https://open.spotify.com/album/1tuekzsMZQOuiMejKP6t2Y" 2615 }, 2616 "href": "https://api.spotify.com/v1/albums/1tuekzsMZQOuiMejKP6t2Y", 2617 "id": "1tuekzsMZQOuiMejKP6t2Y", 2618 "images": [ 2619 { 2620 "height": 640, 2621 "url": "https://i.scdn.co/image/ab67616d0000b2739f0c014998bac13d3181474c", 2622 "width": 640 2623 }, 2624 { 2625 "height": 300, 2626 "url": "https://i.scdn.co/image/ab67616d00001e029f0c014998bac13d3181474c", 2627 "width": 300 2628 }, 2629 { 2630 "height": 64, 2631 "url": "https://i.scdn.co/image/ab67616d000048519f0c014998bac13d3181474c", 2632 "width": 64 2633 } 2634 ], 2635 "is_playable": true, 2636 "name": "Lady Wood", 2637 "release_date": "2016-10-28", 2638 "release_date_precision": "day", 2639 "total_tracks": 12, 2640 "type": "album", 2641 "uri": "spotify:album:1tuekzsMZQOuiMejKP6t2Y" 2642 }, 2643 "artists": [ 2644 { 2645 "external_urls": { 2646 "spotify": "https://open.spotify.com/artist/4NHQUGzhtTLFvgF5SZesLK" 2647 }, 2648 "href": "https://api.spotify.com/v1/artists/4NHQUGzhtTLFvgF5SZesLK", 2649 "id": "4NHQUGzhtTLFvgF5SZesLK", 2650 "name": "Tove Lo", 2651 "type": "artist", 2652 "uri": "spotify:artist:4NHQUGzhtTLFvgF5SZesLK" 2653 } 2654 ], 2655 "disc_number": 1, 2656 "duration_ms": 234474, 2657 "explicit": true, 2658 "external_ids": { 2659 "isrc": "SEUM71601201" 2660 }, 2661 "external_urls": { 2662 "spotify": "https://open.spotify.com/track/073A1FsNWqMxmdcRMeU57t" 2663 }, 2664 "href": "https://api.spotify.com/v1/tracks/073A1FsNWqMxmdcRMeU57t", 2665 "id": "073A1FsNWqMxmdcRMeU57t", 2666 "is_local": false, 2667 "is_playable": true, 2668 "name": "Don’t Talk About It", 2669 "popularity": 39, 2670 "preview_url": null, 2671 "track_number": 8, 2672 "type": "track", 2673 "uri": "spotify:track:073A1FsNWqMxmdcRMeU57t" 2674 }, 2675 { 2676 "album": { 2677 "album_type": "SINGLE", 2678 "artists": [ 2679 { 2680 "external_urls": { 2681 "spotify": "https://open.spotify.com/artist/6y02TEMv71ArWB2qhIaQ5m" 2682 }, 2683 "href": "https://api.spotify.com/v1/artists/6y02TEMv71ArWB2qhIaQ5m", 2684 "id": "6y02TEMv71ArWB2qhIaQ5m", 2685 "name": "Kaiydo", 2686 "type": "artist", 2687 "uri": "spotify:artist:6y02TEMv71ArWB2qhIaQ5m" 2688 } 2689 ], 2690 "external_urls": { 2691 "spotify": "https://open.spotify.com/album/0oeJCHQwiz5dQhMuiFlhhM" 2692 }, 2693 "href": "https://api.spotify.com/v1/albums/0oeJCHQwiz5dQhMuiFlhhM", 2694 "id": "0oeJCHQwiz5dQhMuiFlhhM", 2695 "images": [ 2696 { 2697 "height": 640, 2698 "url": "https://i.scdn.co/image/ab67616d0000b2731927f0378a81361fb80e20d6", 2699 "width": 640 2700 }, 2701 { 2702 "height": 300, 2703 "url": "https://i.scdn.co/image/ab67616d00001e021927f0378a81361fb80e20d6", 2704 "width": 300 2705 }, 2706 { 2707 "height": 64, 2708 "url": "https://i.scdn.co/image/ab67616d000048511927f0378a81361fb80e20d6", 2709 "width": 64 2710 } 2711 ], 2712 "is_playable": true, 2713 "name": "Fruit Punch", 2714 "release_date": "2016-08-12", 2715 "release_date_precision": "day", 2716 "total_tracks": 1, 2717 "type": "album", 2718 "uri": "spotify:album:0oeJCHQwiz5dQhMuiFlhhM" 2719 }, 2720 "artists": [ 2721 { 2722 "external_urls": { 2723 "spotify": "https://open.spotify.com/artist/6y02TEMv71ArWB2qhIaQ5m" 2724 }, 2725 "href": "https://api.spotify.com/v1/artists/6y02TEMv71ArWB2qhIaQ5m", 2726 "id": "6y02TEMv71ArWB2qhIaQ5m", 2727 "name": "Kaiydo", 2728 "type": "artist", 2729 "uri": "spotify:artist:6y02TEMv71ArWB2qhIaQ5m" 2730 } 2731 ], 2732 "disc_number": 1, 2733 "duration_ms": 221622, 2734 "explicit": true, 2735 "external_ids": { 2736 "isrc": "TCACR1606159" 2737 }, 2738 "external_urls": { 2739 "spotify": "https://open.spotify.com/track/0lpRp9KzzMbC0w1uiL7H7f" 2740 }, 2741 "href": "https://api.spotify.com/v1/tracks/0lpRp9KzzMbC0w1uiL7H7f", 2742 "id": "0lpRp9KzzMbC0w1uiL7H7f", 2743 "is_local": false, 2744 "is_playable": true, 2745 "name": "Fruit Punch", 2746 "popularity": 48, 2747 "preview_url": "https://p.scdn.co/mp3-preview/62b43baeb1993236dfc7e78557996514f64a3d9f?cid=b800bf59070c4b52a8917d2230d25bdc", 2748 "track_number": 1, 2749 "type": "track", 2750 "uri": "spotify:track:0lpRp9KzzMbC0w1uiL7H7f" 2751 }, 2752 { 2753 "album": { 2754 "album_type": "SINGLE", 2755 "artists": [ 2756 { 2757 "external_urls": { 2758 "spotify": "https://open.spotify.com/artist/21dooacK2WGBB5amYvKyfM" 2759 }, 2760 "href": "https://api.spotify.com/v1/artists/21dooacK2WGBB5amYvKyfM", 2761 "id": "21dooacK2WGBB5amYvKyfM", 2762 "name": "Smokepurpp", 2763 "type": "artist", 2764 "uri": "spotify:artist:21dooacK2WGBB5amYvKyfM" 2765 } 2766 ], 2767 "external_urls": { 2768 "spotify": "https://open.spotify.com/album/2TeGNWZYaj9yeM0mjiO8zL" 2769 }, 2770 "href": "https://api.spotify.com/v1/albums/2TeGNWZYaj9yeM0mjiO8zL", 2771 "id": "2TeGNWZYaj9yeM0mjiO8zL", 2772 "images": [ 2773 { 2774 "height": 640, 2775 "url": "https://i.scdn.co/image/ab67616d0000b273069c2ee3bae4205c1fed2981", 2776 "width": 640 2777 }, 2778 { 2779 "height": 300, 2780 "url": "https://i.scdn.co/image/ab67616d00001e02069c2ee3bae4205c1fed2981", 2781 "width": 300 2782 }, 2783 { 2784 "height": 64, 2785 "url": "https://i.scdn.co/image/ab67616d00004851069c2ee3bae4205c1fed2981", 2786 "width": 64 2787 } 2788 ], 2789 "is_playable": true, 2790 "name": "Nephew (feat. Lil Pump)", 2791 "release_date": "2018-07-27", 2792 "release_date_precision": "day", 2793 "total_tracks": 1, 2794 "type": "album", 2795 "uri": "spotify:album:2TeGNWZYaj9yeM0mjiO8zL" 2796 }, 2797 "artists": [ 2798 { 2799 "external_urls": { 2800 "spotify": "https://open.spotify.com/artist/21dooacK2WGBB5amYvKyfM" 2801 }, 2802 "href": "https://api.spotify.com/v1/artists/21dooacK2WGBB5amYvKyfM", 2803 "id": "21dooacK2WGBB5amYvKyfM", 2804 "name": "Smokepurpp", 2805 "type": "artist", 2806 "uri": "spotify:artist:21dooacK2WGBB5amYvKyfM" 2807 }, 2808 { 2809 "external_urls": { 2810 "spotify": "https://open.spotify.com/artist/3wyVrVrFCkukjdVIdirGVY" 2811 }, 2812 "href": "https://api.spotify.com/v1/artists/3wyVrVrFCkukjdVIdirGVY", 2813 "id": "3wyVrVrFCkukjdVIdirGVY", 2814 "name": "Lil Pump", 2815 "type": "artist", 2816 "uri": "spotify:artist:3wyVrVrFCkukjdVIdirGVY" 2817 } 2818 ], 2819 "disc_number": 1, 2820 "duration_ms": 202962, 2821 "explicit": true, 2822 "external_ids": { 2823 "isrc": "USUM71810095" 2824 }, 2825 "external_urls": { 2826 "spotify": "https://open.spotify.com/track/67KvputYgCRghvDQYj5FMq" 2827 }, 2828 "href": "https://api.spotify.com/v1/tracks/67KvputYgCRghvDQYj5FMq", 2829 "id": "67KvputYgCRghvDQYj5FMq", 2830 "is_local": false, 2831 "is_playable": true, 2832 "linked_from": { 2833 "external_urls": { 2834 "spotify": "https://open.spotify.com/track/0ibOcbkp2XG46Do8jcy0bL" 2835 }, 2836 "href": "https://api.spotify.com/v1/tracks/0ibOcbkp2XG46Do8jcy0bL", 2837 "id": "0ibOcbkp2XG46Do8jcy0bL", 2838 "type": "track", 2839 "uri": "spotify:track:0ibOcbkp2XG46Do8jcy0bL" 2840 }, 2841 "name": "Nephew (feat. Lil Pump)", 2842 "popularity": 50, 2843 "preview_url": "https://p.scdn.co/mp3-preview/bc901043dd4bcb1c43f6e7b1642e2d9d93b9539a?cid=b800bf59070c4b52a8917d2230d25bdc", 2844 "track_number": 1, 2845 "type": "track", 2846 "uri": "spotify:track:67KvputYgCRghvDQYj5FMq" 2847 }, 2848 { 2849 "album": { 2850 "album_type": "ALBUM", 2851 "artists": [ 2852 { 2853 "external_urls": { 2854 "spotify": "https://open.spotify.com/artist/5xtqw2B8z8JGfDYi2eAZHI" 2855 }, 2856 "href": "https://api.spotify.com/v1/artists/5xtqw2B8z8JGfDYi2eAZHI", 2857 "id": "5xtqw2B8z8JGfDYi2eAZHI", 2858 "name": "Sonique", 2859 "type": "artist", 2860 "uri": "spotify:artist:5xtqw2B8z8JGfDYi2eAZHI" 2861 } 2862 ], 2863 "external_urls": { 2864 "spotify": "https://open.spotify.com/album/4LX27S3cszKEJW84BGa1Ff" 2865 }, 2866 "href": "https://api.spotify.com/v1/albums/4LX27S3cszKEJW84BGa1Ff", 2867 "id": "4LX27S3cszKEJW84BGa1Ff", 2868 "images": [ 2869 { 2870 "height": 640, 2871 "url": "https://i.scdn.co/image/ab67616d0000b2732b30281b18d808c60135837c", 2872 "width": 640 2873 }, 2874 { 2875 "height": 300, 2876 "url": "https://i.scdn.co/image/ab67616d00001e022b30281b18d808c60135837c", 2877 "width": 300 2878 }, 2879 { 2880 "height": 64, 2881 "url": "https://i.scdn.co/image/ab67616d000048512b30281b18d808c60135837c", 2882 "width": 64 2883 } 2884 ], 2885 "is_playable": true, 2886 "name": "Hear My Cry", 2887 "release_date": "2000-08-24", 2888 "release_date_precision": "day", 2889 "total_tracks": 13, 2890 "type": "album", 2891 "uri": "spotify:album:4LX27S3cszKEJW84BGa1Ff" 2892 }, 2893 "artists": [ 2894 { 2895 "external_urls": { 2896 "spotify": "https://open.spotify.com/artist/5xtqw2B8z8JGfDYi2eAZHI" 2897 }, 2898 "href": "https://api.spotify.com/v1/artists/5xtqw2B8z8JGfDYi2eAZHI", 2899 "id": "5xtqw2B8z8JGfDYi2eAZHI", 2900 "name": "Sonique", 2901 "type": "artist", 2902 "uri": "spotify:artist:5xtqw2B8z8JGfDYi2eAZHI" 2903 } 2904 ], 2905 "disc_number": 1, 2906 "duration_ms": 240866, 2907 "explicit": false, 2908 "external_ids": { 2909 "isrc": "USUR10300602" 2910 }, 2911 "external_urls": { 2912 "spotify": "https://open.spotify.com/track/4Y8q64VnhD0vFYy9g2WFpi" 2913 }, 2914 "href": "https://api.spotify.com/v1/tracks/4Y8q64VnhD0vFYy9g2WFpi", 2915 "id": "4Y8q64VnhD0vFYy9g2WFpi", 2916 "is_local": false, 2917 "is_playable": true, 2918 "name": "It Feels So Good", 2919 "popularity": 67, 2920 "preview_url": "https://p.scdn.co/mp3-preview/028068f83d3d988b1661c03e93ab0651f6dd5957?cid=b800bf59070c4b52a8917d2230d25bdc", 2921 "track_number": 1, 2922 "type": "track", 2923 "uri": "spotify:track:4Y8q64VnhD0vFYy9g2WFpi" 2924 } 2925 ], 2926 "seeds": [ 2927 { 2928 "initialPoolSize": 378, 2929 "afterFilteringSize": 378, 2930 "afterRelinkingSize": 378, 2931 "id": "4NHQUGzhtTLFvgF5SZesLK", 2932 "type": "ARTIST", 2933 "href": "https://api.spotify.com/v1/artists/4NHQUGzhtTLFvgF5SZesLK" 2934 }, 2935 { 2936 "initialPoolSize": 400, 2937 "afterFilteringSize": 400, 2938 "afterRelinkingSize": 400, 2939 "id": "0c6xIDDpzE81m2q797ordA", 2940 "type": "TRACK", 2941 "href": "https://api.spotify.com/v1/tracks/0c6xIDDpzE81m2q797ordA" 2942 }, 2943 { 2944 "initialPoolSize": 404, 2945 "afterFilteringSize": 404, 2946 "afterRelinkingSize": 404, 2947 "id": "classical", 2948 "type": "GENRE", 2949 "href": null 2950 }, 2951 { 2952 "initialPoolSize": 369, 2953 "afterFilteringSize": 369, 2954 "afterRelinkingSize": 369, 2955 "id": "hip-hop", 2956 "type": "GENRE", 2957 "href": null 2958 } 2959 ] 2960} 2961"""
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
listofstrrepresenting 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
listof 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
listof 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
listof 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
listof 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
listof 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
listof 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
listof 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
listof 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
listof 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): IfTrueit will generate an HTML version (for an email or web page) and ifFalse(default) will generate a string to print in Python.
Returns:
- a
strthat 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
strwith 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): IfTrueit will generate an HTML version (for an email or web page) and ifFalse(default) will generate a string to print in Python.
Returns:
- a
strthat 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
listof tracks (dictionaries)