new data model: add Spotify

This commit is contained in:
Your Name 2022-12-08 15:45:37 +00:00
parent 3a95e5fc5c
commit 997e5fad0d
9 changed files with 501 additions and 6 deletions

View file

@ -5,4 +5,4 @@ from .scrapers import *
from . import jsondata from . import jsondata
__all__ = ('IdType', 'Item', 'ExternalPage', 'PageData', 'ParseError', 'ScraperMixin', 'AbstractSite', 'SiteList', 'jsondata', 'PrimaryLookupIdDescriptor', 'LookupIdDescriptor', 'setMockMode', 'use_local_response', 'RetryDownloader', 'BasicDownloader', 'ProxiedDownloader', 'BasicImageDownloader', 'RESPONSE_OK', 'RESPONSE_NETWORK_ERROR', 'RESPONSE_INVALID_CONTENT', 'RESPONSE_CENSORSHIP') __all__ = ('IdType', 'Item', 'ExternalPage', 'PageData', 'ParseError', 'ScraperMixin', 'AbstractSite', 'SiteList', 'jsondata', 'PrimaryLookupIdDescriptor', 'LookupIdDescriptor', 'setMockMode', 'getMockMode', 'use_local_response', 'RetryDownloader', 'BasicDownloader', 'ProxiedDownloader', 'BasicImageDownloader', 'RESPONSE_OK', 'RESPONSE_NETWORK_ERROR', 'RESPONSE_INVALID_CONTENT', 'RESPONSE_CENSORSHIP')

View file

@ -34,6 +34,11 @@ def setMockMode(enabled):
MockMode = enabled MockMode = enabled
def getMockMode():
global MockMode
return MockMode
class DownloadError(Exception): class DownloadError(Exception):
def __init__(self, downloader): def __init__(self, downloader):
self.url = downloader.url self.url = downloader.url
@ -63,10 +68,12 @@ class BasicDownloader:
'Cache-Control': 'no-cache', 'Cache-Control': 'no-cache',
} }
def __init__(self, url): def __init__(self, url, headers=None):
self.url = url self.url = url
self.response_type = RESPONSE_OK self.response_type = RESPONSE_OK
self.logs = [] self.logs = []
if headers:
self.headers = headers
def get_timeout(self): def get_timeout(self):
return settings.SCRAPING_TIMEOUT return settings.SCRAPING_TIMEOUT

View file

@ -18,7 +18,7 @@ class IdType(models.TextChoices):
ISSN = 'issn', _('ISSN') ISSN = 'issn', _('ISSN')
CUBN = 'cubn', _('统一书号') CUBN = 'cubn', _('统一书号')
ISRC = 'isrc', _('ISRC') # only for songs ISRC = 'isrc', _('ISRC') # only for songs
UPC = 'upc', _('GTIN UPC EAN码') GTIN = 'gtin', _('GTIN UPC EAN码') # ISBN is separate
Feed = 'feed', _('Feed URL') Feed = 'feed', _('Feed URL')
IMDB = 'imdb', _('IMDb') IMDB = 'imdb', _('IMDb')
TMDB_TV = 'tmdb_tv', _('TMDB剧集') TMDB_TV = 'tmdb_tv', _('TMDB剧集')
@ -37,6 +37,9 @@ class IdType(models.TextChoices):
Bandcamp = 'bandcamp', _('Bandcamp') Bandcamp = 'bandcamp', _('Bandcamp')
Spotify_Album = 'spotify_album', _('Spotify专辑') Spotify_Album = 'spotify_album', _('Spotify专辑')
Spotify_Show = 'spotify_show', _('Spotify播客') Spotify_Show = 'spotify_show', _('Spotify播客')
Discogs_Release = 'discogs_release', ('Discogs Release')
Discogs_Master = 'discogs_master', ('Discogs Master')
MusicBrainz = 'musicbrainz', ('MusicBrainz ID')
DoubanBook_Author = 'doubanbook_author', _('豆瓣读书作者') DoubanBook_Author = 'doubanbook_author', _('豆瓣读书作者')
DoubanCelebrity = 'doubanmovie_celebrity', _('豆瓣电影影人') DoubanCelebrity = 'doubanmovie_celebrity', _('豆瓣电影影人')
Goodreads_Author = 'goodreads_author', _('Goodreads作者') Goodreads_Author = 'goodreads_author', _('Goodreads作者')

View file

@ -2,9 +2,9 @@ from catalog.common import *
class Album(Item): class Album(Item):
upc = LookupIdDescriptor(IdType.UPC) barcode = PrimaryLookupIdDescriptor(IdType.GTIN)
douban_music = LookupIdDescriptor(IdType.DoubanMusic) douban_music = PrimaryLookupIdDescriptor(IdType.DoubanMusic)
spotify_album = LookupIdDescriptor(IdType.Spotify_Album) spotify_album = PrimaryLookupIdDescriptor(IdType.Spotify_Album)
class Meta: class Meta:
proxy = True proxy = True

27
catalog/music/tests.py Normal file
View file

