summaryrefslogtreecommitdiffstats
path: root/addons/metadata.generic.artists/lib
diff options
context:
space:
mode:
Diffstat (limited to 'addons/metadata.generic.artists/lib')
-rw-r--r--addons/metadata.generic.artists/lib/allmusic.py108
-rw-r--r--addons/metadata.generic.artists/lib/discogs.py45
-rw-r--r--addons/metadata.generic.artists/lib/fanarttv.py48
-rw-r--r--addons/metadata.generic.artists/lib/musicbrainz.py60
-rw-r--r--addons/metadata.generic.artists/lib/nfo.py8
-rw-r--r--addons/metadata.generic.artists/lib/scraper.py503
-rw-r--r--addons/metadata.generic.artists/lib/theaudiodb.py123
-rw-r--r--addons/metadata.generic.artists/lib/utils.py26
-rw-r--r--addons/metadata.generic.artists/lib/wikipedia.py9
9 files changed, 930 insertions, 0 deletions
diff --git a/addons/metadata.generic.artists/lib/allmusic.py b/addons/metadata.generic.artists/lib/allmusic.py
new file mode 100644
index 0000000..f8e9799
--- /dev/null
+++ b/addons/metadata.generic.artists/lib/allmusic.py
@@ -0,0 +1,108 @@
+# -*- coding: utf-8 -*-
+import difflib
+import re
+
+def allmusic_artistfind(data, artist):
+ data = data.decode('utf-8')
+ artists = []
+ artistlist = re.findall('class="artist">\s*(.*?)\s*</li', data, re.S)
+ for item in artistlist:
+ artistdata = {}
+ artistname = re.search('class="name">.*?>(.*?)</a', item, re.S)
+ if artistname:
+ artistdata['artist'] = artistname.group(1)
+ else: # not likely to happen, but just in case
+ continue
+ # filter inaccurate results
+ artistmatch = difflib.SequenceMatcher(None, artist.lower(), artistdata['artist'].lower()).ratio()
+ if artistmatch > 0.95:
+ artisturl = re.search('class="name">\s*<a href="(.*?)"', item)
+ if artisturl:
+ artistdata['url'] = artisturl.group(1)
+ else: # not likely to happen, but just in case
+ continue
+ artists.append(artistdata)
+ # we are only interested in the top result
+ break
+ return artists
+
+def allmusic_artistdetails(data):
+ data = data.decode('utf-8')
+ artistdata = {}
+ artist = re.search(r'artist-name" itemprop="name">\s*(.*?)\s*<', data)
+ if artist:
+ artistdata['artist'] = artist.group(1)
+ else:
+ # no discography page available for this artist
+ return
+ active = re.search(r'class="active-dates">.*?<div>(.*?)<', data, re.S)
+ if active:
+ artistdata['active'] = active.group(1)
+ begin = re.search(r'class="birth">.*?<h4>\s*(.*?)\s*<', data, re.S)
+ if begin and begin.group(1) == 'Born':
+ born = re.search(r'class="birth">.*?<a.*?>(.*?)<', data, re.S)
+ if born:
+ artistdata['born'] = born.group(1)
+ elif begin and begin.group(1) == 'Formed':
+ formed = re.search(r'class="birth">.*?<a.*?>(.*?)<', data, re.S)
+ if formed:
+ artistdata['formed'] = formed.group(1)
+ end = re.search(r'class="died">.*?<h4>\s*(.*?)\s*<', data, re.S)
+ if end and end.group(1) == 'Died':
+ died = re.search(r'class="died">.*?<a.*?>(.*?)<', data, re.S)
+ if died:
+ artistdata['died'] = died.group(1)
+ elif end and end.group(1) == 'Disbanded':
+ disbanded = re.search(r'class="died">.*?<a.*?>(.*?)<', data, re.S)
+ if disbanded:
+ artistdata['disbanded'] = disbanded.group(1)
+ genre = re.search(r'class="genre">.*?<a.*?>(.*?)<', data, re.S)
+ if genre:
+ artistdata['genre'] = genre.group(1)
+ styledata = re.search(r'class="styles">.*?<div>\s*(.*?)\s*</div', data, re.S)
+ if styledata:
+ styles = re.findall(r'">(.*?)<', styledata.group(1))
+ if styles:
+ artistdata['styles'] = ' / '.join(styles)
+ mooddata = re.search(r'class="moods">.*?<li>\s*(.*?)\s*</ul', data, re.S)
+ if mooddata:
+ moods = re.findall(r'">(.*?)<', mooddata.group(1))
+ if moods:
+ artistdata['moods'] = ' / '.join(moods)
+ thumbsdata = re.search(r'class="artist-image">.*?<img src="(.*?)"', data, re.S)
+ if thumbsdata:
+ thumbs = []
+ thumbdata = {}
+ thumb = thumbsdata.group(1).rstrip('?partner=allrovi.com')
+ # 0=largest / 1=75 / 2=150 / 3=250 / 4=400 / 5=500 / 6=1080
+ if thumb.endswith('f=4'):
+ thumbdata['image'] = thumb.replace('f=4', 'f=0')
+ thumbdata['preview'] = thumb.replace('f=4', 'f=2')
+ else:
+ thumbdata['image'] = thumb
+ thumbdata['preview'] = thumb
+ thumbdata['aspect'] = 'thumb'
+ thumbs.append(thumbdata)
+ artistdata['thumb'] = thumbs
+ return artistdata
+
+def allmusic_artistalbums(data):
+ data = data.decode('utf-8')
+ albums = []
+ albumdata = re.search(r'tbody>\s*(.*?)\s*</tbody', data, re.S)
+ if albumdata:
+ albumlist = re.findall(r'tr.*?>\s*(.*?)\s*</tr', albumdata.group(1), re.S)
+ if albumlist:
+ for album in albumlist:
+ albumdata = {}
+ title = re.search(r'<a.*?>(.*?)<', album)
+ if title:
+ albumdata['title'] = title.group(1)
+ year = re.search(r'class="year".*?>\s*(.*?)\s*<', album)
+ if year:
+ albumdata['year'] = year.group(1)
+ else:
+ albumdata['year'] = ''
+ if albumdata:
+ albums.append(albumdata)
+ return albums
diff --git a/addons/metadata.generic.artists/lib/discogs.py b/addons/metadata.generic.artists/lib/discogs.py
new file mode 100644
index 0000000..42fa130
--- /dev/null
+++ b/addons/metadata.generic.artists/lib/discogs.py
@@ -0,0 +1,45 @@
+# -*- coding: utf-8 -*-
+import difflib
+
+def discogs_artistfind(data, artist):
+ artists = []
+ for item in data.get('results',[]):
+ artistdata = {}
+ artistdata['artist'] = item['title']
+ # filter inaccurate results
+ match = difflib.SequenceMatcher(None, artist.lower(), item['title'].lower()).ratio()
+ score = round(match, 2)
+ if score > 0.90:
+ artistdata['thumb'] = item['thumb']
+ artistdata['genre'] = ''
+ artistdata['born'] = ''
+ artistdata['dcid'] = item['id']
+ # discogs does not provide relevance, use our own
+ artistdata['relevance'] = str(score)
+ artists.append(artistdata)
+ return artists
+
+def discogs_artistdetails(data):
+ artistdata = {}
+ artistdata['artist'] = data['name']
+ artistdata['biography'] = data['profile']
+ if 'images' in data:
+ thumbs = []
+ for item in data['images']:
+ thumbdata = {}
+ thumbdata['image'] = item['uri']
+ thumbdata['preview'] = item['uri150']
+ thumbdata['aspect'] = 'thumb'
+ thumbs.append(thumbdata)
+ artistdata['thumb'] = thumbs
+ return artistdata
+
+def discogs_artistalbums(data):
+ albums = []
+ for item in data['releases']:
+ if item['role'] == 'Main':
+ albumdata = {}
+ albumdata['title'] = item['title']
+ albumdata['year'] = str(item.get('year', ''))
+ albums.append(albumdata)
+ return albums
diff --git a/addons/metadata.generic.artists/lib/fanarttv.py b/addons/metadata.generic.artists/lib/fanarttv.py
new file mode 100644
index 0000000..b7d5253
--- /dev/null
+++ b/addons/metadata.generic.artists/lib/fanarttv.py
@@ -0,0 +1,48 @@
+# -*- coding: utf-8 -*-
+
+def fanarttv_artistart(data):
+ artistdata = {}
+ extras = []
+ if 'artistbackground' in data:
+ fanart = []
+ for item in data['artistbackground']:
+ fanartdata = {}
+ fanartdata['image'] = item['url']
+ fanartdata['preview'] = item['url'].replace('/fanart/', '/preview/')
+ fanartdata['aspect'] = 'fanart'
+ fanart.append(fanartdata)
+ artistdata['fanart'] = fanart
+ if 'artistthumb' in data:
+ thumbs = []
+ for item in data['artistthumb']:
+ thumbdata = {}
+ thumbdata['image'] = item['url']
+ thumbdata['preview'] = item['url'].replace('/fanart/', '/preview/')
+ thumbdata['aspect'] = 'thumb'
+ thumbs.append(thumbdata)
+ if thumbs:
+ artistdata['thumb'] = thumbs
+ if 'musicbanner' in data:
+ for item in data['musicbanner']:
+ extradata = {}
+ extradata['image'] = item['url']
+ extradata['preview'] = item['url'].replace('/fanart/', '/preview/')
+ extradata['aspect'] = 'banner'
+ extras.append(extradata)
+ if 'hdmusiclogo' in data:
+ for item in data['hdmusiclogo']:
+ extradata = {}
+ extradata['image'] = item['url']
+ extradata['preview'] = item['url'].replace('/fanart/', '/preview/')
+ extradata['aspect'] = 'clearlogo'
+ extras.append(extradata)
+ elif 'musiclogo' in data:
+ for item in data['musiclogo']:
+ extradata = {}
+ extradata['image'] = item['url']
+ extradata['preview'] = item['url'].replace('/fanart/', '/preview/')
+ extradata['aspect'] = 'clearlogo'
+ extras.append(extradata)
+ if extras:
+ artistdata['extras'] = extras
+ return artistdata
diff --git a/addons/metadata.generic.artists/lib/musicbrainz.py b/addons/metadata.generic.artists/lib/musicbrainz.py
new file mode 100644
index 0000000..d5bdaf3
--- /dev/null
+++ b/addons/metadata.generic.artists/lib/musicbrainz.py
@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+
+def musicbrainz_artistfind(data, artist):
+ artists = []
+ for item in data.get('artists',[]):
+ artistdata = {}
+ artistdata['artist'] = item['name']
+ artistdata['thumb'] = ''
+ artistdata['genre'] = ''
+ artistdata['born'] = item['life-span'].get('begin', '')
+ if 'type' in item:
+ artistdata['type'] = item['type']
+ if 'gender' in item:
+ artistdata['gender'] = item['gender']
+ if 'disambiguation' in item:
+ artistdata['disambiguation'] = item['disambiguation']
+ artistdata['mbartistid'] = item['id']
+ if item.get('score',1):
+ artistdata['relevance'] = str(item['score'] / 100.00)
+ artists.append(artistdata)
+ return artists
+
+def musicbrainz_artistdetails(data):
+ artistdata = {}
+ artistdata['artist'] = data['name']
+ artistdata['mbartistid'] = data['id']
+ artistdata['type'] = data['type']
+ artistdata['gender'] = data['gender']
+ artistdata['disambiguation'] = data['disambiguation']
+ if data.get('life-span','') and data.get('type',''):
+ begin = data['life-span'].get('begin', '')
+ end = data['life-span'].get('end', '')
+ if data['type'] in ['Group', 'Orchestra', 'Choir']:
+ artistdata['formed'] = begin
+ artistdata['disbanded'] = end
+ elif data['type'] in ['Person', 'Character']:
+ artistdata['born'] = begin
+ artistdata['died'] = end
+ albums = []
+ for item in data.get('release-groups',[]):
+ albumdata = {}
+ albumdata['title'] = item.get('title','')
+ albumdata['year'] = item.get('first-release-date','')
+ albumdata['musicbrainzreleasegroupid'] = item.get('id','')
+ albums.append(albumdata)
+ if albums:
+ artistdata['albums'] = albums
+ for item in data['relations']:
+ if item['type'] == 'allmusic':
+ artistdata['allmusic'] = item['url']['resource']
+ elif item['type'] == 'discogs':
+ dataid = item['url']['resource'].rsplit('/', 1)[1]
+ artistdata['discogs'] = dataid
+ elif item['type'] == 'wikidata':
+ dataid = item['url']['resource'].rsplit('/', 1)[1]
+ artistdata['wikidata'] = dataid
+ elif item['type'] == 'wikipedia':
+ dataid = item['url']['resource'].rsplit('/', 1)[1]
+ artistdata['wikipedia'] = dataid
+ return artistdata
diff --git a/addons/metadata.generic.artists/lib/nfo.py b/addons/metadata.generic.artists/lib/nfo.py
new file mode 100644
index 0000000..7aeb7ff
--- /dev/null
+++ b/addons/metadata.generic.artists/lib/nfo.py
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+import re
+
+def nfo_geturl(data):
+ result = re.search('https://musicbrainz.org/(ws/2/)?artist/([0-9a-z\-]*)', data)
+ if result:
+ return result.group(2)
diff --git a/addons/metadata.generic.artists/lib/scraper.py b/addons/metadata.generic.artists/lib/scraper.py
new file mode 100644
index 0000000..0979ffb
--- /dev/null
+++ b/addons/metadata.generic.artists/lib/scraper.py
@@ -0,0 +1,503 @@
+# -*- coding: utf-8 -*-
+
+import json
+import socket
+import sys
+import time
+import urllib.parse
+import urllib.request
+import _strptime # https://bugs.python.org/issue7980
+from socket import timeout
+from threading import Thread
+from urllib.error import HTTPError, URLError
+import xbmc
+import xbmcaddon
+import xbmcgui
+import xbmcplugin
+from .allmusic import allmusic_artistfind
+from .allmusic import allmusic_artistdetails
+from .allmusic import allmusic_artistalbums
+from .discogs import discogs_artistfind
+from .discogs import discogs_artistdetails
+from .discogs import discogs_artistalbums
+from .fanarttv import fanarttv_artistart
+from .musicbrainz import musicbrainz_artistfind
+from .musicbrainz import musicbrainz_artistdetails
+from .nfo import nfo_geturl
+from .theaudiodb import theaudiodb_artistdetails
+from .theaudiodb import theaudiodb_artistalbums
+from .wikipedia import wikipedia_artistdetails
+from .utils import *
+
+ADDONID = xbmcaddon.Addon().getAddonInfo('id')
+ADDONNAME = xbmcaddon.Addon().getAddonInfo('name')
+ADDONVERSION = xbmcaddon.Addon().getAddonInfo('version')
+
+
+def log(txt):
+ message = '%s: %s' % (ADDONID, txt)
+ xbmc.log(msg=message, level=xbmc.LOGDEBUG)
+
+def get_data(url, jsonformat, retry=True):
+ try:
+ if url.startswith('https://musicbrainz.org/'):
+ api_timeout('musicbrainztime')
+ elif url.startswith('https://api.discogs.com/'):
+ api_timeout('discogstime')
+ headers = {}
+ headers['User-Agent'] = '%s/%s ( http://kodi.tv )' % (ADDONNAME, ADDONVERSION)
+ req = urllib.request.Request(url, headers=headers)
+ resp = urllib.request.urlopen(req, timeout=5)
+ respdata = resp.read()
+ except URLError as e:
+ log('URLError: %s - %s' % (e.reason, url))
+ return
+ except HTTPError as e:
+ log('HTTPError: %s - %s' % (e.reason, url))
+ return
+ except socket.timeout as e:
+ log('socket: %s - %s' % (e, url))
+ return
+ if resp.getcode() == 503:
+ log('exceeding musicbrainz api limit')
+ if retry:
+ xbmc.sleep(1000)
+ get_data(url, jsonformat, retry=False)
+ else:
+ return
+ elif resp.getcode() == 429:
+ log('exceeding discogs api limit')
+ if retry:
+ xbmc.sleep(1000)
+ get_data(url, jsonformat, retry=False)
+ else:
+ return
+ if jsonformat:
+ respdata = json.loads(respdata)
+ return respdata
+
+def api_timeout(scraper):
+ currenttime = round(time.time() * 1000)
+ previoustime = xbmcgui.Window(10000).getProperty(scraper)
+ if previoustime:
+ timeout = currenttime - int(previoustime)
+ if timeout < 1000:
+ xbmc.sleep(1000 - timeout)
+ xbmcgui.Window(10000).setProperty(scraper, str(round(time.time() * 1000)))
+
+
+class Scraper():
+ def __init__(self, action, key, artist, url, nfo, settings):
+ # parse path settings
+ self.parse_settings(settings)
+ # this is just for backward compitability with xml based scrapers https://github.com/xbmc/xbmc/pull/11632
+ if action == 'resolveid':
+ # return the result
+ result = self.resolve_mbid(key)
+ self.return_resolved(result)
+ # search for artist name matches
+ elif action == 'find':
+ # try musicbrainz first
+ result = self.find_artist(artist, 'musicbrainz')
+ if result:
+ self.return_search(result)
+ # fallback to discogs
+ else:
+ result = self.find_artist(artist, 'discogs')
+ if result:
+ self.return_search(result)
+ # return info using id's
+ elif action == 'getdetails':
+ details = {}
+ discography = {}
+ url = json.loads(url)
+ artist = url.get('artist')
+ mbartistid = url.get('mbartistid')
+ dcid = url.get('dcid')
+ threads = []
+ extrascrapers = []
+ discographyscrapers = []
+ # we have a musicbrainz id
+ if mbartistid:
+ scrapers = [[mbartistid, 'musicbrainz'], [mbartistid, 'theaudiodb'], [mbartistid, 'fanarttv']]
+ for item in scrapers:
+ thread = Thread(target = self.get_details, args = (item[0], item[1], details))
+ threads.append(thread)
+ thread.start()
+ # theaudiodb discograhy
+ thread = Thread(target = self.get_discography, args = (mbartistid, 'theaudiodb', discography))
+ threads.append(thread)
+ thread.start()
+ # wait for musicbrainz to finish
+ threads[0].join()
+ # check if we have a result:
+ if 'musicbrainz' in details:
+ if not artist:
+ artist = details['musicbrainz']['artist']
+ # scrape allmusic if we have an url provided by musicbrainz
+ if 'allmusic' in details['musicbrainz']:
+ extrascrapers.append([{'url': details['musicbrainz']['allmusic']}, 'allmusic'])
+ # allmusic discograhy
+ discographyscrapers.append([{'url': details['musicbrainz']['allmusic']}, 'allmusic'])
+ # only scrape allmusic by artistname if explicitly enabled
+ elif self.inaccurate and artist:
+ extrascrapers.append([{'artist': artist}, 'allmusic'])
+ # scrape wikipedia if we have an url provided by musicbrainz
+ if 'wikipedia' in details['musicbrainz']:
+ extrascrapers.append([details['musicbrainz']['wikipedia'], 'wikipedia'])
+ elif 'wikidata' in details['musicbrainz']:
+ extrascrapers.append([details['musicbrainz']['wikidata'], 'wikidata'])
+ # scrape discogs if we have an url provided by musicbrainz
+ if 'discogs' in details['musicbrainz']:
+ extrascrapers.append([{'url': details['musicbrainz']['discogs']}, 'discogs'])
+ # discogs discograhy
+ discographyscrapers.append([{'url': details['musicbrainz']['discogs']}, 'discogs'])
+ # only scrape discogs by artistname if explicitly enabled
+ elif self.inaccurate and artist:
+ extrascrapers.append([{'artist': artist}, 'discogs'])
+ for item in extrascrapers:
+ thread = Thread(target = self.get_details, args = (item[0], item[1], details))
+ threads.append(thread)
+ thread.start()
+ # get allmusic / discogs discography if we have an url
+ for item in discographyscrapers:
+ thread = Thread(target = self.get_discography, args = (item[0], item[1], discography))
+ threads.append(thread)
+ thread.start()
+ # we have a discogs id
+ else:
+ thread = Thread(target = self.get_details, args = ({'url': dcid}, 'discogs', details))
+ threads.append(thread)
+ thread.start()
+ thread = Thread(target = self.get_discography, args = ({'url': dcid}, 'discogs', discography))
+ threads.append(thread)
+ thread.start()
+ if threads:
+ for thread in threads:
+ thread.join()
+ # merge discography items
+ for site, albumlist in discography.items():
+ if site in details:
+ details[site]['albums'] = albumlist
+ else:
+ details[site] = {}
+ details[site]['albums'] = albumlist
+ result = self.compile_results(details)
+ if result:
+ self.return_details(result)
+ elif action == 'NfoUrl':
+ # check if there is a musicbrainz url in the nfo file
+ mbartistid = nfo_geturl(nfo)
+ if mbartistid:
+ # return the result
+ result = self.resolve_mbid(mbartistid)
+ self.return_nfourl(result)
+ xbmcplugin.endOfDirectory(int(sys.argv[1]))
+
+ def parse_settings(self, data):
+ settings = json.loads(data)
+ # note: path settings are taken from the db, they may not reflect the current settings.xml file
+ self.bio = settings['bio']
+ self.discog = settings['discog']
+ self.genre = settings['genre']
+ self.lang = settings['lang']
+ self.mood = settings['mood']
+ self.style = settings['style']
+ self.inaccurate = settings['inaccurate']
+
+ def resolve_mbid(self, mbartistid):
+ item = {}
+ item['artist'] = ''
+ item['mbartistid'] = mbartistid
+ return item
+
+ def find_artist(self, artist, site):
+ json = True
+ # musicbrainz
+ if site == 'musicbrainz':
+ url = MUSICBRAINZURL % (MUSICBRAINZSEARCH % urllib.parse.quote_plus(artist))
+ scraper = musicbrainz_artistfind
+ # musicbrainz
+ if site == 'discogs':
+ url = DISCOGSURL % (DISCOGSSEARCH % (urllib.parse.quote_plus(artist), DISCOGSKEY , DISCOGSSECRET))
+ scraper = discogs_artistfind
+ result = get_data(url, json)
+ if not result:
+ return
+ artistresults = scraper(result, artist)
+ return artistresults
+
+ def get_details(self, param, site, details, discography={}):
+ json = True
+ # theaudiodb
+ if site == 'theaudiodb':
+ url = AUDIODBURL % (AUDIODBKEY, AUDIODBDETAILS % param)
+ artistscraper = theaudiodb_artistdetails
+ # musicbrainz
+ elif site == 'musicbrainz':
+ url = MUSICBRAINZURL % (MUSICBRAINZDETAILS % param)
+ artistscraper = musicbrainz_artistdetails
+ # fanarttv
+ elif site == 'fanarttv':
+ url = FANARTVURL % (param, FANARTVKEY)
+ artistscraper = fanarttv_artistart
+ # discogs
+ elif site == 'discogs':
+ # search by artistname if we do not have an url
+ if 'artist' in param:
+ url = DISCOGSURL % (DISCOGSSEARCH % (urllib.parse.quote_plus(param['artist']), DISCOGSKEY , DISCOGSSECRET))
+ artistresult = get_data(url, json)
+ if artistresult:
+ artists = discogs_artistfind(artistresult, param['artist'])
+ if artists:
+ artistresult = sorted(artists, key=lambda k: k['relevance'], reverse=True)
+ param['url'] = artistresult[0]['dcid']
+ else:
+ return
+ else:
+ return
+ url = DISCOGSURL % (DISCOGSDETAILS % (param['url'], DISCOGSKEY, DISCOGSSECRET))
+ artistscraper = discogs_artistdetails
+ # wikipedia
+ elif site == 'wikipedia':
+ url = WIKIPEDIAURL % param
+ artistscraper = wikipedia_artistdetails
+ elif site == 'wikidata':
+ # resolve wikidata to wikipedia url
+ result = get_data(WIKIDATAURL % param, json)
+ try:
+ artist = result['entities'][param]['sitelinks']['enwiki']['url'].rsplit('/', 1)[1]
+ except:
+ return
+ site = 'wikipedia'
+ url = WIKIPEDIAURL % artist
+ artistscraper = wikipedia_artistdetails
+ # allmusic
+ elif site == 'allmusic':
+ json = False
+ # search by artistname if we do not have an url
+ if 'artist' in param:
+ url = ALLMUSICURL % urllib.parse.quote_plus(param['artist'])
+ artistresult = get_data(url, json)
+ if artistresult:
+ artists = allmusic_artistfind(artistresult, param['artist'])
+ if artists:
+ param['url'] = artists[0]['url']
+ else:
+ return
+ else:
+ return
+ url = param['url']
+ artistscraper = allmusic_artistdetails
+ result = get_data(url, json)
+ if not result:
+ return
+ artistresults = artistscraper(result)
+ if not artistresults:
+ return
+ details[site] = artistresults
+ # get allmusic / discogs discography if we searched by artistname
+ if (site == 'discogs' or site == 'allmusic') and 'artist' in param:
+ albums = self.get_discography(param, site, {})
+ if albums:
+ details[site]['albums'] = albums[site]
+ return details
+
+ def get_discography(self, param, site, discography):
+ json = True
+ if site == 'theaudiodb':
+ # theaudiodb - discography
+ albumsurl = AUDIODBURL % (AUDIODBKEY, AUDIODBDISCOGRAPHY % param)
+ scraper = theaudiodb_artistalbums
+ elif site == 'discogs':
+ # discogs - discography
+ albumsurl = DISCOGSURL % (DISCOGSDISCOGRAPHY % (param['url'], DISCOGSKEY, DISCOGSSECRET))
+ scraper = discogs_artistalbums
+ elif site == 'allmusic':
+ # allmusic - discography
+ json = False
+ albumsurl = param['url'] + '/discography'
+ scraper = allmusic_artistalbums
+ albumdata = get_data(albumsurl, json)
+ if not albumdata:
+ return
+ albumresults = scraper(albumdata)
+ if not albumresults:
+ return
+ discography[site] = albumresults
+ return discography
+
+ def compile_results(self, details):
+ result = {}
+ thumbs = []
+ fanart = []
+ extras = []
+ # merge metadata results, start with the least accurate sources
+ if 'discogs' in details:
+ for k, v in details['discogs'].items():
+ if v:
+ result[k] = v
+ if k == 'thumb' and v:
+ thumbs.append(v)
+ if 'wikipedia' in details:
+ for k, v in details['wikipedia'].items():
+ if v:
+ result[k] = v
+ if 'allmusic' in details:
+ for k, v in details['allmusic'].items():
+ if v:
+ result[k] = v
+ if k == 'thumb' and v:
+ thumbs.append(v)
+ if 'theaudiodb' in details:
+ for k, v in details['theaudiodb'].items():
+ if v:
+ result[k] = v
+ if k == 'thumb' and v:
+ thumbs.append(v)
+ elif k == 'fanart' and v:
+ fanart.append(v)
+ if k == 'extras' and v:
+ extras.append(v)
+ if 'musicbrainz' in details:
+ for k, v in details['musicbrainz'].items():
+ if v:
+ result[k] = v
+ if 'fanarttv' in details:
+ for k, v in details['fanarttv'].items():
+ if v:
+ result[k] = v
+ if k == 'thumb' and v:
+ thumbs.append(v)
+ elif k == 'fanart' and v:
+ fanart.append(v)
+ if k == 'extras' and v:
+ extras.append(v)
+ # merge artwork from all scrapers
+ if result:
+ # artworks from most accurate sources first
+ thumbs.reverse()
+ thumbnails = []
+ fanart.reverse()
+ fanarts = []
+ # the order for extra art does not matter
+ extraart = []
+ for thumblist in thumbs:
+ for item in thumblist:
+ thumbnails.append(item)
+ for extralist in extras:
+ for item in extralist:
+ extraart.append(item)
+ # add the extra art to the end of the thumb list
+ if extraart:
+ thumbnails.extend(extraart)
+ for fanartlist in fanart:
+ for item in fanartlist:
+ fanarts.append(item)
+ # add the fanart to the end of the thumb list
+ if fanarts:
+ thumbnails.extend(fanarts)
+ if thumbnails:
+ result['thumb'] = thumbnails
+ data = self.user_prefs(details, result)
+ return data
+
+ def user_prefs(self, details, result):
+ # user preferences
+ lang = 'biography' + self.lang
+ if self.bio == 'theaudiodb' and 'theaudiodb' in details:
+ if lang in details['theaudiodb']:
+ result['biography'] = details['theaudiodb'][lang]
+ elif 'biographyEN' in details['theaudiodb']:
+ result['biography'] = details['theaudiodb']['biographyEN']
+ elif (self.bio in details) and ('biography' in details[self.bio]):
+ result['biography'] = details[self.bio]['biography']
+ if (self.discog in details) and ('albums' in details[self.discog]):
+ result['albums'] = details[self.discog]['albums']
+ if (self.genre in details) and ('genre' in details[self.genre]):
+ result['genre'] = details[self.genre]['genre']
+ if (self.style in details) and ('styles' in details[self.style]):
+ result['styles'] = details[self.style]['styles']
+ if (self.mood in details) and ('moods' in details[self.mood]):
+ result['moods'] = details[self.mood]['moods']
+ return result
+
+ def return_search(self, data):
+ items = []
+ for item in data:
+ listitem = xbmcgui.ListItem(item['artist'], offscreen=True)
+ listitem.setArt({'thumb': item['thumb']})
+ listitem.setProperty('artist.genre', item['genre'])
+ listitem.setProperty('artist.born', item['born'])
+ listitem.setProperty('relevance', item['relevance'])
+ if 'type' in item:
+ listitem.setProperty('artist.type', item['type'])
+ if 'gender' in item:
+ listitem.setProperty('artist.gender', item['gender'])
+ if 'disambiguation' in item:
+ listitem.setProperty('artist.disambiguation', item['disambiguation'])
+ url = {'artist':item['artist']}
+ if 'mbartistid' in item:
+ url['mbartistid'] = item['mbartistid']
+ if 'dcid' in item:
+ url['dcid'] = item['dcid']
+ items.append((json.dumps(url), listitem, True))
+ if items:
+ xbmcplugin.addDirectoryItems(handle=int(sys.argv[1]), items=items)
+
+ def return_nfourl(self, item):
+ listitem = xbmcgui.ListItem(offscreen=True)
+ xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=json.dumps(item), listitem=listitem, isFolder=True)
+
+ def return_resolved(self, item):
+ listitem = xbmcgui.ListItem(path=json.dumps(item), offscreen=True)
+ xbmcplugin.setResolvedUrl(handle=int(sys.argv[1]), succeeded=True, listitem=listitem)
+
+ def return_details(self, item):
+ if not 'artist' in item:
+ return
+ listitem = xbmcgui.ListItem(item['artist'], offscreen=True)
+ if 'mbartistid' in item:
+ listitem.setProperty('artist.musicbrainzid', item['mbartistid'])
+ if 'genre' in item:
+ listitem.setProperty('artist.genre', item['genre'])
+ if 'biography' in item:
+ listitem.setProperty('artist.biography', item['biography'])
+ if 'gender' in item:
+ listitem.setProperty('artist.gender', item['gender'])
+ if 'styles' in item:
+ listitem.setProperty('artist.styles', item['styles'])
+ if 'moods' in item:
+ listitem.setProperty('artist.moods', item['moods'])
+ if 'instruments' in item:
+ listitem.setProperty('artist.instruments', item['instruments'])
+ if 'disambiguation' in item:
+ listitem.setProperty('artist.disambiguation', item['disambiguation'])
+ if 'type' in item:
+ listitem.setProperty('artist.type', item['type'])
+ if 'sortname' in item:
+ listitem.setProperty('artist.sortname', item['sortname'])
+ if 'active' in item:
+ listitem.setProperty('artist.years_active', item['active'])
+ if 'born' in item:
+ listitem.setProperty('artist.born', item['born'])
+ if 'formed' in item:
+ listitem.setProperty('artist.formed', item['formed'])
+ if 'died' in item:
+ listitem.setProperty('artist.died', item['died'])
+ if 'disbanded' in item:
+ listitem.setProperty('artist.disbanded', item['disbanded'])
+ if 'thumb' in item:
+ listitem.setProperty('artist.thumbs', str(len(item['thumb'])))
+ for count, thumb in enumerate(item['thumb']):
+ listitem.setProperty('artist.thumb%i.url' % (count + 1), thumb['image'])
+ listitem.setProperty('artist.thumb%i.preview' % (count + 1), thumb['preview'])
+ listitem.setProperty('artist.thumb%i.aspect' % (count + 1), thumb['aspect'])
+ if 'albums' in item:
+ listitem.setProperty('artist.albums', str(len(item['albums'])))
+ for count, album in enumerate(item['albums']):
+ listitem.setProperty('artist.album%i.title' % (count + 1), album['title'])
+ listitem.setProperty('artist.album%i.year' % (count + 1), album['year'])
+ if 'musicbrainzreleasegroupid' in album:
+ listitem.setProperty('artist.album%i.musicbrainzreleasegroupid' % (count + 1), album['musicbrainzreleasegroupid'])
+ xbmcplugin.setResolvedUrl(handle=int(sys.argv[1]), succeeded=True, listitem=listitem)
diff --git a/addons/metadata.generic.artists/lib/theaudiodb.py b/addons/metadata.generic.artists/lib/theaudiodb.py
new file mode 100644
index 0000000..c5c9cb2
--- /dev/null
+++ b/addons/metadata.generic.artists/lib/theaudiodb.py
@@ -0,0 +1,123 @@
+# -*- coding: utf-8 -*-
+
+def theaudiodb_artistdetails(data):
+ if data.get('artists',[]):
+ item = data['artists'][0]
+ artistdata = {}
+ extras = []
+ artistdata['artist'] = item['strArtist']
+ # api inconsistent
+ if item.get('intFormedYear','') and item['intFormedYear'] != '0':
+ artistdata['formed'] = item['intFormedYear']
+ if item.get('intBornYear','') and item['intBornYear'] != '0':
+ artistdata['born'] = item['intBornYear']
+ if item.get('intDiedYear','') and item['intDiedYear'] != '0':
+ artistdata['died'] = item['intDiedYear']
+ if item.get('strDisbanded','') and item['strDisbanded'] != '0':
+ artistdata['disbanded'] = item['strDisbanded']
+ if item.get('strStyle',''):
+ artistdata['styles'] = item['strStyle']
+ if item.get('strGenre',''):
+ artistdata['genre'] = item['strGenre']
+ if item.get('strMood',''):
+ artistdata['moods'] = item['strMood']
+ if item.get('strGender',''):
+ artistdata['gender'] = item['strGender']
+ if item.get('strBiographyEN',''):
+ artistdata['biographyEN'] = item['strBiographyEN']
+ if item.get('strBiographyDE',''):
+ artistdata['biographyDE'] = item['strBiographyDE']
+ if item.get('strBiographyFR',''):
+ artistdata['biographyFR'] = item['strBiographyFR']
+ if item.get('strBiographyCN',''):
+ artistdata['biographyCN'] = item['strBiographyCN']
+ if item.get('strBiographyIT',''):
+ artistdata['biographyIT'] = item['strBiographyIT']
+ if item.get('strBiographyJP',''):
+ artistdata['biographyJP'] = item['strBiographyJP']
+ if item.get('strBiographyRU',''):
+ artistdata['biographyRU'] = item['strBiographyRU']
+ if item.get('strBiographyES',''):
+ artistdata['biographyES'] = item['strBiographyES']
+ if item.get('strBiographyPT',''):
+ artistdata['biographyPT'] = item['strBiographyPT']
+ if item.get('strBiographySE',''):
+ artistdata['biographySE'] = item['strBiographySE']
+ if item.get('strBiographyNL',''):
+ artistdata['biographyNL'] = item['strBiographyNL']
+ if item.get('strBiographyHU',''):
+ artistdata['biographyHU'] = item['strBiographyHU']
+ if item.get('strBiographyNO',''):
+ artistdata['biographyNO'] = item['strBiographyNO']
+ if item.get('strBiographyIL',''):
+ artistdata['biographyIL'] = item['strBiographyIL']
+ if item.get('strBiographyPL',''):
+ artistdata['biographyPL'] = item['strBiographyPL']
+ if item.get('strMusicBrainzID',''):
+ artistdata['mbartistid'] = item['strMusicBrainzID']
+ if item.get('strArtistFanart',''):
+ fanart = []
+ fanartdata = {}
+ fanartdata['image'] = item['strArtistFanart']
+ fanartdata['preview'] = item['strArtistFanart'] + '/preview'
+ fanartdata['aspect'] = 'fanart'
+ fanart.append(fanartdata)
+ if item['strArtistFanart2']:
+ fanartdata = {}
+ fanartdata['image'] = item['strArtistFanart2']
+ fanartdata['preview'] = item['strArtistFanart2'] + '/preview'
+ fanartdata['aspect'] = 'fanart'
+ fanart.append(fanartdata)
+ if item['strArtistFanart3']:
+ fanartdata = {}
+ fanartdata['image'] = item['strArtistFanart3']
+ fanartdata['preview'] = item['strArtistFanart3'] + '/preview'
+ fanartdata['aspect'] = 'fanart'
+ fanart.append(fanartdata)
+ artistdata['fanart'] = fanart
+ if item.get('strArtistThumb',''):
+ thumbs = []
+ thumbdata = {}
+ thumbdata['image'] = item['strArtistThumb']
+ thumbdata['preview'] = item['strArtistThumb'] + '/preview'
+ thumbdata['aspect'] = 'thumb'
+ thumbs.append(thumbdata)
+ artistdata['thumb'] = thumbs
+ if item.get('strArtistLogo',''):
+ extradata = {}
+ extradata['image'] = item['strArtistLogo']
+ extradata['preview'] = item['strArtistLogo'] + '/preview'
+ extradata['aspect'] = 'clearlogo'
+ extras.append(extradata)
+ if item.get('strArtistClearart',''):
+ extradata = {}
+ extradata['image'] = item['strArtistClearart']
+ extradata['preview'] = item['strArtistClearart'] + '/preview'
+ extradata['aspect'] = 'clearart'
+ extras.append(extradata)
+ if item.get('strArtistWideThumb',''):
+ extradata = {}
+ extradata['image'] = item['strArtistWideThumb']
+ extradata['preview'] = item['strArtistWideThumb'] + '/preview'
+ extradata['aspect'] = 'landscape'
+ extras.append(extradata)
+ if item.get('strArtistBanner',''):
+ extradata = {}
+ extradata['image'] = item['strArtistBanner']
+ extradata['preview'] = item['strArtistBanner'] + '/preview'
+ extradata['aspect'] = 'banner'
+ extras.append(extradata)
+ if extras:
+ artistdata['extras'] = extras
+ return artistdata
+
+def theaudiodb_artistalbums(data):
+ albums = []
+ albumlist = data.get('album',[])
+ if albumlist:
+ for item in data.get('album',[]):
+ albumdata = {}
+ albumdata['title'] = item['strAlbum']
+ albumdata['year'] = item.get('intYearReleased', '')
+ albums.append(albumdata)
+ return albums
diff --git a/addons/metadata.generic.artists/lib/utils.py b/addons/metadata.generic.artists/lib/utils.py
new file mode 100644
index 0000000..1958707
--- /dev/null
+++ b/addons/metadata.generic.artists/lib/utils.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+
+AUDIODBKEY = '95424d43204d6564696538'
+AUDIODBURL = 'https://www.theaudiodb.com/api/v1/json/%s/%s'
+AUDIODBSEARCH = 'search.php?s=%s'
+AUDIODBDETAILS = 'artist-mb.php?i=%s'
+AUDIODBDISCOGRAPHY = 'discography-mb.php?s=%s'
+
+MUSICBRAINZURL = 'https://musicbrainz.org/ws/2/artist/%s'
+MUSICBRAINZSEARCH = '?query="%s"&fmt=json'
+MUSICBRAINZDETAILS = '%s?inc=url-rels+release-groups&type=album&fmt=json'
+
+DISCOGSKEY = 'zACPgktOmNegwbwKWMaC'
+DISCOGSSECRET = 'wGuSOeMtfdkQxtERKQKPquyBwExSHdQq'
+DISCOGSURL = 'https://api.discogs.com/%s'
+DISCOGSSEARCH = 'database/search?q=%s&type=artist&key=%s&secret=%s'
+DISCOGSDETAILS = 'artists/%s?key=%s&secret=%s'
+DISCOGSDISCOGRAPHY = 'artists/%s/releases?sort=format&page=1&per_page=100&key=%s&secret=%s'
+
+ALLMUSICURL = 'https://www.allmusic.com/search/artists/%s'
+
+FANARTVKEY = '88ca41db0d6878929f1f9771eade41fd'
+FANARTVURL = 'https://webservice.fanart.tv/v3/music/%s?api_key=%s'
+
+WIKIDATAURL = 'https://www.wikidata.org/wiki/Special:EntityData/%s.json'
+WIKIPEDIAURL = 'https://en.wikipedia.org/w/api.php?action=query&format=json&prop=extracts&titles=%s&formatversion=2&exsentences=10&exlimit=1&explaintext=1'
diff --git a/addons/metadata.generic.artists/lib/wikipedia.py b/addons/metadata.generic.artists/lib/wikipedia.py
new file mode 100644
index 0000000..3a31992
--- /dev/null
+++ b/addons/metadata.generic.artists/lib/wikipedia.py
@@ -0,0 +1,9 @@
+# -*- coding: utf-8 -*-
+import re
+
+def wikipedia_artistdetails(data):
+ artistdata = {}
+ # check in case musicbrainz did not provide a direct link
+ if 'extract' in data['query']['pages'][0] and not data['query']['pages'][0]['extract'].endswith('may refer to:'):
+ artistdata['biography'] = re.sub('\n\n\n== .*? ==\n', ' ', data['query']['pages'][0]['extract'])
+ return artistdata