diff options
Diffstat (limited to 'addons/metadata.generic.artists/lib')
-rw-r--r-- | addons/metadata.generic.artists/lib/allmusic.py | 108 | ||||
-rw-r--r-- | addons/metadata.generic.artists/lib/discogs.py | 45 | ||||
-rw-r--r-- | addons/metadata.generic.artists/lib/fanarttv.py | 48 | ||||
-rw-r--r-- | addons/metadata.generic.artists/lib/musicbrainz.py | 60 | ||||
-rw-r--r-- | addons/metadata.generic.artists/lib/nfo.py | 8 | ||||
-rw-r--r-- | addons/metadata.generic.artists/lib/scraper.py | 503 | ||||
-rw-r--r-- | addons/metadata.generic.artists/lib/theaudiodb.py | 123 | ||||
-rw-r--r-- | addons/metadata.generic.artists/lib/utils.py | 26 | ||||
-rw-r--r-- | addons/metadata.generic.artists/lib/wikipedia.py | 9 |
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 |