@ -0,0 +1,27 @@
from django.test import TestCase
from catalog.common import *
from catalog.models import *
class SpotifyTestCase(TestCase):
def test_parse(self):
t_id_type = IdType.Spotify_Album
t_id_value = '65KwtzkJXw7oT819NFWmEP'
t_url = 'https://open.spotify.com/album/65KwtzkJXw7oT819NFWmEP'
site = SiteList.get_site_by_id_type(t_id_type)
self.assertIsNotNone(site)
self.assertEqual(site.validate_url(t_url), True)
site = SiteList.get_site_by_url(t_url)
self.assertEqual(site.url, t_url)
self.assertEqual(site.id_value, t_id_value)
@use_local_response
def test_scrape(self):
t_url = 'https://open.spotify.com/album/65KwtzkJXw7oT819NFWmEP'
site = SiteList.get_site_by_url(t_url)
self.assertEqual(site.ready, False)
site.get_page_ready()
self.assertEqual(site.ready, True)
self.assertEqual(site.page.metadata['title'], 'The Race For Space')
self.assertIsInstance(site.page.item, Album)
self.assertEqual(site.page.item.barcode, '3610159662676')

View file

@ -6,3 +6,4 @@ from .douban_drama import DoubanDrama
from .goodreads import Goodreads from .goodreads import Goodreads
from .tmdb import TMDB_Movie from .tmdb import TMDB_Movie
from .imdb import IMDB from .imdb import IMDB
from .spotify import Spotify

153
catalog/sites/spotify.py Normal file
View file

@ -0,0 +1,153 @@
"""
Spotify
"""
from django.conf import settings
from catalog.common import *
from catalog.models import *
from .douban import *
import time
import datetime
import requests
import dateparser
import logging
_logger = logging.getLogger(__name__)
spotify_token = None
spotify_token_expire_time = time.time()
@SiteList.register
class Spotify(AbstractSite):
ID_TYPE = IdType.Spotify_Album
URL_PATTERNS = [r'\w+://open\.spotify\.com/album/([a-zA-Z0-9]+)']
WIKI_PROPERTY_ID = '?'
DEFAULT_MODEL = Album
@classmethod
def id_to_url(self, id_value):
return f"https://open.spotify.com/album/{id_value}"
def scrape(self):
api_url = "https://api.spotify.com/v1/albums/" + self.id_value
headers = {
'Authorization': f"Bearer {get_spotify_token()}"
}
res_data = BasicDownloader(api_url, headers=headers).download().json()
artist = []
for artist_dict in res_data['artists']:
artist.append(artist_dict['name'])
title = res_data['name']
genre = ', '.join(res_data['genres'])
company = []
for com in res_data['copyrights']:
company.append(com['text'])
duration = 0
track_list = []
track_urls = []
for track in res_data['tracks']['items']:
track_urls.append(track['external_urls']['spotify'])
duration += track['duration_ms']
if res_data['tracks']['items'][-1]['disc_number'] > 1:
# more than one disc
track_list.append(str(
track['disc_number']) + '-' + str(track['track_number']) + '. ' + track['name'])
else:
track_list.append(str(track['track_number']) + '. ' + track['name'])
track_list = '\n'.join(track_list)
release_date = parse_date(res_data['release_date']).strftime('%Y-%m-%d')
gtin = None
if res_data['external_ids'].get('upc'):
gtin = res_data['external_ids'].get('upc')
if res_data['external_ids'].get('ean'):
gtin = res_data['external_ids'].get('ean')
# isrc = None
# if res_data['external_ids'].get('isrc'):
# isrc = res_data['external_ids'].get('isrc')
# _logger.error('isrc for album? this should not happen')
pd = PageData(metadata={
'title': title,
'artist': artist,
'genre': genre,
'track_list': track_list,
'release_date': release_date,
'duration': duration,
'company': company,
'brief': None,
'cover_image_url': res_data['images'][0]['url']
})
if gtin:
pd.lookup_ids[IdType.GTIN] = gtin
if pd.metadata["cover_image_url"]:
imgdl = BasicImageDownloader(pd.metadata["cover_image_url"], self.url)
try:
pd.cover_image = imgdl.download().content
pd.cover_image_extention = imgdl.extention
except Exception:
_logger.debug(f'failed to download cover for {self.url} from {pd.metadata["cover_image_url"]}')
return pd
def get_spotify_token():
global spotify_token, spotify_token_expire_time
if getMockMode():
return 'mocked'
if spotify_token is None or is_spotify_token_expired():
invoke_spotify_token()
return spotify_token
def is_spotify_token_expired():
global spotify_token_expire_time
return True if spotify_token_expire_time <= time.time() else False
def invoke_spotify_token():
global spotify_token, spotify_token_expire_time
r = requests.post(
"https://accounts.spotify.com/api/token",
data={
"grant_type": "client_credentials"
},
headers={
"Authorization": f"Basic {settings.SPOTIFY_CREDENTIAL}"
}
)
data = r.json()
if r.status_code == 401:
# token expired, try one more time
# this maybe caused by external operations,
# for example debugging using a http client
r = requests.post(
"https://accounts.spotify.com/api/token",
data={
"grant_type": "client_credentials"
},
headers={
"Authorization": f"Basic {settings.SPOTIFY_CREDENTIAL}"
}
)
data = r.json()
elif r.status_code != 200:
raise Exception(f"Request to spotify API fails. Reason: {r.reason}")
# minus 2 for execution time error
spotify_token_expire_time = int(data['expires_in']) + time.time() - 2
spotify_token = data['access_token']
def parse_date(raw_str):
return dateparser.parse(
raw_str,
settings={
"RELATIVE_BASE": datetime.datetime(1900, 1, 1)
}
)

