Skip to content

Instantly share code, notes, and snippets.

@felipeochoa
Created April 22, 2013 23:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save felipeochoa/5439424 to your computer and use it in GitHub Desktop.
Save felipeochoa/5439424 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
"""
Used to fetch cover art. Based heavily on obeythepenguin's
`Cover Fetcher' (http://coverfetcher.sourceforge.net/)
obeythepenguin AT users.sourceforge.net, but attempting to
provide a CLI interface.
"""
import argparse
import urllib.request
import urllib.parse
import xml.etree.ElementTree as etree
import pdb
BASE_URL = ("http://ws.audioscrobbler.com/2.0/?"
"method=album.getinfo"
"&api_key=XXXXXX") # Need to input key!
class CoverError(Exception):
"""Base class for the errors in this module."""
pass
class CommunicationError(CoverError):
"""
Raised when there's a communication failure with LastFM.
"""
def __init__(self, reason):
self.reason = reason
class ResourceError(CoverError):
"""
Raised when the artwork is not on the server.
"""
def __init__(self, album, artist):
self.album = album
self.artist = artist
class AlbumError(ResourceError):
"""Raised when the album is not found on the server."""
class ArtError(ResourceError):
"""Raised when art is not on the server, but the album is."""
def get_xml(album, artist):
"""Get the album xml data (as a file-like object)."""
target = BASE_URL
target += "&album=" +urllib.parse.quote(album)
target += "&artist=" +urllib.parse.quote(artist)
# URLError can be raised by following line
return urllib.request.urlopen(target)
def get_url(xml, size):
"""Get the url with the location of the artwork."""
# Based on Sam Wirch's post
# www.samwirch.com/blog/parsing-xml-data-lists-python-3
tree = etree.parse(xml)
album_node = tree.getroot().find("album")
sizes = ['extralarge', 'large', 'medium', 'small']
sizes.remove(size)
sizes = [size] + sizes
for s in sizes:
for child in album_node.findall('image'):
if s == child.get('size'):
if child.text is not None:
return child.text
return None
def get_picture(album, artist, size):
"""
Retrieve the cover art.
Size is one of 'small', 'medium', 'large', or 'extralarge',
determined by LastFM.
Returns the picture as a readable file-like object.
"""
try:
xml = get_xml(album, artist)
except urllib.request.HTTPError as e:
if e.code == 400:
raise AlbumError(album, artist)
else:
raise CommunicationError(e.code)
except urllib.request.URLError as e:
raise CommunicationError(e.reason)
location = get_url(xml, size)
if location is None:
raise ArtError(album, artist)
try:
return urllib.request.urlopen(location)
except urllib.request.HTTPError as e:
raise CommunicationError(e.code)
except urllib.request.URLError as e:
raise CommunicationError(e.reason)
def parse_args():
"""Parse the command line args."""
usage = """
This is the CLI-Cover-Fetcher. Use it like this:
python coverfetcher.py [options] <artist> <album>
The result will be saved as <artist>_<album>.jpg (with spaces
replaced by dashes.
"""
parser = argparse.ArgumentParser(usage)
help = "The name of the artist."
parser.add_argument("artist", help=help)
help = "The name of the album."
parser.add_argument("album", help=help)
help = "Override the default file location."
parser.add_argument("-t", "--target", help=help,
metavar='f')
help = ("One of 'small', 'medium', 'large', or 'extralarge'."
"(Default 'extralarge')")
parser.add_argument("-s", "--size", help=help,
default='extralarge')
args = parser.parse_args()
return args
def main():
args = parse_args()
if args.target is None:
args.target = args.artist + '_' + args.album
args.target = args.target.replace(' ','-')
args.target += '.png'
try:
picture = get_picture(args.album, args.artist, args.size)
picture = picture.read()
except ArtError as e:
from sys import stderr
stderr.write("LastFM has no artwork for {0.album} by "
"{0.artist}! (The album is there though)\n".format(
args))
return False
except AlbumError as e:
from sys import stderr
stderr.write("{0.album} by {0.artist} is not "
"on the LastFM server!\n".format(args))
return False
except CommunicationError:
from sys import stderr
stderr.write("I couldn't communicate with the LastFM "
"server!\n")
return False
with open(args.target, 'wb') as f:
f.write(picture)
return True
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment