new data model: add Spotify
This commit is contained in:
parent
3a95e5fc5c
commit
997e5fad0d
9 changed files with 501 additions and 6 deletions
|
@ -5,4 +5,4 @@ from .scrapers import *
|
|||
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')
|
||||
|
|
|
@ -34,6 +34,11 @@ def setMockMode(enabled):
|
|||
MockMode = enabled
|
||||
|
||||
|
||||
def getMockMode():
|
||||
global MockMode
|
||||
return MockMode
|
||||
|
||||
|
||||
class DownloadError(Exception):
|
||||
def __init__(self, downloader):
|
||||
self.url = downloader.url
|
||||
|
@ -63,10 +68,12 @@ class BasicDownloader:
|
|||
'Cache-Control': 'no-cache',
|
||||
}
|
||||
|
||||
def __init__(self, url):
|
||||
def __init__(self, url, headers=None):
|
||||
self.url = url
|
||||
self.response_type = RESPONSE_OK
|
||||
self.logs = []
|
||||
if headers:
|
||||
self.headers = headers
|
||||
|
||||
def get_timeout(self):
|
||||
return settings.SCRAPING_TIMEOUT
|
||||
|
|
|
@ -18,7 +18,7 @@ class IdType(models.TextChoices):
|
|||
ISSN = 'issn', _('ISSN')
|
||||
CUBN = 'cubn', _('统一书号')
|
||||
ISRC = 'isrc', _('ISRC') # only for songs
|
||||
UPC = 'upc', _('GTIN UPC EAN码')
|
||||
GTIN = 'gtin', _('GTIN UPC EAN码') # ISBN is separate
|
||||
Feed = 'feed', _('Feed URL')
|
||||
IMDB = 'imdb', _('IMDb')
|
||||
TMDB_TV = 'tmdb_tv', _('TMDB剧集')
|
||||
|
@ -37,6 +37,9 @@ class IdType(models.TextChoices):
|
|||
Bandcamp = 'bandcamp', _('Bandcamp')
|
||||
Spotify_Album = 'spotify_album', _('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', _('豆瓣读书作者')
|
||||
DoubanCelebrity = 'doubanmovie_celebrity', _('豆瓣电影影人')
|
||||
Goodreads_Author = 'goodreads_author', _('Goodreads作者')
|
||||
|
|
|
@ -2,9 +2,9 @@ from catalog.common import *
|
|||
|
||||
|
||||
class Album(Item):
|
||||
upc = LookupIdDescriptor(IdType.UPC)
|
||||
douban_music = LookupIdDescriptor(IdType.DoubanMusic)
|
||||
spotify_album = LookupIdDescriptor(IdType.Spotify_Album)
|
||||
barcode = PrimaryLookupIdDescriptor(IdType.GTIN)
|
||||
douban_music = PrimaryLookupIdDescriptor(IdType.DoubanMusic)
|
||||
spotify_album = PrimaryLookupIdDescriptor(IdType.Spotify_Album)
|
||||
|
||||
class Meta:
|
||||
proxy = True
|
||||
|
|
27
catalog/music/tests.py
Normal file
27
catalog/music/tests.py
Normal 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')
|
|
@ -6,3 +6,4 @@ from .douban_drama import DoubanDrama
|
|||
from .goodreads import Goodreads
|
||||
from .tmdb import TMDB_Movie
|
||||
from .imdb import IMDB
|
||||
from .spotify import Spotify
|
||||
|
|
153
catalog/sites/spotify.py
Normal file
153
catalog/sites/spotify.py
Normal 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)
|
||||
}
|
||||
)
|
|
@ -2,6 +2,7 @@ from django.test import TestCase
|
|||
from catalog.book.tests import *
|
||||
from catalog.movie.tests import *
|
||||
from catalog.tv.tests import *
|
||||
from catalog.music.tests import *
|
||||
from catalog.podcast.tests import *
|
||||
from catalog.performance.tests import *
|
||||
|
||||
|
|
|
@ -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"
|
||||
}
|
Loading…
Add table
Reference in a new issue