View file

@ -2,6 +2,7 @@ from django.test import TestCase
from catalog.book.tests import * from catalog.book.tests import *
from catalog.movie.tests import * from catalog.movie.tests import *
from catalog.tv.tests import * from catalog.tv.tests import *
from catalog.music.tests import *
from catalog.podcast.tests import * from catalog.podcast.tests import *
from catalog.performance.tests import * from catalog.performance.tests import *

View file

@ -0,0 +1,303 @@
{
"album_type" : "album",
"artists" : [ {
"external_urls" : {
"spotify" : "https://open.spotify.com/artist/6VsiDFMZJlJ053P1uO4A6h"
},
"href" : "https://api.spotify.com/v1/artists/6VsiDFMZJlJ053P1uO4A6h",
"id" : "6VsiDFMZJlJ053P1uO4A6h",
"name" : "Public Service Broadcasting",
"type" : "artist",
"uri" : "spotify:artist:6VsiDFMZJlJ053P1uO4A6h"
} ],
"available_markets" : [ "AD", "AE", "AG", "AL", "AM", "AO", "AR", "AT", "AZ", "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", "BJ", "BN", "BO", "BR", "BS", "BT", "BW", "BY", "BZ", "CA", "CD", "CG", "CH", "CI", "CL", "CM", "CO", "CR", "CV", "CW", "CY", "CZ", "DE", "DJ", "DK", "DM", "DO", "DZ", "EC", "EE", "EG", "ES", "FI", "FR", "GA", "GB", "GD", "GE", "GH", "GM", "GN", "GQ", "GR", "GT", "GW", "GY", "HN", "HR", "HT", "HU", "ID", "IE", "IL", "IN", "IQ", "IS", "IT", "JM", "JO", "KE", "KG", "KH", "KM", "KN", "KR", "KW", "KZ", "LA", "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU", "LV", "LY", "MA", "MC", "MD", "MG", "MK", "ML", "MN", "MO", "MR", "MT", "MU", "MV", "MW", "MX", "MZ", "NA", "NE", "NG", "NI", "NL", "NO", "NP", "NZ", "OM", "PA", "PE", "PG", "PH", "PK", "PL", "PS", "PT", "PW", "PY", "QA", "RO", "RW", "SA", "SC", "SE", "SI", "SK", "SL", "SM", "SN", "SR", "ST", "SV", "SZ", "TD", "TG", "TH", "TJ", "TL", "TN", "TR", "TT", "TZ", "UA", "UG", "US", "UY", "UZ", "VC", "VE", "VN", "ZA", "ZM", "ZW" ],
"copyrights" : [ {
"text" : "Test Card Recordings",
"type" : "C"
}, {
"text" : "Test Card Recordings",
"type" : "P"
} ],
"external_ids" : {
"upc" : "3610159662676"
},
"external_urls" : {
"spotify" : "https://open.spotify.com/album/65KwtzkJXw7oT819NFWmEP"
},
"genres" : [ ],
"href" : "https://api.spotify.com/v1/albums/65KwtzkJXw7oT819NFWmEP",
"id" : "65KwtzkJXw7oT819NFWmEP",
"images" : [ {
"height" : 640,
"url" : "https://i.scdn.co/image/ab67616d0000b273123ebfc7ca99a9bb6342cd36",
"width" : 640
}, {
"height" : 300,
"url" : "https://i.scdn.co/image/ab67616d00001e02123ebfc7ca99a9bb6342cd36",
"width" : 300
}, {
"height" : 64,
"url" : "https://i.scdn.co/image/ab67616d00004851123ebfc7ca99a9bb6342cd36",
"width" : 64
} ],
"label" : "Test Card Recordings",
"name" : "The Race For Space",
"popularity" : 44,
"release_date" : "2014",
"release_date_precision" : "year",
"total_tracks" : 9,
"tracks" : {
"href" : "https://api.spotify.com/v1/albums/65KwtzkJXw7oT819NFWmEP/tracks?offset=0&limit=50",
"items" : [ {
"artists" : [ {
"external_urls" : {
"spotify" : "https://open.spotify.com/artist/6VsiDFMZJlJ053P1uO4A6h"
},
"href" : "https://api.spotify.com/v1/artists/6VsiDFMZJlJ053P1uO4A6h",
"id" : "6VsiDFMZJlJ053P1uO4A6h",
"name" : "Public Service Broadcasting",
"type" : "artist",
"uri" : "spotify:artist:6VsiDFMZJlJ053P1uO4A6h"
} ],
"available_markets" : [ "AD", "AE", "AG", "AL", "AM", "AO", "AR", "AT", "AZ", "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", "BJ", "BN", "BO", "BR", "BS", "BT", "BW", "BY", "BZ", "CA", "CD", "CG", "CH", "CI", "CL", "CM", "CO", "CR", "CV", "CW", "CY", "CZ", "DE", "DJ", "DK", "DM", "DO", "DZ", "EC", "EE", "EG", "ES", "ET", "FI", "FR", "GA", "GB", "GD", "GE", "GH", "GM", "GN", "GQ", "GR", "GT", "GW", "GY", "HN", "HR", "HT", "HU", "ID", "IE", "IL", "IN", "IQ", "IS", "IT", "JM", "JO", "KE", "KG", "KH", "KM", "KN", "KR", "KW", "KZ", "LA", "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU", "LV", "LY", "MA", "MC", "MD", "MG", "MK", "ML", "MN", "MO", "MR", "MT", "MU", "MV", "MW", "MX", "MZ", "NA", "NE", "NG", "NI", "NL", "NO", "NP", "NZ", "OM", "PA", "PE", "PG", "PH", "PK", "PL", "PS", "PT", "PW", "PY", "QA", "RO", "RW", "SA", "SC", "SE", "SI", "SK", "SL", "SM", "SN", "SR", "ST", "SV", "SZ", "TD", "TG", "TH", "TJ", "TL", "TN", "TR", "TT", "TZ", "UA", "UG", "US", "UY", "UZ", "VC", "VE", "VN", "ZA", "ZM", "ZW" ],
"disc_number" : 1,
"duration_ms" : 159859,
"explicit" : false,
"external_urls" : {
"spotify" : "https://open.spotify.com/track/3982V8R7oW3xyV8zASbCGG"
},
"href" : "https://api.spotify.com/v1/tracks/3982V8R7oW3xyV8zASbCGG",
"id" : "3982V8R7oW3xyV8zASbCGG",
"is_local" : false,
"name" : "The Race For Space",
"preview_url" : "https://p.scdn.co/mp3-preview/cc69663d5b6a7982e5f162e625f1b319b26956ec?cid=4b150d8d6d374d1e8dbb85f4f11a2ad9",
"track_number" : 1,
"type" : "track",
"uri" : "spotify:track:3982V8R7oW3xyV8zASbCGG"
}, {
"artists" : [ {
"external_urls" : {
"spotify" : "https://open.spotify.com/artist/6VsiDFMZJlJ053P1uO4A6h"
},
"href" : "https://api.spotify.com/v1/artists/6VsiDFMZJlJ053P1uO4A6h",
"id" : "6VsiDFMZJlJ053P1uO4A6h",
"name" : "Public Service Broadcasting",
"type" : "artist",
"uri" : "spotify:artist:6VsiDFMZJlJ053P1uO4A6h"
} ],
"available_markets" : [ "AD", "AE", "AG", "AL", "AM", "AO", "AR", "AT", "AZ", "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", "BJ", "BN", "BO", "BR", "BS", "BT", "BW", "BY", "BZ", "CA", "CD", "CG", "CH", "CI", "CL", "CM", "CO", "CR", "CV", "CW", "CY", "CZ", "DE", "DJ", "DK", "DM", "DO", "DZ", "EC", "EE", "EG", "ES", "ET", "FI", "FR", "GA", "GB", "GD", "GE", "GH", "GM", "GN", "GQ", "GR", "GT", "GW", "GY", "HN", "HR", "HT", "HU", "ID", "IE", "IL", "IN", "IQ", "IS", "IT", "JM", "JO", "KE", "KG", "KH", "KM", "KN", "KR", "KW", "KZ", "LA", "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU", "LV", "LY", "MA", "MC", "MD", "MG", "MK", "ML", "MN", "MO", "MR", "MT", "MU", "MV", "MW", "MX", "MZ", "NA", "NE", "NG", "NI", "NL", "NO", "NP", "NZ", "OM", "PA", "PE", "PG", "PH", "PK", "PL", "PS", "PT", "PW", "PY", "QA", "RO", "RW", "SA", "SC", "SE", "SI", "SK", "SL", "SM", "SN", "SR", "ST", "SV", "SZ", "TD", "TG", "TH", "TJ", "TL", "TN", "TR", "TT", "TZ", "UA", "UG", "US", "UY", "UZ", "VC", "VE", "VN", "ZA", "ZM", "ZW" ],
"disc_number" : 1,
"duration_ms" : 429374,
"explicit" : false,
"external_urls" : {
"spotify" : "https://open.spotify.com/track/4EhQrGzqi8k24qWIJuG5CH"
},
"href" : "https://api.spotify.com/v1/tracks/4EhQrGzqi8k24qWIJuG5CH",
"id" : "4EhQrGzqi8k24qWIJuG5CH",
"is_local" : false,
"name" : "Sputnik",
"preview_url" : "https://p.scdn.co/mp3-preview/32ccf0b8f7ef1251c35e97acb405e4e7cc2660d2?cid=4b150d8d6d374d1e8dbb85f4f11a2ad9",
"track_number" : 2,
"type" : "track",
"uri" : "spotify:track:4EhQrGzqi8k24qWIJuG5CH"
}, {
"artists" : [ {
"external_urls" : {
"spotify" : "https://open.spotify.com/artist/6VsiDFMZJlJ053P1uO4A6h"
},
"href" : "https://api.spotify.com/v1/artists/6VsiDFMZJlJ053P1uO4A6h",
"id" : "6VsiDFMZJlJ053P1uO4A6h",
"name" : "Public Service Broadcasting",
"type" : "artist",
"uri" : "spotify:artist:6VsiDFMZJlJ053P1uO4A6h"
} ],
"available_markets" : [ "AD", "AE", "AG", "AL", "AM", "AO", "AR", "AT", "AZ", "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", "BJ", "BN", "BO", "BR", "BS", "BT", "BW", "BY", "BZ", "CA", "CD", "CG", "CH", "CI", "CL", "CM", "CO", "CR", "CV", "CW", "CY", "CZ", "DE", "DJ", "DK", "DM", "DO", "DZ", "EC", "EE", "EG", "ES", "ET", "FI", "FR", "GA", "GB", "GD", "GE", "GH", "GM", "GN", "GQ", "GR", "GT", "GW", "GY", "HN", "HR", "HT", "HU", "ID", "IE", "IL", "IN", "IQ", "IS", "IT", "JM", "JO", "KE", "KG", "KH", "KM", "KN", "KR", "KW", "KZ", "LA", "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU", "LV", "LY", "MA", "MC", "MD", "MG", "MK", "ML", "MN", "MO", "MR", "MT", "MU", "MV", "MW", "MX", "MZ", "NA", "NE", "NG", "NI", "NL", "NO", "NP", "NZ", "OM", "PA", "PE", "PG", "PH", "PK", "PL", "PS", "PT", "PW", "PY", "QA", "RO", "RW", "SA", "SC", "SE", "SI", "SK", "SL", "SM", "SN", "SR", "ST", "SV", "SZ", "TD", "TG", "TH", "TJ", "TL", "TN", "TR", "TT", "TZ", "UA", "UG", "US", "UY", "UZ", "VC", "VE", "VN", "ZA", "ZM", "ZW" ],
"disc_number" : 1,
"duration_ms" : 228623,
"explicit" : false,
"external_urls" : {
"spotify" : "https://open.spotify.com/track/4IaRxPHdzLJ78tm7lxg9M8"
},
"href" : "https://api.spotify.com/v1/tracks/4IaRxPHdzLJ78tm7lxg9M8",
"id" : "4IaRxPHdzLJ78tm7lxg9M8",
"is_local" : false,
"name" : "Gagarin",
"preview_url" : "https://p.scdn.co/mp3-preview/1d91010dc50a73caa3831c4617f3d658ae279339?cid=4b150d8d6d374d1e8dbb85f4f11a2ad9",
"track_number" : 3,
"type" : "track",
"uri" : "spotify:track:4IaRxPHdzLJ78tm7lxg9M8"
}, {
"artists" : [ {
"external_urls" : {
"spotify" : "https://open.spotify.com/artist/6VsiDFMZJlJ053P1uO4A6h"
},
"href" : "https://api.spotify.com/v1/artists/6VsiDFMZJlJ053P1uO4A6h",
"id" : "6VsiDFMZJlJ053P1uO4A6h",
"name" : "Public Service Broadcasting",
"type" : "artist",
"uri" : "spotify:artist:6VsiDFMZJlJ053P1uO4A6h"
} ],
"available_markets" : [ "AD", "AE", "AG", "AL", "AM", "AO", "AR", "AT", "AZ", "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", "BJ", "BN", "BO", "BR", "BS", "BT", "BW", "BY", "BZ", "CA", "CD", "CG", "CH", "CI", "CL", "CM", "CO", "CR", "CV", "CW", "CY", "CZ", "DE", "DJ", "DK", "DM", "DO", "DZ", "EC", "EE", "EG", "ES", "ET", "FI", "FR", "GA", "GB", "GD", "GE", "GH", "GM", "GN", "GQ", "GR", "GT", "GW", "GY", "HN", "HR", "HT", "HU", "ID", "IE", "IL", "IN", "IQ", "IS", "IT", "JM", "JO", "KE", "KG", "KH", "KM", "KN", "KR", "KW", "KZ", "LA", "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU", "LV", "LY", "MA", "MC", "MD", "MG", "MK", "ML", "MN", "MO", "MR", "MT", "MU", "MV", "MW", "MX", "MZ", "NA", "NE", "NG", "NI", "NL", "NO", "NP", "NZ", "OM", "PA", "PE", "PG", "PH", "PK", "PL", "PS", "PT", "PW", "PY", "QA", "RO", "RW", "SA", "SC", "SE", "SI", "SK", "SL", "SM", "SN", "SR", "ST", "SV", "SZ", "TD", "TG", "TH", "TJ", "TL", "TN", "TR", "TT", "TZ", "UA", "UG", "US", "UY", "UZ", "VC", "VE", "VN", "ZA", "ZM", "ZW" ],
"disc_number" : 1,
"duration_ms" : 181621,
"explicit" : false,
"external_urls" : {
"spotify" : "https://open.spotify.com/track/6SONXH9dJQgDY9vCjdkZfK"
},
"href" : "https://api.spotify.com/v1/tracks/6SONXH9dJQgDY9vCjdkZfK",
"id" : "6SONXH9dJQgDY9vCjdkZfK",
"is_local" : false,
"name" : "Fire in the Cockpit",
"preview_url" : "https://p.scdn.co/mp3-preview/a2180cec25187fa80ddc80dcbe36edda1cc169cc?cid=4b150d8d6d374d1e8dbb85f4f11a2ad9",
"track_number" : 4,
"type" : "track",
"uri" : "spotify:track:6SONXH9dJQgDY9vCjdkZfK"
}, {
"artists" : [ {
"external_urls" : {
"spotify" : "https://open.spotify.com/artist/6VsiDFMZJlJ053P1uO4A6h"
},
"href" : "https://api.spotify.com/v1/artists/6VsiDFMZJlJ053P1uO4A6h",
"id" : "6VsiDFMZJlJ053P1uO4A6h",
"name" : "Public Service Broadcasting",
"type" : "artist",
"uri" : "spotify:artist:6VsiDFMZJlJ053P1uO4A6h"
} ],
"available_markets" : [ "AD", "AE", "AG", "AL", "AM", "AO", "AR", "AT", "AZ", "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", "BJ", "BN", "BO", "BR", "BS", "BT", "BW", "BY", "BZ", "CA", "CD", "CG", "CH", "CI", "CL", "CM", "CO", "CR", "CV", "CW", "CY", "CZ", "DE", "DJ", "DK", "DM", "DO", "DZ", "EC", "EE", "EG", "ES", "ET", "FI", "FR", "GA", "GB", "GD", "GE", "GH", "GM", "GN", "GQ", "GR", "GT", "GW", "GY", "HN", "HR", "HT", "HU", "ID", "IE", "IL", "IN", "IQ", "IS", "IT", "JM", "JO", "KE", "KG", "KH", "KM", "KN", "KR", "KW", "KZ", "LA", "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU", "LV", "LY", "MA", "MC", "MD", "MG", "MK", "ML", "MN", "MO", "MR", "MT", "MU", "MV", "MW", "MX", "MZ", "NA", "NE", "NG", "NI", "NL", "NO", "NP", "NZ", "OM", "PA", "PE", "PG", "PH", "PK", "PL", "PS", "PT", "PW", "PY", "QA", "RO", "RW", "SA", "SC", "SE", "SI", "SK", "SL", "SM", "SN", "SR", "ST", "SV", "SZ", "TD", "TG", "TH", "TJ", "TL", "TN", "TR", "TT", "TZ", "UA", "UG", "US", "UY", "UZ", "VC", "VE", "VN", "ZA", "ZM", "ZW" ],
"disc_number" : 1,
"duration_ms" : 255606,
"explicit" : false,
"external_urls" : {
"spotify" : "https://open.spotify.com/track/52KMWPHDL84oo2Ncj3O6RX"
},
"href" : "https://api.spotify.com/v1/tracks/52KMWPHDL84oo2Ncj3O6RX",
"id" : "52KMWPHDL84oo2Ncj3O6RX",
"is_local" : false,
"name" : "E.V.A.",
"preview_url" : "https://p.scdn.co/mp3-preview/732171a4a5e27540b6709602b4af9662fda98595?cid=4b150d8d6d374d1e8dbb85f4f11a2ad9",
"track_number" : 5,
"type" : "track",
"uri" : "spotify:track:52KMWPHDL84oo2Ncj3O6RX"
}, {
"artists" : [ {
"external_urls" : {
"spotify" : "https://open.spotify.com/artist/6VsiDFMZJlJ053P1uO4A6h"
},
"href" : "https://api.spotify.com/v1/artists/6VsiDFMZJlJ053P1uO4A6h",
"id" : "6VsiDFMZJlJ053P1uO4A6h",
"name" : "Public Service Broadcasting",
"type" : "artist",
"uri" : "spotify:artist:6VsiDFMZJlJ053P1uO4A6h"
} ],
"available_markets" : [ "AD", "AE", "AG", "AL", "AM", "AO", "AR", "AT", "AZ", "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", "BJ", "BN", "BO", "BR", "BS", "BT", "BW", "BY", "BZ", "CA", "CD", "CG", "CH", "CI", "CL", "CM", "CO", "CR", "CV", "CW", "CY", "CZ", "DE", "DJ", "DK", "DM", "DO", "DZ", "EC", "EE", "EG", "ES", "ET", "FI", "FR", "GA", "GB", "GD", "GE", "GH", "GM", "GN", "GQ", "GR", "GT", "GW", "GY", "HN", "HR", "HT", "HU", "ID", "IE", "IL", "IN", "IQ", "IS", "IT", "JM", "JO", "KE", "KG", "KH", "KM", "KN", "KR", "KW", "KZ", "LA", "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU", "LV", "LY", "MA", "MC", "MD", "MG", "MK", "ML", "MN", "MO", "MR", "MT", "MU", "MV", "MW", "MX", "MZ", "NA", "NE", "NG", "NI", "NL", "NO", "NP", "NZ", "OM", "PA", "PE", "PG", "PH", "PK", "PL", "PS", "PT", "PW", "PY", "QA", "RO", "RW", "SA", "SC", "SE", "SI", "SK", "SL", "SM", "SN", "SR", "ST", "SV", "SZ", "TD", "TG", "TH", "TJ", "TL", "TN", "TR", "TT", "TZ", "UA", "UG", "US", "UY", "UZ", "VC", "VE", "VN", "ZA", "ZM", "ZW" ],
"disc_number" : 1,
"duration_ms" : 379931,
"explicit" : false,
"external_urls" : {
"spotify" : "https://open.spotify.com/track/3jjMyq44OIjNgmpXLhpw7W"
},
"href" : "https://api.spotify.com/v1/tracks/3jjMyq44OIjNgmpXLhpw7W",
"id" : "3jjMyq44OIjNgmpXLhpw7W",
"is_local" : false,
"name" : "The Other Side",
"preview_url" : "https://p.scdn.co/mp3-preview/5eda4958044595b36842f2362799d91f080a7357?cid=4b150d8d6d374d1e8dbb85f4f11a2ad9",
"track_number" : 6,
"type" : "track",
"uri" : "spotify:track:3jjMyq44OIjNgmpXLhpw7W"
}, {
"artists" : [ {
"external_urls" : {
"spotify" : "https://open.spotify.com/artist/6VsiDFMZJlJ053P1uO4A6h"
},
"href" : "https://api.spotify.com/v1/artists/6VsiDFMZJlJ053P1uO4A6h",
"id" : "6VsiDFMZJlJ053P1uO4A6h",
"name" : "Public Service Broadcasting",
"type" : "artist",
"uri" : "spotify:artist:6VsiDFMZJlJ053P1uO4A6h"
}, {
"external_urls" : {
"spotify" : "https://open.spotify.com/artist/7wbZFLV3wwTqyrKNCJ8Y8D"
},
"href" : "https://api.spotify.com/v1/artists/7wbZFLV3wwTqyrKNCJ8Y8D",
"id" : "7wbZFLV3wwTqyrKNCJ8Y8D",
"name" : "Smoke Fairies",
"type" : "artist",
"uri" : "spotify:artist:7wbZFLV3wwTqyrKNCJ8Y8D"
} ],
"available_markets" : [ "AD", "AE", "AG", "AL", "AM", "AO", "AR", "AT", "AZ", "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", "BJ", "BN", "BO", "BR", "BS", "BT", "BW", "BY", "BZ", "CA", "CD", "CG", "CH", "CI", "CL", "CM", "CO", "CR", "CV", "CW", "CY", "CZ", "DE", "DJ", "DK", "DM", "DO", "DZ", "EC", "EE", "EG", "ES", "ET", "FI", "FR", "GA", "GB", "GD", "GE", "GH", "GM", "GN", "GQ", "GR", "GT", "GW", "GY", "HN", "HR", "HT", "HU", "ID", "IE", "IL", "IN", "IQ", "IS", "IT", "JM", "JO", "KE", "KG", "KH", "KM", "KN", "KR", "KW", "KZ", "LA", "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU", "LV", "LY", "MA", "MC", "MD", "MG", "MK", "ML", "MN", "MO", "MR", "MT", "MU", "MV", "MW", "MX", "MZ", "NA", "NE", "NG", "NI", "NL", "NO", "NP", "NZ", "OM", "PA", "PE", "PG", "PH", "PK", "PL", "PS", "PT", "PW", "PY", "QA", "RO", "RW", "SA", "SC", "SE", "SI", "SK", "SL", "SM", "SN", "SR", "ST", "SV", "SZ", "TD", "TG", "TH", "TJ", "TL", "TN", "TR", "TT", "TZ", "UA", "UG", "US", "UY", "UZ", "VC", "VE", "VN", "ZA", "ZM", "ZW" ],
"disc_number" : 1,
"duration_ms" : 269376,
"explicit" : false,
"external_urls" : {
"spotify" : "https://open.spotify.com/track/5Um9ghqMlKALp9AcRMIk7B"
},
"href" : "https://api.spotify.com/v1/tracks/5Um9ghqMlKALp9AcRMIk7B",
"id" : "5Um9ghqMlKALp9AcRMIk7B",
"is_local" : false,
"name" : "Valentina",
"preview_url" : "https://p.scdn.co/mp3-preview/9e812bde9e2944d22f1eae78eab2adb89ce1f1cd?cid=4b150d8d6d374d1e8dbb85f4f11a2ad9",
"track_number" : 7,
"type" : "track",
"uri" : "spotify:track:5Um9ghqMlKALp9AcRMIk7B"
}, {
"artists" : [ {
"external_urls" : {
"spotify" : "https://open.spotify.com/artist/6VsiDFMZJlJ053P1uO4A6h"
},
"href" : "https://api.spotify.com/v1/artists/6VsiDFMZJlJ053P1uO4A6h",
"id" : "6VsiDFMZJlJ053P1uO4A6h",
"name" : "Public Service Broadcasting",
"type" : "artist",
"uri" : "spotify:artist:6VsiDFMZJlJ053P1uO4A6h"
} ],
"available_markets" : [ "AD", "AE", "AG", "AL", "AM", "AO", "AR", "AT", "AZ", "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", "BJ", "BN", "BO", "BR", "BS", "BT", "BW", "BY", "BZ", "CA", "CD", "CG", "CH", "CI", "CL", "CM", "CO", "CR", "CV", "CW", "CY", "CZ", "DE", "DJ", "DK", "DM", "DO", "DZ", "EC", "EE", "EG", "ES", "ET", "FI", "FR", "GA", "GB", "GD", "GE", "GH", "GM", "GN", "GQ", "GR", "GT", "GW", "GY", "HN", "HR", "HT", "HU", "ID", "IE", "IL", "IN", "IQ", "IS", "IT", "JM", "JO", "KE", "KG", "KH", "KM", "KN", "KR", "KW", "KZ", "LA", "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU", "LV", "LY", "MA", "MC", "MD", "MG", "MK", "ML", "MN", "MO", "MR", "MT", "MU", "MV", "MW", "MX", "MZ", "NA", "NE", "NG", "NI", "NL", "NO", "NP", "NZ", "OM", "PA", "PE", "PG", "PH", "PK", "PL", "PS", "PT", "PW", "PY", "QA", "RO", "RW", "SA", "SC", "SE", "SI", "SK", "SL", "SM", "SN", "SR", "ST", "SV", "SZ", "TD", "TG", "TH", "TJ", "TL", "TN", "TR", "TT", "TZ", "UA", "UG", "US", "UY", "UZ", "VC", "VE", "VN", "ZA", "ZM", "ZW" ],
"disc_number" : 1,
"duration_ms" : 252720,
"explicit" : false,
"external_urls" : {
"spotify" : "https://open.spotify.com/track/5xYZXIgVAND5sWjN8G0hID"
},
"href" : "https://api.spotify.com/v1/tracks/5xYZXIgVAND5sWjN8G0hID",
"id" : "5xYZXIgVAND5sWjN8G0hID",
"is_local" : false,
"name" : "Go!",
"preview_url" : "https://p.scdn.co/mp3-preview/a7f4e9d98224dea630ee6604938848c3fd0c2842?cid=4b150d8d6d374d1e8dbb85f4f11a2ad9",
"track_number" : 8,
"type" : "track",
"uri" : "spotify:track:5xYZXIgVAND5sWjN8G0hID"
}, {
"artists" : [ {
"external_urls" : {
"spotify" : "https://open.spotify.com/artist/6VsiDFMZJlJ053P1uO4A6h"
},
"href" : "https://api.spotify.com/v1/artists/6VsiDFMZJlJ053P1uO4A6h",
"id" : "6VsiDFMZJlJ053P1uO4A6h",
"name" : "Public Service Broadcasting",
"type" : "artist",
"uri" : "spotify:artist:6VsiDFMZJlJ053P1uO4A6h"
} ],
"available_markets" : [ "AD", "AE", "AG", "AL", "AM", "AO", "AR", "AT", "AZ", "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", "BJ", "BN", "BO", "BR", "BS", "BT", "BW", "BY", "BZ", "CA", "CD", "CG", "CH", "CI", "CL", "CM", "CO", "CR", "CV", "CW", "CY", "CZ", "DE", "DJ", "DK", "DM", "DO", "DZ", "EC", "EE", "EG", "ES", "ET", "FI", "FR", "GA", "GB", "GD", "GE", "GH", "GM", "GN", "GQ", "GR", "GT", "GW", "GY", "HN", "HR", "HT", "HU", "ID", "IE", "IL", "IN", "IQ", "IS", "IT", "JM", "JO", "KE", "KG", "KH", "KM", "KN", "KR", "KW", "KZ", "LA", "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU", "LV", "LY", "MA", "MC", "MD", "MG", "MK", "ML", "MN", "MO", "MR", "MT", "MU", "MV", "MW", "MX", "MZ", "NA", "NE", "NG", "NI", "NL", "NO", "NP", "NZ", "OM", "PA", "PE", "PG", "PH", "PK", "PL", "PS", "PT", "PW", "PY", "QA", "RO", "RW", "SA", "SC", "SE", "SI", "SK", "SL", "SM", "SN", "SR", "ST", "SV", "SZ", "TD", "TG", "TH", "TJ", "TL", "TN", "TR", "TT", "TZ", "UA", "UG", "US", "UY", "UZ", "VC", "VE", "VN", "ZA", "ZM", "ZW" ],
"disc_number" : 1,
"duration_ms" : 442359,
"explicit" : false,
"external_urls" : {
"spotify" : "https://open.spotify.com/track/5ERrJuNLnmHj525ooOKyqJ"
},
"href" : "https://api.spotify.com/v1/tracks/5ERrJuNLnmHj525ooOKyqJ",
"id" : "5ERrJuNLnmHj525ooOKyqJ",
"is_local" : false,
"name" : "Tomorrow",
"preview_url" : "https://p.scdn.co/mp3-preview/779a285aca862b886613815a0c1d1817446b550e?cid=4b150d8d6d374d1e8dbb85f4f11a2ad9",
"track_number" : 9,
"type" : "track",
"uri" : "spotify:track:5ERrJuNLnmHj525ooOKyqJ"
} ],
"limit" : 50,
"next" : null,
"offset" : 0,
"previous" : null,
"total" : 9
},
"type" : "album",
"uri" : "spotify:album:65KwtzkJXw7oT819NFWmEP"
}