This commit is contained in:
2025-10-25 13:21:06 +02:00
parent eb57506d39
commit 033ffb21f5
8388 changed files with 484789 additions and 16 deletions

View File

@@ -0,0 +1,113 @@
# -*- coding: utf-8 -*-
import datetime
import difflib
import time
import re
def allmusic_albumfind(data, artist, album):
data = data.decode('utf-8')
albums = []
albumlist = re.findall('class="album">\s*(.*?)\s*</li', data, re.S)
for item in albumlist:
albumdata = {}
albumartist = re.search('class="artist">.*?>(.*?)</a', item, re.S)
if albumartist:
albumdata['artist'] = albumartist.group(1)
else: # classical album
continue
albumname = re.search('class="title">.*?>(.*?)</a', item, re.S)
if albumname:
albumdata['album'] = albumname.group(1)
else: # not likely to happen, but just in case
continue
# filter inaccurate results
artistmatch = difflib.SequenceMatcher(None, artist.lower(), albumdata['artist'].lower()).ratio()
albummatch = difflib.SequenceMatcher(None, album.lower(), albumdata['album'].lower()).ratio()
if artistmatch > 0.90 and albummatch > 0.90:
albumurl = re.search('class="title">\s*<a href="(.*?)"', item)
if albumurl:
albumdata['url'] = albumurl.group(1)
else: # not likely to happen, but just in case
continue
albums.append(albumdata)
# we are only interested in the top result
break
return albums
def allmusic_albumdetails(data):
data = data.decode('utf-8')
albumdata = {}
releasedata = re.search('class="release-date">.*?<span>(.*?)<', data, re.S)
if releasedata:
dateformat = releasedata.group(1)
if len(dateformat) > 4:
try:
# month day, year
albumdata['releasedate'] = datetime.datetime(*(time.strptime(dateformat, '%B %d, %Y')[0:3])).strftime('%Y-%m-%d')
except:
# month, year
albumdata['releasedate'] = datetime.datetime(*(time.strptime(dateformat, '%B, %Y')[0:3])).strftime('%Y-%m')
else:
# year
albumdata['releasedate'] = dateformat
yeardata = re.search('class="year".*?>\s*(.*?)\s*<', data)
if yeardata:
albumdata['year'] = yeardata.group(1)
genredata = re.search('class="genre">.*?">(.*?)<', data, re.S)
if genredata:
albumdata['genre'] = genredata.group(1)
styledata = re.search('class="styles">.*?div>\s*(.*?)\s*</div', data, re.S)
if styledata:
stylelist = re.findall('">(.*?)<', styledata.group(1))
if stylelist:
albumdata['styles'] = ' / '.join(stylelist)
mooddata = re.search('class="moods">.*?div>\s*(.*?)\s*</div', data, re.S)
if mooddata:
moodlist = re.findall('">(.*?)<', mooddata.group(1))
if moodlist:
albumdata['moods'] = ' / '.join(moodlist)
themedata = re.search('class="themes">.*?div>\s*(.*?)\s*</div', data, re.S)
if themedata:
themelist = re.findall('">(.*?)<', themedata.group(1))
if themelist:
albumdata['themes'] = ' / '.join(themelist)
ratingdata = re.search('itemprop="ratingValue">\s*(.*?)\s*</div', data)
if ratingdata:
albumdata['rating'] = ratingdata.group(1)
albumdata['votes'] = ''
titledata = re.search('class="album-title".*?>\s*(.*?)\s*<', data, re.S)
if titledata:
albumdata['album'] = titledata.group(1)
labeldata = re.search('class="label-catalog".*?<.*?>(.*?)<', data, re.S)
if labeldata:
albumdata['label'] = labeldata.group(1)
artistdata = re.search('class="album-artist".*?<span.*?>\s*(.*?)\s*</span', data, re.S)
if artistdata:
artistlist = re.findall('">(.*?)<', artistdata.group(1))
artists = []
for item in artistlist:
artistinfo = {}
artistinfo['artist'] = item
artists.append(artistinfo)
if artists:
albumdata['artist'] = artists
albumdata['artist_description'] = ' / '.join(artistlist)
thumbsdata = re.search('class="album-contain".*?src="(.*?)"', data, re.S)
if thumbsdata:
thumbs = []
thumbdata = {}
thumb = thumbsdata.group(1).rstrip('?partner=allrovi.com')
# ignore internal blank thumb
if thumb.startswith('http'):
# 0=largest / 1=75 / 2=150 / 3=250 / 4=400 / 5=500 / 6=1080
if thumb.endswith('f=5'):
thumbdata['image'] = thumb.replace('f=5', 'f=0')
thumbdata['preview'] = thumb.replace('f=5', 'f=2')
else:
thumbdata['image'] = thumb
thumbdata['preview'] = thumb
thumbdata['aspect'] = 'thumb'
thumbs.append(thumbdata)
albumdata['thumb'] = thumbs
return albumdata

