我正在尝试为 Plex 编辑 LastFM 音乐插件,以使其与音乐视频一起使用。我几乎在那里,有很多谷歌搜索和一些问题,但我坚持艺术家似乎聚集在一起的事实。
我的视频采用“艺术家 - Title.ext”格式,我已将其设置为元数据标题,但它似乎仍然分组。
即使我在将视频分组后拆分为 Plex,它们都具有第一个视频的名称。
我不确定这是否是正确的地方,但这里是代码。如果有人可以提供帮助,将不胜感激。
import lastfm, re, time, os, urllib
GOOGLE_JSON = 'http://ajax.googleapis.com/ajax/services/search/web?v=1.0&userip=%s&rsz=large&q=%s+site:last.fm+inurl:music'
def Start():
HTTP.CacheTime = CACHE_1WEEK
def GetPublicIP():
return HTTP.Request('http://plexapp.com/ip.php').content.strip()
def GetGoogleArtist(artist):
try:
url = GOOGLE_JSON % (GetPublicIP(), String.Quote(artist.encode('utf-8'), usePlus=True))
jsonObj = JSON.ObjectFromURL(url, headers={'Referer' : 'http://www.plexapp.com'}, sleep=0.5)
if jsonObj['responseData'] != None:
jsonObj = jsonObj['responseData']['results']
if len(jsonObj) > 0:
result = jsonObj[0]
url = result['unescapedUrl'].replace('+','%20')
return re.findall('/music/([^/]+)', url)[0]
except:
pass
return None
def CallWithRetries(fun, *args):
tries = 10
while tries > 0:
try:
return fun(*args)
except:
tries = tries - 1
if tries > 0:
Log('Call failed, retrying')
time.sleep(2)
else:
raise
class LastFmAgent(Agent.Movies, Agent.Artist):
name = 'Music Video'
languages = [Locale.Language.English, Locale.Language.Korean]
def safe_strip(self, ss):
"""
This method strips the diacritic marks from a string, but if it's too extreme (i.e. would remove everything,
as is the case with some foreign text), then don't perform the strip.
"""
s = String.StripDiacritics(ss)
if len(s.strip()) == 0:
return ss
return s
def search(self, results, media, lang):
path = media.filename
path = urllib.unquote(path)
filename = os.path.splitext(os.path.basename(path))[0]
Log(filename)
artist = filename.split(' - ')[0]
artist = artist.lower()
Log(artist)
Log("Search for artist")
CallWithRetries(self.findArtists, lang, results, media, artist)
Log("If the artist starts with 'The', try stripping.")
if artist.startswith('the '):
try: CallWithRetries(self.findArtists, lang, results, media, artist[4:])
except: pass
Log("If the artist has an '&', try with 'and'.")
if artist.find(' & ') != -1:
try: CallWithRetries(self.findArtists, lang, results, media, artist.replace(' & ', ' and '))
except: pass
Log("If the artist has an 'and', try with '&'.")
if artist.find(' and ') != -1:
try: CallWithRetries(self.findArtists, lang, results, media, artist.replace(' and ', ' & '))
except: pass
try: highest_score = max([x.score for x in results])
except: highest_score = 0
if len(results) == 0 or highest_score < 85:
artist_id = GetGoogleArtist(artist)
google_artist = CallWithRetries(lastfm.ArtistInfo, artist_id)
if google_artist:
(url, name, image, listeners) = google_artist
Log("Google said 'you should try %s' (ID: %s)." % (name, artist_id))
if listeners > 250:
results.Append(MetadataSearchResult(id=artist_id, name=name, thumb=image, lang=lang, score = 100-Util.LevenshteinDistance(name.lower(), artist.lower())))
Log("Finally, de-dupe the results.")
toWhack = []
resultMap = {}
for result in results:
if not resultMap.has_key(result.id):
resultMap[result.id] = True
else:
toWhack.append(result)
for dupe in toWhack:
results.Remove(dupe)
def findArtists(self, lang, results, media, artist):
score = 90
for r in lastfm.SearchArtists(artist,limit=5)[0]:
id = r[0]
Log(id)
# Skip artists without many listeners, they're probanly wrong.
if r[3] < 1000 and id.find('+noredirect') == -1:
Log("Skipping %s with only %d listeners." % (r[1], r[3]))
continue
if id.find('+noredirect') == -1:
id = r[1]
dist = Util.LevenshteinDistance(r[1].lower(), artist.lower())
albumBonus = self.bonusArtistMatchUsingAlbums(media, artistID=id, maxBonus=5)
id = String.Quote(id.encode('utf-8'))
Log('artist: ' + artist + ' albumBonus: ' + str(albumBonus))
Log('Artist result: ' + r[1] + ' id: ' + id + ' score: ' + str(score) + ' thumb: ' + str(r[2]))
results.Append(MetadataSearchResult(id = id.replace('%2B','%20'), name = r[1], thumb = r[2], lang = lang, score = score + albumBonus - dist))
else:
# Get a correction.
Log('Getting correction to artist.')
correctArtists = lastfm.CorrectedArtists(artist)
for result in correctArtists:
id = String.Quote(result[0].encode('utf-8'))
dist = Util.LevenshteinDistance(result[0].lower(), artist.lower())
results.Append(MetadataSearchResult(id = id.replace('%2B','%20'), name = result[0], lang = lang, score = score - dist + 5))
score = score - 2
def bonusArtistMatchUsingAlbums(self, media, artistID, maxBonus=5):
Log('bonusArtistMatchUsingAlbums')
lastFM_artistAlbums = []
for album in lastfm.ArtistAlbums(artistID):
(name, artist, thumb, url) = album
lastFM_artistAlbums.append(name.lower())
if len(lastFM_artistAlbums) == 0: return 0 #no last.fm albums for the artist, so abort!
bonus = 0
for a in media.children:
album = a.title.lower()
for lfa in lastFM_artistAlbums:
score = Util.LevenshteinDistance(lfa, album)
#Log(lfa, album, score)
if score <= 2: #pretty solid match
bonus += 1
if bonus == maxBonus: break
if bonus == 0 and album[-1:] == ')': #if we got nothing, let's try again without anything in paranthesis [e.g.'limited edition']
album = album[:album.rfind('(')].strip()
for lfa in lastFM_artistAlbums:
score = Util.LevenshteinDistance(lfa, album)
#Log(lfa, album, score)
if score <= 2: #pretty solid match
bonus += 1
if bonus == maxBonus: break
return bonus
def update(self, metadata, media, lang):
artist = CallWithRetries(XML.ElementFromURL, lastfm.ARTIST_INFO % String.Quote(String.Unquote(metadata.id), True))[0]
summary = artist.xpath('//bio/content')[0]
metadata.title = String.Unquote(artist.xpath('//artist/name')[0].text, True)
Log(metadata.title)
filename = media.items[0].parts[0].file.decode('utf-8')
cleanname = os.path.splitext(os.path.basename(filename))[0]
Log(cleanname)
metadata.title = cleanname
Log(metadata.title)
Log("QWERTY")
if summary.text:
metadata.summary = decodeXml(re.sub(r'<[^<>]+>', '', summary.text))
try:
url = artist.xpath('//artist/image[@size="mega"]//text()')[0]
if url not in metadata.posters:
metadata.posters[url] = Proxy.Media(HTTP.Request(url))
except:
pass
metadata.genres.clear()
for genre in artist.xpath('//artist/tags/tag/name'):
metadata.genres.add(genre.text.capitalize())
def decodeXml(text):
trans = [('&','&'),('"','"'),('<','<'),('>','>'),(''','\''),('\n ','\n')]
for src, dst in trans:
text = text.replace(src, dst)
return text