Topic: coverart update for 0.15b2 and local files
I have coded an update for the coverart.py plugin. (Which downloads cover art for any releases that have a CoverArtLink or ASIN relation.)
The code shown below works with Picard 0.15.0beta2, and will also process a local file if one exists.
Credit for these changes has to be given to other members of the forum. I started with the coverart.py update mentioned in this post http://forums.musicbrainz.org/viewtopic … 869#p13869, and which can be downloaded from this link http://users.musicbrainz.org/~luks/pica … overart.py.
I then stole the code from this post http://forums.musicbrainz.org/viewtopic … 724#p11724 by rockmaloins, and integrated it.
I made a few changes to rockmaloins code when processing local files - I look in each directory only once, and search for files starting with "cover", "folder" and the user defined coverart name saved in the Picard options. Multiple filetypes are searched (something that was commented our in the original code). The first file found is used.
Hope sombody (other than me) can make use of my modification.
Cheers,
Dave.
"""
A small plugin to download cover art for any releseas that have a
CoverArtLink or ASIN relation.
Changelog:
[2008-04-15] Refactored the code to be similar to the server code (hartzell, phw)
[2008-03-10] Added CDBaby support (phw)
[2007-09-06] Added Jamendo support (phw)
[2007-04-24] Moved parsing code into here
Swapped to QUrl
Moved to a list of urls
[2007-04-23] Moved it to use the bzr picard
Took the hack out
Added Amazon ASIN support
[2007-04-23] Initial plugin, uses a hack that relies on Python being
installed and musicbrainz2 for the query.
"""
PLUGIN_NAME = 'Cover Art Downloader'
PLUGIN_AUTHOR = 'Oliver Charles, Philipp Wolfer'
PLUGIN_DESCRIPTION = '''Downloads cover artwork for releases that have a
CoverArtLink or ASIN.'''
PLUGIN_VERSION = "0.6.3"
PLUGIN_API_VERSIONS = ["0.12", "0.15"]
from picard.metadata import register_album_metadata_processor
from picard.util import partial, mimetype
from PyQt4.QtCore import QUrl
import re
import traceback
import os
# data transliterated from the perl stuff used to find cover art for the
# musicbrainz server.
# See mb_server/cgi-bin/MusicBrainz/Server/CoverArt.pm
# hartzell --- Tue Apr 15 15:25:58 PDT 2008
COVERART_SITES = (
# CD-Baby
# tested with http://musicbrainz.org/release/1243cc17-b9f7-48bd-a536-b10d2013c938.html
{
'regexp': 'http://(www\.)?cdbaby.com/cd/(\w)(\w)(\w*)',
'imguri': 'http://cdbaby.name/$2/$3/$2$3$4.jpg',
},
# Jamendo
# tested with http://musicbrainz.org/release/2fe63977-bda9-45da-8184-25a4e7af8da7.html
{
'regexp': 'http:\/\/(?:www.)?jamendo.com\/(?:[a-z]+\/)?album\/([0-9]+)',
'imguri': 'http://www.jamendo.com/get/album/id/album/artworkurl/redirect/$1/?artwork_size=0',
},
)
# amazon image file names are unique on all servers and constructed like
# <ASIN>.<ServerNumber>.[SML]ZZZZZZZ.jpg
# A release sold on amazon.de has always <ServerNumber> = 03, for example.
# Releases not sold on amazon.com, don't have a "01"-version of the image,
# so we need to make sure we grab an existing image.
AMAZON_SERVER = {
"amazon.jp": {
"server": "ec1.images-amazon.com",
"id" : "09",
},
"amazon.co.jp": {
"server": "ec1.images-amazon.com",
"id" : "09",
},
"amazon.co.uk": {
"server": "ec1.images-amazon.com",
"id" : "02",
},
"amazon.de": {
"server": "ec2.images-amazon.com",
"id" : "03",
},
"amazon.com": {
"server": "ec1.images-amazon.com",
"id" : "01",
},
"amazon.ca": {
"server": "ec1.images-amazon.com",
"id" : "01", # .com and .ca are identical
},
"amazon.fr": {
"server": "ec1.images-amazon.com",
"id" : "08"
},
}
AMAZON_IMAGE_PATH = '/images/P/%s.%s.%sZZZZZZZ.jpg'
AMAZON_ASIN_URL_REGEX = re.compile(r'^http://(?:www.)?(.*?)(?:\:[0-9]+)?/.*/([0-9B][0-9A-Z]{9})(?:[^0-9A-Z]|$)')
def _coverart_downloaded(album, metadata, release, try_list, data, http, error):
try:
if error or len(data) < 1000:
if error:
album.log.error(str(http.errorString()))
coverart(album, metadata, release, try_list)
else:
mime = mimetype.get_from_data(data, default="image/jpeg")
metadata.add_image(mime, data)
for track in album._new_tracks:
track.metadata.add_image(mime, data)
finally:
album._requests -= 1
album._finalize_loading(None)
def coverart(album, metadata, release, try_list=None):
""" Gets all cover art URLs from the metadata and then attempts to
download the album art. """
# try_list will be None for the first call
if try_list is None:
try_list = []
_process_local_files(try_list, album)
try:
if release.children.has_key('relation_list'):
for relation_list in release.relation_list:
if relation_list.target_type == 'url':
for relation in relation_list.relation:
_process_url_relation(try_list, relation)
# Use the URL of a cover art link directly
if relation.type == 'cover art link' or relation.type == 'has_cover_art_at':
_try_list_append_image_url(try_list, QUrl(relation.target[0].text))
elif relation.type == 'amazon asin' or relation.type == 'has_Amazon_ASIN':
_process_asin_relation(try_list, relation)
except AttributeError, e:
album.log.error(traceback.format_exc())
if len(try_list) > 0:
# We still have some items to try!
album._requests += 1
if try_list[0]['host'] == 'localhost':
f = open(try_list[0]['path'], 'rb')
data = f.read()
mime = mimetype.get_from_data(data)
metadata.add_image(mime, data)
for track in album._new_tracks:
track.metadata.add_image(mime, data)
f.close()
album._requests -= 1
album._finalize_loading(None)
else:
album.tagger.xmlws.download(
try_list[0]['host'], try_list[0]['port'], try_list[0]['path'],
partial(_coverart_downloaded, album, metadata, release, try_list[1:]),
position=1)
def _process_url_relation(try_list, relation):
# Search for cover art on special sites
for site in COVERART_SITES:
# this loop transliterated from the perl stuff used to find cover art for the
# musicbrainz server.
# See mb_server/cgi-bin/MusicBrainz/Server/CoverArt.pm
# hartzell --- Tue Apr 15 15:25:58 PDT 2008
match = re.match(site['regexp'], relation.target[0].text)
if match != None:
imgURI = site['imguri']
for i in range(1, len(match.groups())+1 ):
if match.group(i) != None:
imgURI = imgURI.replace('$' + str(i), match.group(i))
_try_list_append_image_url(try_list, QUrl(imgURI))
def _process_asin_relation(try_list, relation):
match = AMAZON_ASIN_URL_REGEX.match(relation.target[0].text)
if match != None:
asinHost = match.group(1)
asin = match.group(2);
if AMAZON_SERVER.has_key(asinHost):
serverInfo = AMAZON_SERVER[asinHost]
else:
serverInfo = AMAZON_SERVER['amazon.com']
try_list.append({'host': serverInfo['server'], 'port': 80,
'path': AMAZON_IMAGE_PATH % (asin, serverInfo['id'], 'L')
})
try_list.append({'host': serverInfo['server'], 'port': 80,
'path': AMAZON_IMAGE_PATH % (asin, serverInfo['id'], 'M')
})
def _try_list_append_image_url(try_list, parsedUrl):
path = str(parsedUrl.encodedPath())
if parsedUrl.hasQuery():
path += '?' + parsedUrl.encodedQuery()
try_list.append({
'host': str(parsedUrl.host()),
'port': parsedUrl.port(80),
'path': str(path)
})
def _process_local_files(try_list, album):
filenameList = []
parsedDirectories = {}
for file in album.iterfiles():
currentDirectory = os.path.dirname(file.filename)
if (currentDirectory in parsedDirectories):
continue
parsedDirectories[currentDirectory] = 1
for root, dirs, files in os.walk(currentDirectory):
for filename in files:
lowercaseFilename = filename.lower()
if (lowercaseFilename.endswith(".jpg") or
lowercaseFilename.endswith(".jpeg") or
lowercaseFilename.endswith(".png") or
lowercaseFilename.endswith(".gif") or
lowercaseFilename.endswith(".tif") or
lowercaseFilename.endswith(".tiff")):
if (lowercaseFilename.startswith("cover.") or
lowercaseFilename.startswith("folder.") or
lowercaseFilename.startswith(album.config.setting['cover_image_filename'].lower() + '.')):
album.log.debug("coverart _process_local_files found %r", filename)
filenameList.append(os.path.join(currentDirectory, root, filename))
imageFilename = ""
if len(filenameList) > 0:
imageFilename = filenameList[0]
if os.path.exists(imageFilename):
try_list.append({
'host': 'localhost',
'port': '0',
'path': imageFilename
})
register_album_metadata_processor(coverart)