View File

@@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
import difflib
def discogs_albumfind(data, artist, album):
albums = []
masters = []
# sort results by lowest release id (first version of a release)
releases = sorted(data.get('results',[]), key=lambda k: k['id'])
for item in releases:
masterid = item['master_id']
# we are not interested in multiple versions that belong to the same master release
if masterid not in masters:
masters.append(masterid)
albumdata = {}
albumdata['artist'] = item['title'].split(' - ',1)[0]
albumdata['album'] = item['title'].split(' - ',1)[1]
albumdata['artist_description'] = item['title'].split(' - ',1)[0]
albumdata['year'] = str(item.get('year', ''))
albumdata['label'] = item['label'][0]
albumdata['thumb'] = item['thumb']
albumdata['dcalbumid'] = item['id']
# discogs does not provide relevance, use our own
artistmatch = difflib.SequenceMatcher(None, artist.lower(), albumdata['artist'].lower()).ratio()
albummatch = difflib.SequenceMatcher(None, album.lower(), albumdata['album'].lower()).ratio()
if artistmatch > 0.90 and albummatch > 0.90:
score = round(((artistmatch + albummatch) / 2), 2)
albumdata['relevance'] = str(score)
albums.append(albumdata)
return albums
def discogs_albummain(data):
if data:
if 'main_release_url' in data:
url = data['main_release_url'].rsplit('/', 1)[1]
return url
def discogs_albumdetails(data):
albumdata = {}
albumdata['album'] = data['title']
if 'styles' in data:
albumdata['styles'] = ' / '.join(data['styles'])
albumdata['genres'] = ' / '.join(data['genres'])
albumdata['year'] = str(data['year'])
albumdata['label'] = data['labels'][0]['name']
artists = []
for artist in data['artists']:
artistdata = {}
artistdata['artist'] = artist['name']
artists.append(artistdata)
albumdata['artist'] = artists
albumdata['artist_description'] = data['artists_sort']
albumdata['rating'] = str(int((float(data['community']['rating']['average']) * 2) + 0.5))
albumdata['votes'] = str(data['community']['rating']['count'])
if 'images' in data:
thumbs = []
for thumb in data['images']:
thumbdata = {}
thumbdata['image'] = thumb['uri']
thumbdata['preview'] = thumb['uri150']
# not accurate: discogs can provide any art type, there is no indication if it is an album front cover (thumb)
thumbdata['aspect'] = 'thumb'
thumbs.append(thumbdata)
albumdata['thumb'] = thumbs
return albumdata

View File

@@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
def fanarttv_albumart(data):
if 'albums' in data:
albumdata = {}
thumbs = []
extras = []
discs = {}
for mbid, art in data['albums'].items():
if 'albumcover' in art:
for thumb in art['albumcover']:
thumbdata = {}
thumbdata['image'] = thumb['url']
thumbdata['preview'] = thumb['url'].replace('/fanart/', '/preview/')
thumbdata['aspect'] = 'thumb'
thumbs.append(thumbdata)
if 'cdart' in art:
for cdart in art['cdart']:
extradata = {}
extradata['image'] = cdart['url']
extradata['preview'] = cdart['url'].replace('/fanart/', '/preview/')
extradata['aspect'] = 'discart'
extras.append(extradata)
# support for multi-disc albums
multidata = {}
num = cdart['disc']
multidata['image'] = cdart['url']
multidata['preview'] = cdart['url'].replace('/fanart/', '/preview/')
multidata['aspect'] = 'discart%s' % num
if not num in discs:
discs[num] = [multidata]
else:
discs[num].append(multidata)
if thumbs:
albumdata['thumb'] = thumbs
# only return for multi-discs, not single discs
if len(discs) > 1:
for k, v in discs.items():
for item in v:
extras.append(item)
if extras:
albumdata['extras'] = extras
return albumdata

View File

@@ -0,0 +1,165 @@
# -*- coding: utf-8 -*-
def musicbrainz_albumfind(data, artist, album):
albums = []
# count how often each releasegroup occurs in the release results
# keep track of the release with the highest score and earliest releasedate in each releasegroup
releasegroups = {}
for item in data.get('releases'):
mbid = item['id']
score = item.get('score', 0)
releasegroup = item['release-group']['id']
if 'date' in item and item['date']:
date = item['date'].replace('-','')
if len(date) == 4:
date = date + '9999'
else:
date = '99999999'
if releasegroup in releasegroups:
count = releasegroups[releasegroup][0] + 1
topmbid = releasegroups[releasegroup][1]
topdate = releasegroups[releasegroup][2]
topscore = releasegroups[releasegroup][3]
if date < topdate and score >= topscore:
topdate = date
topmbid = mbid
releasegroups[releasegroup] = [count, topmbid, topdate, topscore]
else:
releasegroups[releasegroup] = [1, mbid, date, score]
if releasegroups:
# get the highest releasegroup count
maxcount = max(releasegroups.values())[0]
# get the releasegroup(s) that match this highest value
topgroups = [k for k, v in releasegroups.items() if v[0] == maxcount]
for item in data.get('releases'):
# only use the 'top' release from each releasegroup
if item['id'] != releasegroups[item['release-group']['id']][1]:
continue
albumdata = {}
if item.get('artist-credit'):
artists = []
artistdisp = ""
for artist in item['artist-credit']:
artistdata = {}
artistdata['artist'] = artist['artist']['name']
artistdata['mbartistid'] = artist['artist']['id']
artistdata['artistsort'] = artist['artist']['sort-name']
artistdisp = artistdisp + artist['artist']['name']
artistdisp = artistdisp + artist.get('joinphrase', '')
artists.append(artistdata)
albumdata['artist'] = artists
albumdata['artist_description'] = artistdisp
if item.get('label-info','') and item['label-info'][0].get('label','') and item['label-info'][0]['label'].get('name',''):
albumdata['label'] = item['label-info'][0]['label']['name']
albumdata['album'] = item['title']
if item.get('date',''):
albumdata['year'] = item['date'][:4]
albumdata['thumb'] = 'https://coverartarchive.org/release-group/%s/front-250' % item['release-group']['id']
if item.get('label-info','') and item['label-info'][0].get('label','') and item['label-info'][0]['label'].get('name',''):
albumdata['label'] = item['label-info'][0]['label']['name']
if item.get('status',''):
albumdata['releasestatus'] = item['status']
albumdata['type'] = item['release-group'].get('primary-type')
albumdata['mbalbumid'] = item['id']
albumdata['mbreleasegroupid'] = item['release-group']['id']
if item.get('score'):
releasescore = item['score'] / 100.0
# if the release is in the releasegroup with most releases, it is considered the most accurate one
# (this also helps with preferring official releases over bootlegs, assuming there are more variations of an official release than of a bootleg)
if item['release-group']['id'] not in topgroups:
releasescore -= 0.001
# if the release is an album, prefer it over singles/ep's
# (this needs to be the double of the above, as me might have just given the album a lesser score if the single happened to be in the topgroup)
if item['release-group'].get('primary-type') != 'Album':
releasescore -= 0.002
albumdata['relevance'] = str(releasescore)
albums.append(albumdata)
return albums
def musicbrainz_albumlinks(data):
albumlinks = {}
if 'relations' in data and data['relations']:
for item in data['relations']:
if item['type'] == 'allmusic':
albumlinks['allmusic'] = item['url']['resource']
elif item['type'] == 'discogs':
albumlinks['discogs'] = item['url']['resource'].rsplit('/', 1)[1]
elif item['type'] == 'wikipedia':
albumlinks['wikipedia'] = item['url']['resource'].rsplit('/', 1)[1]
elif item['type'] == 'wikidata':
albumlinks['wikidata'] = item['url']['resource'].rsplit('/', 1)[1]
return albumlinks
def musicbrainz_albumdetails(data):
albumdata = {}
albumdata['album'] = data['title']
albumdata['mbalbumid'] = data['id']
if data.get('release-group',''):
albumdata['mbreleasegroupid'] = data['release-group']['id']
if data['release-group']['rating'] and data['release-group']['rating']['value']:
albumdata['rating'] = str(int((float(data['release-group']['rating']['value']) * 2) + 0.5))
albumdata['votes'] = str(data['release-group']['rating']['votes-count'])
if data['release-group'].get('primary-type'):
albumtypes = [data['release-group']['primary-type']] + data['release-group']['secondary-types']
albumdata['type'] = ' / '.join(albumtypes)
if 'Compilation' in albumtypes:
albumdata['compilation'] = 'true'
if data['release-group'].get('first-release-date',''):
albumdata['originaldate'] = data['release-group']['first-release-date']
if data.get('release-events',''):
albumdata['year'] = data['release-events'][0]['date'][:4]
albumdata['releasedate'] = data['release-events'][0]['date']
if data.get('label-info','') and data['label-info'][0].get('label','') and data['label-info'][0]['label'].get('name',''):
albumdata['label'] = data['label-info'][0]['label']['name']
if data.get('status',''):
albumdata['releasestatus'] = data['status']
if data.get('artist-credit'):
artists = []
artistdisp = ''
for artist in data['artist-credit']:
artistdata = {}
artistdata['artist'] = artist['name']
artistdata['mbartistid'] = artist['artist']['id']
artistdata['artistsort'] = artist['artist']['sort-name']
artistdisp = artistdisp + artist['name']
artistdisp = artistdisp + artist.get('joinphrase', '')
artists.append(artistdata)
albumdata['artist'] = artists
albumdata['artist_description'] = artistdisp
return albumdata
def musicbrainz_albumart(data):
albumdata = {}
thumbs = []
extras = []
for item in data['images']:
if 'Front' in item['types']:
thumbdata = {}
thumbdata['image'] = item['image']
thumbdata['preview'] = item['thumbnails']['small']
thumbdata['aspect'] = 'thumb'
thumbs.append(thumbdata)
if 'Back' in item['types']:
backdata = {}
backdata['image'] = item['image']
backdata['preview'] = item['thumbnails']['small']
backdata['aspect'] = 'back'
extras.append(backdata)
if 'Medium' in item['types']:
discartdata = {}
discartdata['image'] = item['image']
discartdata['preview'] = item['thumbnails']['small']
discartdata['aspect'] = 'discart'
extras.append(discartdata)
# exclude spine+back images
if 'Spine' in item['types'] and len(item['types']) == 1:
spinedata = {}
spinedata['image'] = item['image']
spinedata['preview'] = item['thumbnails']['small']
spinedata['aspect'] = 'spine'
extras.append(spinedata)
if thumbs:
albumdata['thumb'] = thumbs
if extras:
albumdata['extras'] = extras
return albumdata

View File

@@ -0,0 +1,8 @@
# -*- coding: utf-8 -*-
import re
def nfo_geturl(data):
result = re.search('https://musicbrainz.org/(ws/2/)?release/([0-9a-z\-]*)', data)
if result:
return result.group(2)

View File

@@ -0,0 +1,497 @@
# -*- 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_albumfind
from .allmusic import allmusic_albumdetails
from .discogs import discogs_albumfind
from .discogs import discogs_albummain
from .discogs import discogs_albumdetails
from .fanarttv import fanarttv_albumart
from .musicbrainz import musicbrainz_albumfind
from .musicbrainz import musicbrainz_albumdetails
from .musicbrainz import musicbrainz_albumlinks
from .musicbrainz import musicbrainz_albumart
from .nfo import nfo_geturl
from .theaudiodb import theaudiodb_albumdetails
from .wikipedia import wikipedia_albumdetails
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, album, url, nfo, settings):
# parse path settings
self.parse_settings(settings)
# this is just for backward compatibility 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 / album title matches
elif action == 'find':
# try musicbrainz first
result = self.find_album(artist, album, 'musicbrainz')
if result:
self.return_search(result)
# fallback to discogs
else:
result = self.find_album(artist, album, 'discogs')
if result:
self.return_search(result)
# return info id's
elif action == 'getdetails':
details = {}
links = {}
url = json.loads(url)
artist = url.get('artist')
album = url.get('album')
mbalbumid = url.get('mbalbumid')
mbreleasegroupid = url.get('mbreleasegroupid')
dcid = url.get('dcalbumid')
threads = []
extrascrapers = []
# we have musicbrainz album id
if mbalbumid:
# get the mbreleasegroupid, artist and album if we don't have them
if not mbreleasegroupid:
result = self.get_details(mbalbumid, 'musicbrainz', details)
if not result:
scrapers = [[mbalbumid, 'musicbrainz']]
else:
mbreleasegroupid = details['musicbrainz']['mbreleasegroupid']
artist = details['musicbrainz']['artist_description']
album = details['musicbrainz']['album']
scrapers = [[mbreleasegroupid, 'theaudiodb'], [mbreleasegroupid, 'fanarttv'], [mbreleasegroupid, 'coverarchive']]
else:
scrapers = [[mbalbumid, 'musicbrainz'], [mbreleasegroupid, 'theaudiodb'], [mbreleasegroupid, 'fanarttv'], [mbreleasegroupid, 'coverarchive']]
# get musicbrainz links to other metadata sites
lthread = Thread(target = self.get_links, args = (mbreleasegroupid, links))
lthread.start()
for item in scrapers:
thread = Thread(target = self.get_details, args = (item[0], item[1], details))
threads.append(thread)
thread.start()
# wait for the musicbrainz links to return
lthread.join()
if 'musicbrainz' in links:
# scrape allmusic if we have an url provided by musicbrainz
if 'allmusic' in links['musicbrainz']:
extrascrapers.append([{'url': links['musicbrainz']['allmusic']}, 'allmusic'])
# only scrape allmusic by artistname and albumtitle if explicitly enabled
elif self.inaccurate and artist and album:
extrascrapers.append([{'artist': artist, 'album': album}, 'allmusic'])
# scrape discogs if we have an url provided by musicbrainz
if 'discogs' in links['musicbrainz']:
extrascrapers.append([{'masterurl': links['musicbrainz']['discogs']}, 'discogs'])
# only scrape discogs by artistname and albumtitle if explicitly enabled
elif self.inaccurate and artist and album:
extrascrapers.append([{'artist': artist, 'album': album}, 'discogs'])
# scrape wikipedia if we have an url provided by musicbrainz
if 'wikipedia' in links['musicbrainz']:
extrascrapers.append([links['musicbrainz']['wikipedia'], 'wikipedia'])
elif 'wikidata' in links['musicbrainz']:
extrascrapers.append([links['musicbrainz']['wikidata'], 'wikidata'])
for item in extrascrapers:
thread = Thread(target = self.get_details, args = (item[0], item[1], details))
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()
for thread in threads:
thread.join()
result = self.compile_results(details)
if result:
self.return_details(result)
# extract the mbalbumid from the provided musicbrainz url
elif action == 'NfoUrl':
# check if there is a musicbrainz url in the nfo file
mbalbumid = nfo_geturl(nfo)
if mbalbumid:
# return the result
result = self.resolve_mbid(mbalbumid)
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.review = settings['review']
self.genre = settings['genre']
self.lang = settings['lang']
self.mood = settings['mood']
self.rating = settings['rating']
self.style = settings['style']
self.theme = settings['theme']
self.inaccurate = settings['inaccurate']
def resolve_mbid(self, mbalbumid):
item = {}
item['artist_description'] = ''
item['album'] = ''
item['mbalbumid'] = mbalbumid
item['mbreleasegroupid'] = ''
return item
def find_album(self, artist, album, site):
json = True
# musicbrainz
if site == 'musicbrainz':
url = MUSICBRAINZURL % (MUSICBRAINZSEARCH % (urllib.parse.quote_plus(album), urllib.parse.quote_plus(artist), urllib.parse.quote_plus(artist)))
scraper = musicbrainz_albumfind
# discogs
elif site == 'discogs':
url = DISCOGSURL % (DISCOGSSEARCH % (urllib.parse.quote_plus(album), urllib.parse.quote_plus(artist), DISCOGSKEY , DISCOGSSECRET))
scraper = discogs_albumfind
result = get_data(url, json)
if not result:
return
albumresults = scraper(result, artist, album)
return albumresults
def get_links(self, param, links):
json = True
url = MUSICBRAINZURL % (MUSICBRAINZLINKS % param)
result = get_data(url, json)
if result:
linkresults = musicbrainz_albumlinks(result)
links['musicbrainz'] = linkresults
return links
def get_details(self, param, site, details):
json = True
# theaudiodb
if site == 'theaudiodb':
url = AUDIODBURL % (AUDIODBKEY, AUDIODBDETAILS % param)
albumscraper = theaudiodb_albumdetails
# musicbrainz
elif site == 'musicbrainz':
url = MUSICBRAINZURL % (MUSICBRAINZDETAILS % param)
albumscraper = musicbrainz_albumdetails
# fanarttv
elif site == 'fanarttv':
url = FANARTVURL % (param, FANARTVKEY)
albumscraper = fanarttv_albumart
# coverarchive
elif site == 'coverarchive':
url = MUSICBRAINZART % (param)
albumscraper = musicbrainz_albumart
# discogs
elif site == 'discogs':
# musicbrainz provides a link to the master release, but we need the main release
if 'masterurl' in param:
masterdata = get_data(DISCOGSURL % (DISCOGSMASTER % (param['masterurl'], DISCOGSKEY , DISCOGSSECRET)), True)
if masterdata:
url = discogs_albummain(masterdata)
if url:
param['url'] = url
else:
return
else:
return
# search by artistname and albumtitle if we do not have an url
if not 'url' in param:
url = DISCOGSURL % (DISCOGSSEARCH % (urllib.parse.quote_plus(param['album']), urllib.parse.quote_plus(param['artist']), DISCOGSKEY , DISCOGSSECRET))
albumresult = get_data(url, json)
if albumresult:
albums = discogs_albumfind(albumresult, param['artist'], param['album'])
if albums:
albumresult = sorted(albums, key=lambda k: k['relevance'], reverse=True)
param['url'] = albumresult[0]['dcalbumid']
else:
return
else:
return
url = DISCOGSURL % (DISCOGSDETAILS % (param['url'], DISCOGSKEY, DISCOGSSECRET))
albumscraper = discogs_albumdetails
# wikipedia
elif site == 'wikipedia':
url = WIKIPEDIAURL % param
albumscraper = wikipedia_albumdetails
elif site == 'wikidata':
# resolve wikidata to wikipedia url
result = get_data(WIKIDATAURL % param, json)
try:
album = result['entities'][param]['sitelinks']['enwiki']['url'].rsplit('/', 1)[1]
except:
return
site = 'wikipedia'
url = WIKIPEDIAURL % album
albumscraper = wikipedia_albumdetails
# allmusic
elif site == 'allmusic':
json = False
# search by artistname and albumtitle if we do not have an url
if not 'url' in param:
url = ALLMUSICURL % (ALLMUSICSEARCH % (urllib.parse.quote_plus(param['artist']), urllib.parse.quote_plus(param['album'])))
albumresult = get_data(url, json)
if albumresult:
albums = allmusic_albumfind(albumresult, param['artist'], param['album'])
if albums:
param['url'] = albums[0]['url']
else:
return
else:
return
url = ALLMUSICDETAILS % param['url']
albumscraper = allmusic_albumdetails
result = get_data(url, json)
if not result:
return
albumresults = albumscraper(result)
if not albumresults:
return
details[site] = albumresults
return details
def compile_results(self, details):
result = {}
thumbs = []
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)
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 'coverarchive' in details:
for k, v in details['coverarchive'].items():
if v:
result[k] = v
if k == 'thumb' and v:
thumbs.append(v)
if k == 'extras' and v:
extras.append(v)
# prefer artwork from fanarttv
if 'fanarttv' in details:
for k, v in details['fanarttv'].items():
if v:
result[k] = v
if k == 'thumb' and v:
thumbs.append(v)
if k == 'extras' and v:
extras.append(v)
# use musicbrainz artist as it provides the mbartistid (used for resolveid in the artist scraper)
if 'musicbrainz' in details:
result['artist'] = details['musicbrainz']['artist']
# provide artwork from all scrapers for getthumb option
if result:
# thumb list from most accurate sources first
thumbs.reverse()
thumbnails = []
for thumblist in thumbs:
for item in thumblist:
thumbnails.append(item)
# extra art from most accurate sources first
extras.reverse()
extraart = []
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)
if thumbnails:
result['thumb'] = thumbnails
data = self.user_prefs(details, result)
return data
def user_prefs(self, details, result):
# user preferences
lang = 'description' + self.lang
if self.review == 'theaudiodb' and 'theaudiodb' in details:
if lang in details['theaudiodb']:
result['description'] = details['theaudiodb'][lang]
elif 'descriptionEN' in details['theaudiodb']:
result['description'] = details['theaudiodb']['descriptionEN']
elif (self.review in details) and ('description' in details[self.review]):
result['description'] = details[self.review]['description']
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']
if (self.theme in details) and ('themes' in details[self.theme]):
result['themes'] = details[self.theme]['themes']
if (self.rating in details) and ('rating' in details[self.rating]):
result['rating'] = details[self.rating]['rating']
result['votes'] = details[self.rating]['votes']
return result
def return_search(self, data):
items = []
for item in data:
listitem = xbmcgui.ListItem(item['album'], offscreen=True)
listitem.setArt({'thumb': item['thumb']})
listitem.setProperty('album.artist', item['artist_description'])
listitem.setProperty('album.year', item.get('year',''))
listitem.setProperty('album.type', item.get('type',''))
listitem.setProperty('album.releasestatus', item.get('releasestatus',''))
listitem.setProperty('album.label', item.get('label',''))
listitem.setProperty('relevance', item['relevance'])
url = {'artist':item['artist_description'], 'album':item['album']}
if 'mbalbumid' in item:
url['mbalbumid'] = item['mbalbumid']
url['mbreleasegroupid'] = item['mbreleasegroupid']
if 'dcalbumid' in item:
url['dcalbumid'] = item['dcalbumid']
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 'album' in item:
return
listitem = xbmcgui.ListItem(item['album'], offscreen=True)
if 'mbalbumid' in item:
listitem.setProperty('album.musicbrainzid', item['mbalbumid'])
listitem.setProperty('album.releaseid', item['mbalbumid'])
if 'mbreleasegroupid' in item:
listitem.setProperty('album.releasegroupid', item['mbreleasegroupid'])
if 'scrapedmbid' in item:
listitem.setProperty('album.scrapedmbid', item['scrapedmbid'])
if 'artist' in item:
listitem.setProperty('album.artists', str(len(item['artist'])))
for count, artist in enumerate(item['artist']):
listitem.setProperty('album.artist%i.name' % (count + 1), artist['artist'])
listitem.setProperty('album.artist%i.musicbrainzid' % (count + 1), artist.get('mbartistid', ''))
listitem.setProperty('album.artist%i.sortname' % (count + 1), artist.get('artistsort', ''))
if 'genre' in item:
listitem.setProperty('album.genre', item['genre'])
if 'styles' in item:
listitem.setProperty('album.styles', item['styles'])
if 'moods' in item:
listitem.setProperty('album.moods', item['moods'])
if 'themes' in item:
listitem.setProperty('album.themes', item['themes'])
if 'description' in item:
listitem.setProperty('album.review', item['description'])
if 'releasedate' in item:
listitem.setProperty('album.releasedate', item['releasedate'])
if 'originaldate' in item:
listitem.setProperty('album.originaldate', item['originaldate'])
if 'releasestatus' in item:
listitem.setProperty('album.releasestatus', item['releasestatus'])
if 'artist_description' in item:
listitem.setProperty('album.artist_description', item['artist_description'])
if 'label' in item:
listitem.setProperty('album.label', item['label'])
if 'type' in item:
listitem.setProperty('album.type', item['type'])
if 'compilation' in item:
listitem.setProperty('album.compilation', item['compilation'])
if 'year' in item:
listitem.setProperty('album.year', item['year'])
if 'rating' in item:
listitem.setProperty('album.rating', item['rating'])
if 'votes' in item:
listitem.setProperty('album.votes', item['votes'])
if 'thumb' in item:
listitem.setProperty('album.thumbs', str(len(item['thumb'])))
for count, thumb in enumerate(item['thumb']):
listitem.setProperty('album.thumb%i.url' % (count + 1), thumb['image'])
listitem.setProperty('album.thumb%i.aspect' % (count + 1), thumb['aspect'])
listitem.setProperty('album.thumb%i.preview' % (count + 1), thumb['preview'])
xbmcplugin.setResolvedUrl(handle=int(sys.argv[1]), succeeded=True, listitem=listitem)

