Pberndt V4
Direkt zum Inhalt springen
Quellcode lastfm-rip.py
Sourcecode
APP_NAME = 'lastfm-rip.py'
try:
import sys
import xml.dom.minidom
import urllib
import getpass
import hashlib
import os
import re
import time
import dbm
from optparse import OptionParser
except:
print "A required module was not found. Make sure you have the python"
print "standard library installed."
sys.exit(0)
hasEyeD3lib = False
try:
import eyeD3
hasEyeD3lib = True
except:
print "Note: If you install eyeD3 I'll write tags for you!\n"
hasGnomeKeyring = False
try:
import gtk # sets app name
import gnomekeyring
import gconf
hasGnomeKeyring = True
except:
pass
hasUnidecode = False
try:
import unidecode
hasUnidecode = True
except:
pass
def encodeUrl(data):
retval = ""
allowedRange = map(chr,
range(ord('a'), ord('z') + 1) +
range(ord('A'), ord('Z') + 1) +
range(ord('0'), ord('9') + 1))
return "".join((
"%%%02x" % ord(char) if char not in allowedRange else char
for char in data))
parser = OptionParser(usage="%prog [options] <lastfm-url>", epilog=("lastfm-rip.py - Copyright (c) 2009, Phillip "
"Berndt. This software may be redistributed under the terms of the BSD-License"))
parser.add_option("-u", "--username", dest="username",
help="Last.FM username")
parser.add_option("-p", "--password", dest="password",
help="Last.FM password")
parser.add_option("-a", "--artist", dest="artist",
help="Play similar artists")
parser.add_option("-d", "--directory", dest="directory",
help="Store files to this directory", default=".")
parser.add_option("-f", "--filter", dest="filter",
help="Apply argument as a regex to filter songs (artist - title)")
parser.add_option("-r", "--enqueue-rythmbox", dest="enqueue",
default=False, action="store_true",
help="Enqueue downloaded songs in Rythmbox (Requires dbus and pygtk)")
parser.add_option("-n", "--fetch-n-songs", dest="number",
help="Try to fetch exactly n songs, then exit")
parser.add_option("-k", "--keep-list", dest="keep", action="store_true",
help="Keep a persistent list of downloaded songs so this will never download the same song twice")
optlist, args = parser.parse_args()
numberOfSongs = -1
if optlist.number:
try:
numberOfSongs = int(optlist.number)
assert(numberOfSongs > 0)
except:
parser.error("Parameter to -n should be numeric and greater than zero")
if optlist.filter:
try:
re.compile(optlist.filter)
except:
parser.error("--filter argument has a syntax error")
if optlist.artist and len(args) != 0:
parser.error("Supply EITHER -a oder url")
if optlist.artist:
args.append("lastfm://artist/%s/similarartists" % \
encodeUrl(optlist.artist))
if not optlist.username:
parser.error("--username is mandatory")
if len(args) != 1:
print "You must supply a last-fm url. Using personal radio as default."
args.append("lastfm://user/%s/personal" % optlist.username)
if optlist.enqueue:
hasConnectionToRhythmbox = False
try:
import dbus
session_bus = dbus.SessionBus()
proxy_obj = session_bus.get_object('org.gnome.Rhythmbox', '/org/gnome/Rhythmbox/Shell')
rhythmboxShell = dbus.Interface(proxy_obj, 'org.gnome.Rhythmbox.Shell')
hasConnectionToRhythmbox = True
except:
pass
if (not optlist.password) and hasGnomeKeyring:
gconf_key = "/apps/%s/%s/keyring_auth_token" % (APP_NAME, optlist.username )
keyring = gnomekeyring.get_default_keyring_sync()
auth_token = gconf.client_get_default().get_int(gconf_key)
if auth_token > 0:
item = gnomekeyring.item_get_info_sync(keyring,auth_token)
optlist.password = item.get_secret()
if not optlist.password:
# ask the user for a password
password = getpass.getpass()
if hasGnomeKeyring:
# store password in GNOME's keyring
gconf_key = "/apps/%s/%s/keyring_auth_token" % (APP_NAME, optlist.username )
keyring = gnomekeyring.get_default_keyring_sync()
auth_token = gnomekeyring.item_create_sync(
keyring,
gnomekeyring.ITEM_GENERIC_SECRET,
"%s@last.fm" % optlist.username,
dict(appname=APP_NAME,username=optlist.username),
str(password), True)
gconf.client_get_default().set_int(gconf_key, auth_token)
passwordmd5 = hashlib.md5(password).hexdigest()
else:
passwordmd5 = hashlib.md5(optlist.password).hexdigest()
if optlist.directory:
try:
os.chdir(optlist.directory)
except:
print "Failed to chdir into ", optlist.directory
sys.exit(1)
try:
handshakeUrl = (
"http://ws.audioscrobbler.com/radio/handshake.php?version=1.4." +
"2.58240&platform=linux&platformversion=Unix%2FLinux&username=" + optlist.username +
"&passwordmd5=" + passwordmd5 +
"&language=en" )
handshakeDataRaw = urllib.urlopen(handshakeUrl).read()
handshakeData = dict(filter(lambda x: len(x) == 2, ( x.split("=", 1) for x in handshakeDataRaw.split() )))
except:
print "Handshake failed."
try:
if handshakeDataRaw:
print "Information:"
print handshakeDataRaw
except: pass
sys.exit(1)
if handshakeData["session"] == "FAILED":
print "Login failed."
sys.exit(1)
adjustUrl = (
"http://ws.audioscrobbler.com/radio/adjust.php?session=" + handshakeData["session"] +
"&url=" + args[0] )
adjustDataRaw = urllib.urlopen(adjustUrl).read()
adjustData = dict(filter(lambda x: len(x) == 2, ( x.split("=", 1) for x in adjustDataRaw.split() )))
if "response" not in adjustData or adjustData["response"] != "OK":
print "Failed to tune station"
sys.exit(1)
def getTracks():
trackUrl = "http://ws.audioscrobbler.com/radio/xspf.php?sk=" + handshakeData["session"] + "&discovery=0&desktop=1.4.2.58240"
xmlData = urllib.urlopen(trackUrl).read()
try:
playlist = xml.dom.minidom.parseString(xmlData)
except KeyboardInterrupt:
print "Caught interrupt. The latest file may be corrupted."
sys.exit(0)
except:
print "Failed to parse XML."
#print xmlData
sys.exit(1)
for track in playlist.getElementsByTagName("track"):
try:
attrs = [ "title", "location", "album", "creator" ]
toyield = {}
for attr in attrs:
try:
toyield[attr] = track.getElementsByTagName(attr)[0].firstChild.toxml()
except:
if attr == "title":
raise Exception("Mandatory field title missing")
toyield[attr] = ""
yield toyield
except KeyboardInterrupt:
print "Caught interrupt. The latest file may be corrupted."
sys.exit(0)
except:
print "Failed to extract data from XML node."
#print track.toxml()
sys.exit(1)
def displayDownloadStatus(blocks, blockSize, totalSize):
percent = blocks * blockSize * 100 / totalSize;
megaBytes = blocks * blockSize * 1.0 / 1024**2
print " %d%% (%.2f MB)\r" % (percent, megaBytes),
sys.stdout.flush()
if optlist.keep:
database = dbm.open("knownsongs", "c")
fetchedCount = 0
while True:
for track in getTracks():
# Check against filter / for existance
storeAs = "%s - %s.mp3" % (track["creator"], track["title"])
storeAs = storeAs.translate("/")
trackHash = hashlib.md5(storeAs).hexdigest()
if sys.getfilesystemencoding() != "UTF-8":
if hasUnidecode:
storeAs = unidecode.unidecode(storeAs)
else:
storeAs = storeAs.encode(sys.getfilesystemencoding(), "replace")
if optlist.filter and not re.search(optlist.filter, storeAs, re.I):
print "Skipping", storeAs, "(filter failed)"
try:
time.sleep(3)
except KeyboardInterrupt:
print "Caught interrupt. The latest file may be corrupted."
sys.exit(0)
continue
if optlist.keep and database.has_key(trackHash):
print "Skipping", storeAs, "(downloaded before)"
continue
if os.access(storeAs, os.F_OK):
print "Skipping", storeAs, "(exists)"
continue
# Download file
if numberOfSongs > 0:
fetchedCount += 1
if fetchedCount > numberOfSongs:
sys.exit(0)
print "Receiving", storeAs
try:
urllib.urlretrieve(track["location"], storeAs, displayDownloadStatus)
except KeyboardInterrupt:
os.unlink(storeAs)
print "Caught interrupt. The latest file may be corrupted."
sys.exit(0)
if optlist.keep:
database[trackHash] = ""
if hasEyeD3lib:
print " Writing ID3 tag \r",
tag = eyeD3.Tag()
tag.link(storeAs)
tag.header.setVersion(eyeD3.ID3_V2_3)
tag.setTextEncoding(eyeD3.frames.UTF_16_ENCODING)
tag.setArtist(track["creator"])
tag.setTitle(track["title"])
if track["album"]:
tag.setAlbum(track["album"])
tag.update()
if optlist.enqueue and hasConnectionToRhythmbox:
song = 'file://' + ( os.getcwd() + '/' + storeAs).replace(' ','%20')
print song
rhythmboxShell.loadURI(song,False)
rhythmboxShell.addToQueue(song)
Download
- Dateiname
- lastfm-rip.py
- Größe
- 10.7kb
- Copyright (c) Phillip Berndt, 2006-2013
- Letztes Update 30.10.2013