View File

@@ -0,0 +1,124 @@
# -*- coding: utf-8 -*-
def theaudiodb_albumdetails(data):
if data.get('album'):
item = data['album'][0]
albumdata = {}
albumdata['album'] = item['strAlbum']
if item.get('intYearReleased',''):
albumdata['year'] = item['intYearReleased']
if item.get('strStyle',''):
albumdata['styles'] = item['strStyle']
if item.get('strGenre',''):
albumdata['genre'] = item['strGenre']
if item.get('strLabel',''):
albumdata['label'] = item['strLabel']
if item.get('strReleaseFormat',''):
albumdata['type'] = item['strReleaseFormat']
if item.get('intScore',''):
albumdata['rating'] = str(int(float(item['intScore']) + 0.5))
if item.get('intScoreVotes',''):
albumdata['votes'] = item['intScoreVotes']
if item.get('strMood',''):
albumdata['moods'] = item['strMood']
if item.get('strTheme',''):
albumdata['themes'] = item['strTheme']
if item.get('strMusicBrainzID',''):
albumdata['mbreleasegroupid'] = item['strMusicBrainzID']
# api inconsistent
if item.get('strDescription',''):
albumdata['descriptionEN'] = item['strDescription']
elif item.get('strDescriptionEN',''):
albumdata['descriptionEN'] = item['strDescriptionEN']
if item.get('strDescriptionDE',''):
albumdata['descriptionDE'] = item['strDescriptionDE']
if item.get('strDescriptionFR',''):
albumdata['descriptionFR'] = item['strDescriptionFR']
if item.get('strDescriptionCN',''):
albumdata['descriptionCN'] = item['strDescriptionCN']
if item.get('strDescriptionIT',''):
albumdata['descriptionIT'] = item['strDescriptionIT']
if item.get('strDescriptionJP',''):
albumdata['descriptionJP'] = item['strDescriptionJP']
if item.get('strDescriptionRU',''):
albumdata['descriptionRU'] = item['strDescriptionRU']
if item.get('strDescriptionES',''):
albumdata['descriptionES'] = item['strDescriptionES']
if item.get('strDescriptionPT',''):
albumdata['descriptionPT'] = item['strDescriptionPT']
if item.get('strDescriptionSE',''):
albumdata['descriptionSE'] = item['strDescriptionSE']
if item.get('strDescriptionNL',''):
albumdata['descriptionNL'] = item['strDescriptionNL']
if item.get('strDescriptionHU',''):
albumdata['descriptionHU'] = item['strDescriptionHU']
if item.get('strDescriptionNO',''):
albumdata['descriptionNO'] = item['strDescriptionNO']
if item.get('strDescriptionIL',''):
albumdata['descriptionIL'] = item['strDescriptionIL']
if item.get('strDescriptionPL',''):
albumdata['descriptionPL'] = item['strDescriptionPL']
if item.get('strArtist',''):
albumdata['artist_description'] = item['strArtist']
artists = []
artistdata = {}
artistdata['artist'] = item['strArtist']
if item.get('strMusicBrainzArtistID',''):
artistdata['mbartistid'] = item['strMusicBrainzArtistID']
artists.append(artistdata)
albumdata['artist'] = artists
thumbs = []
extras = []
if item.get('strAlbumThumb',''):
thumbdata = {}
thumbdata['image'] = item['strAlbumThumb']
thumbdata['preview'] = item['strAlbumThumb'] + '/preview'
thumbdata['aspect'] = 'thumb'
thumbs.append(thumbdata)
if item.get('strAlbumThumbBack',''):
extradata = {}
extradata['image'] = item['strAlbumThumbBack']
extradata['preview'] = item['strAlbumThumbBack'] + '/preview'
extradata['aspect'] = 'back'
extras.append(extradata)
if item.get('strAlbumSpine',''):
extradata = {}
extradata['image'] = item['strAlbumSpine']
extradata['preview'] = item['strAlbumSpine'] + '/preview'
extradata['aspect'] = 'spine'
extras.append(extradata)
if item.get('strAlbumCDart',''):
extradata = {}
extradata['image'] = item['strAlbumCDart']
extradata['preview'] = item['strAlbumCDart'] + '/preview'
extradata['aspect'] = 'discart'
extras.append(extradata)
if item.get('strAlbum3DCase',''):
extradata = {}
extradata['image'] = item['strAlbum3DCase']
extradata['preview'] = item['strAlbum3DCase'] + '/preview'
extradata['aspect'] = '3dcase'
extras.append(extradata)
if item.get('strAlbum3DFlat',''):
extradata = {}
extradata['image'] = item['strAlbum3DFlat']
extradata['preview'] = item['strAlbum3DFlat'] + '/preview'
extradata['aspect'] = '3dflat'
extras.append(extradata)
if item.get('strAlbum3DFace',''):
extradata = {}
extradata['image'] = item['strAlbum3DFace']
extradata['preview'] = item['strAlbum3DFace'] + '/preview'
extradata['aspect'] = '3dface'
extras.append(extradata)
if item.get('strAlbum3DThumb',''):
extradata = {}
extradata['image'] = item['strAlbum3DThumb']
extradata['preview'] = item['strAlbum3DThumb'] + '/preview'
extradata['aspect'] = '3dthumb'
extras.append(extradata)
if thumbs:
albumdata['thumb'] = thumbs
if extras:
albumdata['extras'] = extras
return albumdata

View File

@@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
AUDIODBKEY = '95424d43204d6564696538'
AUDIODBURL = 'https://www.theaudiodb.com/api/v1/json/%s/%s'
AUDIODBSEARCH = 'searchalbum.php?s=%s&a=%s'
AUDIODBDETAILS = 'album-mb.php?i=%s'
MUSICBRAINZURL = 'https://musicbrainz.org/ws/2/%s'
MUSICBRAINZSEARCH = 'release/?query=release:"%s"%%20AND%%20(artistname:"%s"%%20OR%%20artist:"%s")&fmt=json'
MUSICBRAINZLINKS = 'release-group/%s?inc=url-rels&fmt=json'
MUSICBRAINZDETAILS = 'release/%s?inc=release-groups+artists+labels+ratings&fmt=json'
MUSICBRAINZART = 'https://coverartarchive.org/release-group/%s'
DISCOGSKEY = 'zACPgktOmNegwbwKWMaC'
DISCOGSSECRET = 'wGuSOeMtfdkQxtERKQKPquyBwExSHdQq'
DISCOGSURL = 'https://api.discogs.com/%s'
DISCOGSSEARCH = 'database/search?release_title=%s&type=release&artist=%s&page=1&per_page=100&key=%s&secret=%s'
DISCOGSMASTER = 'masters/%s?key=%s&secret=%s'
DISCOGSDETAILS = 'releases/%s?key=%s&secret=%s'
ALLMUSICURL = 'https://www.allmusic.com/%s'
ALLMUSICSEARCH = 'search/albums/%s+%s'
ALLMUSICDETAILS = '%s/releases'
FANARTVKEY = '88ca41db0d6878929f1f9771eade41fd'
FANARTVURL = 'https://webservice.fanart.tv/v3/music/albums/%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'

View File

@@ -0,0 +1,9 @@
# -*- coding: utf-8 -*-
import re
def wikipedia_albumdetails(data):
albumdata = {}
# 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:'):
albumdata['description'] = re.sub('\n\n\n== .*? ==\n', ' ', data['query']['pages'][0]['extract'])
return albumdata