diff --git a/headphones/albumswitcher.py b/headphones/albumswitcher.py
index e4a442d8..34ab7b99 100644
--- a/headphones/albumswitcher.py
+++ b/headphones/albumswitcher.py
@@ -39,11 +39,11 @@ def switch(AlbumID, ReleaseID):
"ReleaseCountry": newalbumdata['ReleaseCountry'],
"ReleaseFormat": newalbumdata['ReleaseFormat']
}
-
+
myDB.upsert("albums", newValueDict, controlValueDict)
-
+
for track in newtrackdata:
-
+
controlValueDict = {"TrackID": track['TrackID'],
"AlbumID": AlbumID}
@@ -60,23 +60,23 @@ def switch(AlbumID, ReleaseID):
"Format": track['Format'],
"BitRate": track['BitRate']
}
-
+
myDB.upsert("tracks", newValueDict, controlValueDict)
-
+
# Mark albums as downloaded if they have at least 80% (by default, configurable) of the album
total_track_count = len(newtrackdata)
have_track_count = len(myDB.select('SELECT * from tracks WHERE AlbumID=? AND Location IS NOT NULL', [AlbumID]))
-
+
if oldalbumdata['Status'] == 'Skipped' and ((have_track_count/float(total_track_count)) >= (headphones.ALBUM_COMPLETION_PCT/100.0)):
myDB.action('UPDATE albums SET Status=? WHERE AlbumID=?', ['Downloaded', AlbumID])
-
+
# Update have track counts on index
totaltracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID=? AND AlbumID IN (SELECT AlbumID FROM albums WHERE Status != "Ignored")', [newalbumdata['ArtistID']]))
havetracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID=? AND Location IS NOT NULL', [newalbumdata['ArtistID']]))
controlValueDict = {"ArtistID": newalbumdata['ArtistID']}
-
+
newValueDict = { "TotalTracks": totaltracks,
"HaveTracks": havetracks}
-
+
myDB.upsert("artists", newValueDict, controlValueDict)
diff --git a/headphones/api.py b/headphones/api.py
index 03888a26..87f20ae0 100644
--- a/headphones/api.py
+++ b/headphones/api.py
@@ -21,29 +21,29 @@ import lib.simplejson as simplejson
from xml.dom.minidom import Document
import copy
-cmd_list = [ 'getIndex', 'getArtist', 'getAlbum', 'getUpcoming', 'getWanted', 'getSimilar', 'getHistory', 'getLogs',
+cmd_list = [ 'getIndex', 'getArtist', 'getAlbum', 'getUpcoming', 'getWanted', 'getSimilar', 'getHistory', 'getLogs',
'findArtist', 'findAlbum', 'addArtist', 'delArtist', 'pauseArtist', 'resumeArtist', 'refreshArtist',
- 'addAlbum', 'queueAlbum', 'unqueueAlbum', 'forceSearch', 'forceProcess', 'getVersion', 'checkGithub',
- 'shutdown', 'restart', 'update', 'getArtistArt', 'getAlbumArt', 'getArtistInfo', 'getAlbumInfo',
+ 'addAlbum', 'queueAlbum', 'unqueueAlbum', 'forceSearch', 'forceProcess', 'getVersion', 'checkGithub',
+ 'shutdown', 'restart', 'update', 'getArtistArt', 'getAlbumArt', 'getArtistInfo', 'getAlbumInfo',
'getArtistThumb', 'getAlbumThumb', 'choose_specific_download', 'download_specific_release']
class Api(object):
def __init__(self):
-
+
self.apikey = None
self.cmd = None
self.id = None
-
+
self.kwargs = None
self.data = None
self.callback = None
-
+
def checkParams(self,*args,**kwargs):
-
+
if not headphones.API_ENABLED:
self.data = 'API not enabled'
return
@@ -53,32 +53,32 @@ class Api(object):
if len(headphones.API_KEY) != 32:
self.data = 'API key not generated correctly'
return
-
+
if 'apikey' not in kwargs:
self.data = 'Missing api key'
return
-
+
if kwargs['apikey'] != headphones.API_KEY:
self.data = 'Incorrect API key'
return
else:
self.apikey = kwargs.pop('apikey')
-
+
if 'cmd' not in kwargs:
self.data = 'Missing parameter: cmd'
return
-
+
if kwargs['cmd'] not in cmd_list:
self.data = 'Unknown command: %s' % kwargs['cmd']
return
else:
self.cmd = kwargs.pop('cmd')
-
+
self.kwargs = kwargs
self.data = 'OK'
def fetchData(self):
-
+
if self.data == 'OK':
logger.info('Recieved API command: %s', self.cmd)
methodToCall = getattr(self, "_" + self.cmd)
@@ -95,74 +95,74 @@ class Api(object):
return self.data
else:
return self.data
-
+
def _dic_from_query(self,query):
-
+
myDB = db.DBConnection()
rows = myDB.select(query)
-
+
rows_as_dic = []
-
+
for row in rows:
row_as_dic = dict(zip(row.keys(), row))
rows_as_dic.append(row_as_dic)
-
+
return rows_as_dic
-
+
def _getIndex(self, **kwargs):
-
+
self.data = self._dic_from_query('SELECT * from artists order by ArtistSortName COLLATE NOCASE')
- return
-
+ return
+
def _getArtist(self, **kwargs):
-
+
if 'id' not in kwargs:
self.data = 'Missing parameter: id'
return
else:
self.id = kwargs['id']
-
+
artist = self._dic_from_query('SELECT * from artists WHERE ArtistID="' + self.id + '"')
albums = self._dic_from_query('SELECT * from albums WHERE ArtistID="' + self.id + '" order by ReleaseDate DESC')
description = self._dic_from_query('SELECT * from descriptions WHERE ArtistID="' + self.id + '"')
-
+
self.data = { 'artist': artist, 'albums': albums, 'description' : description }
return
-
+
def _getAlbum(self, **kwargs):
-
+
if 'id' not in kwargs:
self.data = 'Missing parameter: id'
return
else:
self.id = kwargs['id']
-
+
album = self._dic_from_query('SELECT * from albums WHERE AlbumID="' + self.id + '"')
tracks = self._dic_from_query('SELECT * from tracks WHERE AlbumID="' + self.id + '"')
description = self._dic_from_query('SELECT * from descriptions WHERE ReleaseGroupID="' + self.id + '"')
-
+
self.data = { 'album' : album, 'tracks' : tracks, 'description' : description }
return
-
+
def _getHistory(self, **kwargs):
self.data = self._dic_from_query('SELECT * from snatched order by DateAdded DESC')
return
-
+
def _getUpcoming(self, **kwargs):
self.data = self._dic_from_query("SELECT * from albums WHERE ReleaseDate > date('now') order by ReleaseDate DESC")
return
-
+
def _getWanted(self, **kwargs):
self.data = self._dic_from_query("SELECT * from albums WHERE Status='Wanted'")
return
-
+
def _getSimilar(self, **kwargs):
self.data = self._dic_from_query('SELECT * from lastfmcloud')
return
-
+
def _getLogs(self, **kwargs):
pass
-
+
def _findArtist(self, **kwargs):
if 'name' not in kwargs:
self.data = 'Missing parameter: name'
@@ -171,7 +171,7 @@ class Api(object):
limit = kwargs['limit']
else:
limit=50
-
+
self.data = mb.findArtist(kwargs['name'], limit)
def _findAlbum(self, **kwargs):
@@ -182,216 +182,216 @@ class Api(object):
limit = kwargs['limit']
else:
limit=50
-
+
self.data = mb.findRelease(kwargs['name'], limit)
-
+
def _addArtist(self, **kwargs):
if 'id' not in kwargs:
self.data = 'Missing parameter: id'
return
else:
self.id = kwargs['id']
-
+
try:
importer.addArtisttoDB(self.id)
except Exception, e:
self.data = e
-
+
return
-
+
def _delArtist(self, **kwargs):
if 'id' not in kwargs:
self.data = 'Missing parameter: id'
return
else:
self.id = kwargs['id']
-
+
myDB = db.DBConnection()
myDB.action('DELETE from artists WHERE ArtistID="' + self.id + '"')
myDB.action('DELETE from albums WHERE ArtistID="' + self.id + '"')
myDB.action('DELETE from tracks WHERE ArtistID="' + self.id + '"')
-
+
def _pauseArtist(self, **kwargs):
if 'id' not in kwargs:
self.data = 'Missing parameter: id'
return
else:
self.id = kwargs['id']
-
+
myDB = db.DBConnection()
controlValueDict = {'ArtistID': self.id}
newValueDict = {'Status': 'Paused'}
myDB.upsert("artists", newValueDict, controlValueDict)
-
+
def _resumeArtist(self, **kwargs):
if 'id' not in kwargs:
self.data = 'Missing parameter: id'
return
else:
self.id = kwargs['id']
-
+
myDB = db.DBConnection()
controlValueDict = {'ArtistID': self.id}
newValueDict = {'Status': 'Active'}
myDB.upsert("artists", newValueDict, controlValueDict)
-
+
def _refreshArtist(self, **kwargs):
if 'id' not in kwargs:
self.data = 'Missing parameter: id'
return
else:
self.id = kwargs['id']
-
+
try:
importer.addArtisttoDB(self.id)
except Exception, e:
self.data = e
-
+
return
-
+
def _addAlbum(self, **kwargs):
if 'id' not in kwargs:
self.data = 'Missing parameter: id'
return
else:
self.id = kwargs['id']
-
+
try:
importer.addReleaseById(self.id)
except Exception, e:
self.data = e
-
+
return
-
+
def _queueAlbum(self, **kwargs):
-
+
if 'id' not in kwargs:
self.data = 'Missing parameter: id'
return
else:
self.id = kwargs['id']
-
+
if 'new' in kwargs:
new = kwargs['new']
else:
new = False
-
+
if 'lossless' in kwargs:
lossless = kwargs['lossless']
else:
lossless = False
-
+
myDB = db.DBConnection()
controlValueDict = {'AlbumID': self.id}
if lossless:
newValueDict = {'Status': 'Wanted Lossless'}
- else:
+ else:
newValueDict = {'Status': 'Wanted'}
myDB.upsert("albums", newValueDict, controlValueDict)
- searcher.searchforalbum(self.id, new)
-
+ searcher.searchforalbum(self.id, new)
+
def _unqueueAlbum(self, **kwargs):
-
+
if 'id' not in kwargs:
self.data = 'Missing parameter: id'
return
else:
self.id = kwargs['id']
-
+
myDB = db.DBConnection()
controlValueDict = {'AlbumID': self.id}
newValueDict = {'Status': 'Skipped'}
myDB.upsert("albums", newValueDict, controlValueDict)
-
+
def _forceSearch(self, **kwargs):
searcher.searchforalbum()
-
+
def _forceProcess(self, **kwargs):
self.dir = None
if 'dir' in kwargs:
self.dir = kwargs['dir']
postprocessor.forcePostProcess(self.dir)
-
+
def _getVersion(self, **kwargs):
- self.data = {
+ self.data = {
'git_path' : headphones.GIT_PATH,
'install_type' : headphones.INSTALL_TYPE,
'current_version' : headphones.CURRENT_VERSION,
'latest_version' : headphones.LATEST_VERSION,
'commits_behind' : headphones.COMMITS_BEHIND,
}
-
+
def _checkGithub(self, **kwargs):
versioncheck.checkGithub()
self._getVersion()
-
+
def _shutdown(self, **kwargs):
headphones.SIGNAL = 'shutdown'
-
+
def _restart(self, **kwargs):
headphones.SIGNAL = 'restart'
-
+
def _update(self, **kwargs):
headphones.SIGNAL = 'update'
-
+
def _getArtistArt(self, **kwargs):
-
+
if 'id' not in kwargs:
self.data = 'Missing parameter: id'
return
else:
self.id = kwargs['id']
-
+
self.data = cache.getArtwork(ArtistID=self.id)
def _getAlbumArt(self, **kwargs):
-
+
if 'id' not in kwargs:
self.data = 'Missing parameter: id'
return
else:
self.id = kwargs['id']
-
+
self.data = cache.getArtwork(AlbumID=self.id)
-
+
def _getArtistInfo(self, **kwargs):
-
+
if 'id' not in kwargs:
self.data = 'Missing parameter: id'
return
else:
self.id = kwargs['id']
-
+
self.data = cache.getInfo(ArtistID=self.id)
-
+
def _getAlbumInfo(self, **kwargs):
-
+
if 'id' not in kwargs:
self.data = 'Missing parameter: id'
return
else:
self.id = kwargs['id']
-
+
self.data = cache.getInfo(AlbumID=self.id)
-
+
def _getArtistThumb(self, **kwargs):
-
+
if 'id' not in kwargs:
self.data = 'Missing parameter: id'
return
else:
self.id = kwargs['id']
-
+
self.data = cache.getThumb(ArtistID=self.id)
def _getAlbumThumb(self, **kwargs):
-
+
if 'id' not in kwargs:
self.data = 'Missing parameter: id'
return
else:
self.id = kwargs['id']
-
+
self.data = cache.getThumb(AlbumID=self.id)
def _choose_specific_download(self, **kwargs):
@@ -403,9 +403,9 @@ class Api(object):
self.id = kwargs['id']
results = searcher.searchforalbum(self.id, choose_specific_download=True)
-
+
results_as_dicts = []
-
+
for result in results:
result_dict = {
diff --git a/headphones/db.py b/headphones/db.py
index 7068ba0e..576d2533 100644
--- a/headphones/db.py
+++ b/headphones/db.py
@@ -31,7 +31,7 @@ from headphones import logger
def dbFilename(filename="headphones.db"):
return os.path.join(headphones.DATA_DIR, filename)
-
+
def getCacheSize():
#this will protect against typecasting problems produced by empty string and None settings
if not headphones.CACHE_SIZEMB:
@@ -42,25 +42,25 @@ def getCacheSize():
class DBConnection:
def __init__(self, filename="headphones.db"):
-
+
self.filename = filename
self.connection = sqlite3.connect(dbFilename(filename), timeout=20)
#don't wait for the disk to finish writing
self.connection.execute("PRAGMA synchronous = OFF")
#journal disabled since we never do rollbacks
- self.connection.execute("PRAGMA journal_mode = %s" % headphones.JOURNAL_MODE)
+ self.connection.execute("PRAGMA journal_mode = %s" % headphones.JOURNAL_MODE)
#64mb of cache memory,probably need to make it user configurable
self.connection.execute("PRAGMA cache_size=-%s" % (getCacheSize()*1024))
self.connection.row_factory = sqlite3.Row
-
+
def action(self, query, args=None):
if query == None:
return
-
+
sqlResult = None
attempt = 0
-
+
while attempt < 5:
try:
if args == None:
@@ -82,28 +82,28 @@ class DBConnection:
except sqlite3.DatabaseError, e:
logger.error('Fatal Error executing %s :: %s', query, e)
raise
-
+
return sqlResult
-
+
def select(self, query, args=None):
-
+
sqlResults = self.action(query, args).fetchall()
-
+
if sqlResults == None:
return []
-
+
return sqlResults
-
+
def upsert(self, tableName, valueDict, keyDict):
-
+
changesBefore = self.connection.total_changes
-
+
genParams = lambda myDict : [x + " = ?" for x in myDict.keys()]
-
+
query = "UPDATE "+tableName+" SET " + ", ".join(genParams(valueDict)) + " WHERE " + " AND ".join(genParams(keyDict))
-
+
self.action(query, valueDict.values() + keyDict.values())
-
+
if self.connection.total_changes == changesBefore:
query = "INSERT INTO "+tableName+" (" + ", ".join(valueDict.keys() + keyDict.keys()) + ")" + \
" VALUES (" + ", ".join(["?"] * len(valueDict.keys() + keyDict.keys())) + ")"
diff --git a/headphones/getXldProfile.py b/headphones/getXldProfile.py
index 6c744a87..e083d1b6 100755
--- a/headphones/getXldProfile.py
+++ b/headphones/getXldProfile.py
@@ -8,7 +8,7 @@ from headphones import logger
def getXldProfile(xldProfile):
xldProfileNotFound = xldProfile
expandedPath = os.path.expanduser('~/Library/Preferences/jp.tmkk.XLD.plist')
- try:
+ try:
preferences = plistlib.Plist.fromFile(expandedPath)
except (expat.ExpatError):
os.system("/usr/bin/plutil -convert xml1 %s" % expandedPath )
@@ -61,7 +61,7 @@ def getXldProfile(xldProfile):
elif 'TVBR' in ShortDesc:
XLDAacOutput2_VBRQuality = int(profile.get('XLDAacOutput2_VBRQuality'))
if XLDAacOutput2_VBRQuality > 122:
- xldBitrate = 320
+ xldBitrate = 320
elif XLDAacOutput2_VBRQuality > 113 and XLDAacOutput2_VBRQuality <= 122:
xldBitrate = 285
elif XLDAacOutput2_VBRQuality > 104 and XLDAacOutput2_VBRQuality <= 113:
diff --git a/headphones/importer.py b/headphones/importer.py
index 36eecaf5..a94bf35c 100644
--- a/headphones/importer.py
+++ b/headphones/importer.py
@@ -226,27 +226,27 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False):
skip_log = 0
#Make a user configurable variable to skip update of albums with release dates older than this date (in days)
pause_delta = headphones.MB_IGNORE_AGE
-
+
rg_exists = myDB.action("SELECT * from albums WHERE AlbumID=?", [rg['id']]).fetchone()
if not forcefull:
-
+
new_release_group = False
-
+
try:
check_release_date = rg_exists['ReleaseDate']
except TypeError:
check_release_date = None
new_release_group = True
-
-
+
+
if new_release_group:
-
+
logger.info("[%s] Now adding: %s (New Release Group)" % (artist['artist_name'], rg['title']))
new_releases = mb.get_new_releases(rgid,includeExtras)
-
+
else:
-
+
if check_release_date is None or check_release_date == u"None":
logger.info("[%s] Now updating: %s (No Release Date)" % (artist['artist_name'], rg['title']))
new_releases = mb.get_new_releases(rgid,includeExtras,True)
@@ -384,7 +384,7 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False):
# If there's no release in the main albums tables, add the default (hybrid)
# If there is a release, check the ReleaseID against the AlbumID to see if they differ (user updated)
# check if the album already exists
-
+
if not rg_exists:
releaseid = rg['id']
else:
@@ -410,7 +410,7 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False):
if rg_exists:
newValueDict['DateAdded'] = rg_exists['DateAdded']
newValueDict['Status'] = rg_exists['Status']
-
+
else:
today = helpers.today()
diff --git a/headphones/librarysync.py b/headphones/librarysync.py
index 6fe13e17..416023fc 100644
--- a/headphones/librarysync.py
+++ b/headphones/librarysync.py
@@ -27,18 +27,18 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal
if cron and not headphones.LIBRARYSCAN:
return
-
+
if not dir:
if not headphones.MUSIC_DIR:
return
else:
dir = headphones.MUSIC_DIR
-
+
# If we're appending a dir, it's coming from the post processor which is
# already bytestring
if not append:
dir = dir.encode(headphones.SYS_ENCODING)
-
+
if not os.path.isdir(dir):
logger.warn('Cannot find directory: %s. Not scanning' % dir.decode(headphones.SYS_ENCODING, 'replace'))
return
@@ -47,7 +47,7 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal
new_artists = []
logger.info('Scanning music directory: %s' % dir.decode(headphones.SYS_ENCODING, 'replace'))
-
+
if not append:
# Clean up bad filepaths
tracks = myDB.select('SELECT Location, TrackID from alltracks WHERE Location IS NOT NULL')
@@ -57,7 +57,7 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal
if not os.path.isfile(encoded_track_string):
myDB.action('UPDATE tracks SET Location=?, BitRate=?, Format=? WHERE Location=?', [None, None, None, track['Location']])
myDB.action('UPDATE alltracks SET Location=?, BitRate=?, Format=? WHERE Location=?', [None, None, None, track['Location']])
-
+
del_have_tracks = myDB.select('SELECT Location, Matched, ArtistName from have')
for track in del_have_tracks:
@@ -71,13 +71,13 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal
###############myDB.action('DELETE from have')
bitrates = []
-
+
song_list = []
new_song_count = 0
file_count = 0
latest_subdirectory = []
-
+
for r,d,f in os.walk(dir):
#need to abuse slicing to get a copy of the list, doing it directly will skip the element after a deleted one
#using a list comprehension will not work correctly for nested subdirectories (os.walk keeps its original list)
@@ -108,11 +108,11 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal
except:
logger.error('Cannot read file: ' + unicode_song_path)
continue
-
+
# Grab the bitrates for the auto detect bit rate option
if f.bitrate:
bitrates.append(f.bitrate)
-
+
# Use the album artist over the artist if available
if f.albumartist:
f_artist = f.albumartist
@@ -120,8 +120,8 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal
f_artist = f.artist
else:
f_artist = None
-
- # Add the song to our song list -
+
+ # Add the song to our song list -
# TODO: skip adding songs without the minimum requisite information (just a matter of putting together the right if statements)
if f_artist and f.album and f.title:
@@ -144,7 +144,7 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal
'Format' : f.format,
'CleanName' : CleanName
}
-
+
#song_list.append(song_dict)
check_exist_song = myDB.action("SELECT * FROM have WHERE Location=?", [unicode_song_path]).fetchone()
#Only attempt to match songs that are new, haven't yet been matched, or metadata has changed.
@@ -182,17 +182,17 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal
song_list = myDB.action("SELECT * FROM have WHERE Matched IS NULL AND LOCATION LIKE ?", [dir.decode(headphones.SYS_ENCODING, 'replace')+"%"])
total_number_of_songs = myDB.action("SELECT COUNT(*) FROM have WHERE Matched IS NULL AND LOCATION LIKE ?", [dir.decode(headphones.SYS_ENCODING, 'replace')+"%"]).fetchone()[0]
logger.info("Found " + str(total_number_of_songs) + " new/modified tracks in: '" + dir.decode(headphones.SYS_ENCODING, 'replace') + "'. Matching tracks to the appropriate releases....")
-
+
# Sort the song_list by most vague (e.g. no trackid or releaseid) to most specific (both trackid & releaseid)
# When we insert into the database, the tracks with the most specific information will overwrite the more general matches
-
+
##############song_list = helpers.multikeysort(song_list, ['ReleaseID', 'TrackID'])
song_list = helpers.multikeysort(song_list, ['ArtistName', 'AlbumTitle'])
-
+
# We'll use this to give a % completion, just because the track matching might take a while
song_count = 0
latest_artist = []
-
+
for song in song_list:
latest_artist.append(song['ArtistName'])
@@ -200,26 +200,26 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal
logger.info("Now matching songs by %s" % song['ArtistName'])
elif latest_artist[song_count] != latest_artist[song_count-1] and song_count !=0:
logger.info("Now matching songs by %s" % song['ArtistName'])
-
+
#print song['ArtistName']+' - '+song['AlbumTitle']+' - '+song['TrackTitle']
song_count += 1
completion_percentage = float(song_count)/total_number_of_songs * 100
-
+
if completion_percentage%10 == 0:
logger.info("Track matching is " + str(completion_percentage) + "% complete")
-
+
#THE "MORE-SPECIFIC" CLAUSES HERE HAVE ALL BEEN REMOVED. WHEN RUNNING A LIBRARY SCAN, THE ONLY CLAUSES THAT
#EVER GOT HIT WERE [ARTIST/ALBUM/TRACK] OR CLEANNAME. ARTISTID & RELEASEID ARE NEVER PASSED TO THIS FUNCTION,
#ARE NEVER FOUND, AND THE OTHER CLAUSES WERE NEVER HIT. FURTHERMORE, OTHER MATCHING FUNCTIONS IN THIS PROGRAM
#(IMPORTER.PY, MB.PY) SIMPLY DO A [ARTIST/ALBUM/TRACK] OR CLEANNAME MATCH, SO IT'S ALL CONSISTENT.
if song['ArtistName'] and song['AlbumTitle'] and song['TrackTitle']:
-
+
track = myDB.action('SELECT ArtistName, AlbumTitle, TrackTitle, AlbumID from tracks WHERE ArtistName LIKE ? AND AlbumTitle LIKE ? AND TrackTitle LIKE ?', [song['ArtistName'], song['AlbumTitle'], song['TrackTitle']]).fetchone()
if track:
controlValueDict = { 'ArtistName' : track['ArtistName'],
'AlbumTitle' : track['AlbumTitle'],
- 'TrackTitle' : track['TrackTitle'] }
+ 'TrackTitle' : track['TrackTitle'] }
newValueDict = { 'Location' : song['Location'],
'BitRate' : song['BitRate'],
'Format' : song['Format'] }
@@ -231,10 +231,10 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal
else:
track = myDB.action('SELECT CleanName, AlbumID from tracks WHERE CleanName LIKE ?', [song['CleanName']]).fetchone()
if track:
- controlValueDict = { 'CleanName' : track['CleanName']}
+ controlValueDict = { 'CleanName' : track['CleanName']}
newValueDict = { 'Location' : song['Location'],
'BitRate' : song['BitRate'],
- 'Format' : song['Format'] }
+ 'Format' : song['Format'] }
myDB.upsert("tracks", newValueDict, controlValueDict)
controlValueDict2 = { 'Location' : song['Location']}
@@ -244,9 +244,9 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal
controlValueDict2 = { 'Location' : song['Location']}
newValueDict2 = { 'Matched' : "Failed"}
myDB.upsert("have", newValueDict2, controlValueDict2)
-
- alltrack = myDB.action('SELECT ArtistName, AlbumTitle, TrackTitle, AlbumID from alltracks WHERE ArtistName LIKE ? AND AlbumTitle LIKE ? AND TrackTitle LIKE ?', [song['ArtistName'], song['AlbumTitle'], song['TrackTitle']]).fetchone()
+
+ alltrack = myDB.action('SELECT ArtistName, AlbumTitle, TrackTitle, AlbumID from alltracks WHERE ArtistName LIKE ? AND AlbumTitle LIKE ? AND TrackTitle LIKE ?', [song['ArtistName'], song['AlbumTitle'], song['TrackTitle']]).fetchone()
if alltrack:
controlValueDict = { 'ArtistName' : alltrack['ArtistName'],
'AlbumTitle' : alltrack['AlbumTitle'],
@@ -262,10 +262,10 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal
else:
alltrack = myDB.action('SELECT CleanName, AlbumID from alltracks WHERE CleanName LIKE ?', [song['CleanName']]).fetchone()
if alltrack:
- controlValueDict = { 'CleanName' : alltrack['CleanName']}
+ controlValueDict = { 'CleanName' : alltrack['CleanName']}
newValueDict = { 'Location' : song['Location'],
'BitRate' : song['BitRate'],
- 'Format' : song['Format'] }
+ 'Format' : song['Format'] }
myDB.upsert("alltracks", newValueDict, controlValueDict)
controlValueDict2 = { 'Location' : song['Location']}
@@ -279,35 +279,35 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal
controlValueDict2 = { 'Location' : song['Location']}
newValueDict2 = { 'Matched' : "Failed"}
myDB.upsert("have", newValueDict2, controlValueDict2)
-
+
#######myDB.action('INSERT INTO have (ArtistName, AlbumTitle, TrackNumber, TrackTitle, TrackLength, BitRate, Genre, Date, TrackID, Location, CleanName, Format) VALUES( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [song['ArtistName'], song['AlbumTitle'], song['TrackNumber'], song['TrackTitle'], song['TrackLength'], song['BitRate'], song['Genre'], song['Date'], song['TrackID'], song['Location'], CleanName, song['Format']])
logger.info('Completed matching tracks from directory: %s' % dir.decode(headphones.SYS_ENCODING, 'replace'))
-
+
if not append:
logger.info('Updating scanned artist track counts')
-
+
# Clean up the new artist list
unique_artists = {}.fromkeys(new_artists).keys()
current_artists = myDB.select('SELECT ArtistName, ArtistID from artists')
- #There was a bug where artists with special characters (-,') would show up in new artists.
+ #There was a bug where artists with special characters (-,') would show up in new artists.
artist_list = [f for f in unique_artists if helpers.cleanName(f).lower() not in [helpers.cleanName(x[0]).lower() for x in current_artists]]
artists_checked = [f for f in unique_artists if helpers.cleanName(f).lower() in [helpers.cleanName(x[0]).lower() for x in current_artists]]
# Update track counts
-
+
for artist in artists_checked:
# Have tracks are selected from tracks table and not all tracks because of duplicates
# We update the track count upon an album switch to compliment this
havetracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistName like ? AND Location IS NOT NULL', [artist])) + len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ? AND Matched = "Failed"', [artist]))
- #Note, some people complain about having "artist have tracks" > # of tracks total in artist official releases
+ #Note, some people complain about having "artist have tracks" > # of tracks total in artist official releases
# (can fix by getting rid of second len statement)
myDB.action('UPDATE artists SET HaveTracks=? WHERE ArtistName=?', [havetracks, artist])
-
+
logger.info('Found %i new artists' % len(artist_list))
-
+
if len(artist_list):
if headphones.ADD_ARTISTS:
logger.info('Importing %i new artists' % len(artist_list))
@@ -317,14 +317,14 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal
#myDB.action('DELETE from newartists')
for artist in artist_list:
myDB.action('INSERT OR IGNORE INTO newartists VALUES (?)', [artist])
-
+
if headphones.DETECT_BITRATE:
headphones.PREFERRED_BITRATE = sum(bitrates)/len(bitrates)/1000
-
+
else:
# If we're appending a new album to the database, update the artists total track counts
logger.info('Updating artist track counts')
-
+
havetracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID=? AND Location IS NOT NULL', [ArtistID])) + len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ? AND Matched = "Failed"', [ArtistName]))
myDB.action('UPDATE artists SET HaveTracks=? WHERE ArtistID=?', [havetracks, ArtistID])
@@ -357,7 +357,7 @@ def update_album_status(AlbumID=None):
if album_completion >= headphones.ALBUM_COMPLETION_PCT and album['Status'] == 'Skipped':
new_album_status = "Downloaded"
-
+
# I don't think we want to change Downloaded->Skipped.....
# I think we can only automatically change Skipped->Downloaded when updating
# There was a bug report where this was causing infinite downloads if the album was
@@ -369,7 +369,7 @@ def update_album_status(AlbumID=None):
# new_album_status = album['Status']
else:
new_album_status = album['Status']
-
+
myDB.upsert("albums", {'Status' : new_album_status}, {'AlbumID' : album['AlbumID']})
if new_album_status != album['Status']:
logger.info('Album %s changed to %s' % (album['AlbumTitle'], new_album_status))
diff --git a/headphones/lyrics.py b/headphones/lyrics.py
index aa3c952a..ea8458ee 100644
--- a/headphones/lyrics.py
+++ b/headphones/lyrics.py
@@ -27,26 +27,26 @@ def getLyrics(artist, song):
url = 'http://lyrics.wikia.com/api.php'
data = request.request_minidom(url, params=params)
-
+
if not data:
return
-
+
url = data.getElementsByTagName("url")
-
+
if url:
lyricsurl = url[0].firstChild.nodeValue
else:
logger.info('No lyrics found for %s - %s' % (artist, song))
return
-
+
lyricspage = request.request_content(lyricsurl)
-
+
if not lyricspage:
logger.warn('Error fetching lyrics from: %s' % lyricsurl)
return
m = re.compile('''
.*?
(.*?)
# Reconstruct the original comment.
self.pieces.append('' % locals())
-
+
def handle_pi(self, text):
# called for each processing instruction, e.g.
# Reconstruct original processing instruction.
@@ -1942,7 +1942,7 @@ class _BaseHTMLProcessor(sgmllib.SGMLParser):
# "http://www.w3.org/TR/html4/loose.dtd">
# Reconstruct original DOCTYPE
self.pieces.append('' % locals())
-
+
_new_declname_match = re.compile(r'[a-zA-Z][-_.a-zA-Z0-9:]*\s*').match
def _scan_name(self, i, declstartpos):
rawdata = self.rawdata
@@ -2006,7 +2006,7 @@ class _LooseFeedParser(_FeedParserMixin, _BaseHTMLProcessor):
data = data.replace('"', '"')
data = data.replace(''', "'")
return data
-
+
def strattrs(self, attrs):
return ''.join([' %s="%s"' % (n,v.replace('"','"')) for n,v in attrs])
@@ -2030,12 +2030,12 @@ class _MicroformatsParser:
self.enclosures = []
self.xfn = []
self.vcard = None
-
+
def vcardEscape(self, s):
if type(s) in (type(''), type(u'')):
s = s.replace(',', '\\,').replace(';', '\\;').replace('\n', '\\n')
return s
-
+
def vcardFold(self, s):
s = re.sub(';+$', '', s)
sFolded = ''
@@ -2051,14 +2051,14 @@ class _MicroformatsParser:
def normalize(self, s):
return re.sub(r'\s+', ' ', s).strip()
-
+
def unique(self, aList):
results = []
for element in aList:
if element not in results:
results.append(element)
return results
-
+
def toISO8601(self, dt):
return time.strftime('%Y-%m-%dT%H:%M:%SZ', dt)
@@ -2148,21 +2148,21 @@ class _MicroformatsParser:
def findVCards(self, elmRoot, bAgentParsing=0):
sVCards = ''
-
+
if not bAgentParsing:
arCards = self.getPropertyValue(elmRoot, 'vcard', bAllowMultiple=1)
else:
arCards = [elmRoot]
-
+
for elmCard in arCards:
arLines = []
-
+
def processSingleString(sProperty):
sValue = self.getPropertyValue(elmCard, sProperty, self.STRING, bAutoEscape=1).decode(self.encoding)
if sValue:
arLines.append(self.vcardFold(sProperty.upper() + ':' + sValue))
return sValue or u''
-
+
def processSingleURI(sProperty):
sValue = self.getPropertyValue(elmCard, sProperty, self.URI)
if sValue:
@@ -2185,7 +2185,7 @@ class _MicroformatsParser:
if sContentType:
sContentType = ';TYPE=' + sContentType.upper()
arLines.append(self.vcardFold(sProperty.upper() + sEncoding + sContentType + sValueKey + ':' + sValue))
-
+
def processTypeValue(sProperty, arDefaultType, arForceType=None):
arResults = self.getPropertyValue(elmCard, sProperty, bAllowMultiple=1)
for elmResult in arResults:
@@ -2197,7 +2197,7 @@ class _MicroformatsParser:
sValue = self.getPropertyValue(elmResult, 'value', self.EMAIL, 0)
if sValue:
arLines.append(self.vcardFold(sProperty.upper() + ';TYPE=' + ','.join(arType) + ':' + sValue))
-
+
# AGENT
# must do this before all other properties because it is destructive
# (removes nested class="vcard" nodes so they don't interfere with
@@ -2216,10 +2216,10 @@ class _MicroformatsParser:
sAgentValue = self.getPropertyValue(elmAgent, 'value', self.URI, bAutoEscape=1);
if sAgentValue:
arLines.append(self.vcardFold('AGENT;VALUE=uri:' + sAgentValue))
-
+
# FN (full name)
sFN = processSingleString('fn')
-
+
# N (name)
elmName = self.getPropertyValue(elmCard, 'n')
if elmName:
@@ -2228,7 +2228,7 @@ class _MicroformatsParser:
arAdditionalNames = self.getPropertyValue(elmName, 'additional-name', self.STRING, 1, 1) + self.getPropertyValue(elmName, 'additional-names', self.STRING, 1, 1)
arHonorificPrefixes = self.getPropertyValue(elmName, 'honorific-prefix', self.STRING, 1, 1) + self.getPropertyValue(elmName, 'honorific-prefixes', self.STRING, 1, 1)
arHonorificSuffixes = self.getPropertyValue(elmName, 'honorific-suffix', self.STRING, 1, 1) + self.getPropertyValue(elmName, 'honorific-suffixes', self.STRING, 1, 1)
- arLines.append(self.vcardFold('N:' + sFamilyName + ';' +
+ arLines.append(self.vcardFold('N:' + sFamilyName + ';' +
sGivenName + ';' +
','.join(arAdditionalNames) + ';' +
','.join(arHonorificPrefixes) + ';' +
@@ -2245,25 +2245,25 @@ class _MicroformatsParser:
arLines.append(self.vcardFold('N:' + arNames[0] + ';' + arNames[1]))
else:
arLines.append(self.vcardFold('N:' + arNames[1] + ';' + arNames[0]))
-
+
# SORT-STRING
sSortString = self.getPropertyValue(elmCard, 'sort-string', self.STRING, bAutoEscape=1)
if sSortString:
arLines.append(self.vcardFold('SORT-STRING:' + sSortString))
-
+
# NICKNAME
arNickname = self.getPropertyValue(elmCard, 'nickname', self.STRING, 1, 1)
if arNickname:
arLines.append(self.vcardFold('NICKNAME:' + ','.join(arNickname)))
-
+
# PHOTO
processSingleURI('photo')
-
+
# BDAY
dtBday = self.getPropertyValue(elmCard, 'bday', self.DATE)
if dtBday:
arLines.append(self.vcardFold('BDAY:' + self.toISO8601(dtBday)))
-
+
# ADR (address)
arAdr = self.getPropertyValue(elmCard, 'adr', bAllowMultiple=1)
for elmAdr in arAdr:
@@ -2285,38 +2285,38 @@ class _MicroformatsParser:
sRegion + ';' +
sPostalCode + ';' +
sCountryName))
-
+
# LABEL
processTypeValue('label', ['intl','postal','parcel','work'])
-
+
# TEL (phone number)
processTypeValue('tel', ['voice'])
-
+
# EMAIL
processTypeValue('email', ['internet'], ['internet'])
-
+
# MAILER
processSingleString('mailer')
-
+
# TZ (timezone)
processSingleString('tz')
-
+
# GEO (geographical information)
elmGeo = self.getPropertyValue(elmCard, 'geo')
if elmGeo:
sLatitude = self.getPropertyValue(elmGeo, 'latitude', self.STRING, 0, 1)
sLongitude = self.getPropertyValue(elmGeo, 'longitude', self.STRING, 0, 1)
arLines.append(self.vcardFold('GEO:' + sLatitude + ';' + sLongitude))
-
+
# TITLE
processSingleString('title')
-
+
# ROLE
processSingleString('role')
# LOGO
processSingleURI('logo')
-
+
# ORG (organization)
elmOrg = self.getPropertyValue(elmCard, 'org')
if elmOrg:
@@ -2330,39 +2330,39 @@ class _MicroformatsParser:
else:
arOrganizationUnit = self.getPropertyValue(elmOrg, 'organization-unit', self.STRING, 1, 1)
arLines.append(self.vcardFold('ORG:' + sOrganizationName + ';' + ';'.join(arOrganizationUnit)))
-
+
# CATEGORY
arCategory = self.getPropertyValue(elmCard, 'category', self.STRING, 1, 1) + self.getPropertyValue(elmCard, 'categories', self.STRING, 1, 1)
if arCategory:
arLines.append(self.vcardFold('CATEGORIES:' + ','.join(arCategory)))
-
+
# NOTE
processSingleString('note')
-
+
# REV
processSingleString('rev')
-
+
# SOUND
processSingleURI('sound')
-
+
# UID
processSingleString('uid')
-
+
# URL
processSingleURI('url')
-
+
# CLASS
processSingleString('class')
-
+
# KEY
processSingleURI('key')
-
+
if arLines:
arLines = [u'BEGIN:vCard',u'VERSION:3.0'] + arLines + [u'END:vCard']
sVCards += u'\n'.join(arLines) + u'\n'
-
+
return sVCards.strip()
-
+
def isProbablyDownloadable(self, elm):
attrsD = elm.attrMap
if not attrsD.has_key('href'): return 0
@@ -2461,7 +2461,7 @@ class _RelativeURIResolver(_BaseHTMLProcessor):
def resolveURI(self, uri):
return _makeSafeAbsoluteURI(_urljoin(self.baseuri, uri.strip()))
-
+
def unknown_starttag(self, tag, attrs):
if _debug:
sys.stderr.write('tag: [%s] with attributes: [%s]\n' % (tag, str(attrs)))
@@ -2575,7 +2575,7 @@ class _HTMLSanitizer(_BaseHTMLProcessor):
# svgtiny - foreignObject + linearGradient + radialGradient + stop
svg_elements = ['a', 'animate', 'animateColor', 'animateMotion',
'animateTransform', 'circle', 'defs', 'desc', 'ellipse', 'foreignObject',
- 'font-face', 'font-face-name', 'font-face-src', 'g', 'glyph', 'hkern',
+ 'font-face', 'font-face-name', 'font-face-src', 'g', 'glyph', 'hkern',
'linearGradient', 'line', 'marker', 'metadata', 'missing-glyph', 'mpath',
'path', 'polygon', 'polyline', 'radialGradient', 'rect', 'set', 'stop',
'svg', 'switch', 'text', 'title', 'tspan', 'use']
@@ -2621,7 +2621,7 @@ class _HTMLSanitizer(_BaseHTMLProcessor):
self.unacceptablestack = 0
self.mathmlOK = 0
self.svgOK = 0
-
+
def unknown_starttag(self, tag, attrs):
acceptable_attributes = self.acceptable_attributes
keymap = {}
@@ -2683,7 +2683,7 @@ class _HTMLSanitizer(_BaseHTMLProcessor):
clean_value = self.sanitize_style(value)
if clean_value: clean_attrs.append((key,clean_value))
_BaseHTMLProcessor.unknown_starttag(self, tag, clean_attrs)
-
+
def unknown_endtag(self, tag):
if not tag in self.acceptable_elements:
if tag in self.unacceptable_elements_with_end_tag:
@@ -2815,7 +2815,7 @@ class _FeedURLHandler(urllib2.HTTPDigestAuthHandler, urllib2.HTTPRedirectHandler
http_error_300 = http_error_302
http_error_303 = http_error_302
http_error_307 = http_error_302
-
+
def http_error_401(self, req, fp, code, msg, headers):
# Check if
# - server requires digest auth, AND
@@ -2914,7 +2914,7 @@ def _open_resource(url_file_stream_or_string, etag, modified, agent, referrer, h
return opener.open(request, timeout=15)
finally:
opener.close() # JohnD
-
+
# try to open with native open function (if url_file_stream_or_string is a filename)
try:
return open(url_file_stream_or_string, 'rb')
@@ -2966,7 +2966,7 @@ _date_handlers = []
def registerDateHandler(func):
'''Register a date handler function (takes string, returns 9-tuple date in GMT)'''
_date_handlers.insert(0, func)
-
+
# ISO-8601 date parsing routines written by Fazal Majid.
# The ISO 8601 standard is very convoluted and irregular - a full ISO 8601
# parser is beyond the scope of feedparser and would be a worthwhile addition
@@ -2977,7 +2977,7 @@ def registerDateHandler(func):
# Please note the order in templates is significant because we need a
# greedy match.
_iso8601_tmpl = ['YYYY-?MM-?DD', 'YYYY-0MM?-?DD', 'YYYY-MM', 'YYYY-?OOO',
- 'YY-?MM-?DD', 'YY-?OOO', 'YYYY',
+ 'YY-?MM-?DD', 'YY-?OOO', 'YYYY',
'-YY-?MM', '-OOO', '-YY',
'--MM-?DD', '--MM',
'---DD',
@@ -3079,7 +3079,7 @@ def _parse_date_iso8601(dateString):
# Many implementations have bugs, but we'll pretend they don't.
return time.localtime(time.mktime(tuple(tm)))
registerDateHandler(_parse_date_iso8601)
-
+
# 8-bit date handling routines written by ytrewq1.
_korean_year = u'\ub144' # b3e2 in euc-kr
_korean_month = u'\uc6d4' # bff9 in euc-kr
@@ -3170,7 +3170,7 @@ _greek_wdays = \
u'\u03a4\u03b5\u03c4': u'Wed', # d4e5f4 in iso-8859-7
u'\u03a0\u03b5\u03bc': u'Thu', # d0e5ec in iso-8859-7
u'\u03a0\u03b1\u03c1': u'Fri', # d0e1f1 in iso-8859-7
- u'\u03a3\u03b1\u03b2': u'Sat', # d3e1e2 in iso-8859-7
+ u'\u03a3\u03b1\u03b2': u'Sat', # d3e1e2 in iso-8859-7
}
_greek_date_format_re = \
@@ -3360,7 +3360,7 @@ def _parse_date_rfc822(dateString):
# 'ET' is equivalent to 'EST', etc.
_additional_timezones = {'AT': -400, 'ET': -500, 'CT': -600, 'MT': -700, 'PT': -800}
rfc822._timezones.update(_additional_timezones)
-registerDateHandler(_parse_date_rfc822)
+registerDateHandler(_parse_date_rfc822)
def _parse_date_perforce(aDateString):
"""parse a date in yyyy/mm/dd hh:mm:ss TTT format"""
@@ -3398,7 +3398,7 @@ def _getCharacterEncoding(http_headers, xml_data):
http_headers is a dictionary
xml_data is a raw string (not Unicode)
-
+
This is so much trickier than it sounds, it's not even funny.
According to RFC 3023 ('XML Media Types'), if the HTTP Content-Type
is application/xml, application/*+xml,
@@ -3417,12 +3417,12 @@ def _getCharacterEncoding(http_headers, xml_data):
served with a Content-Type of text/* and no charset parameter
must be treated as us-ascii. (We now do this.) And also that it
must always be flagged as non-well-formed. (We now do this too.)
-
+
If Content-Type is unspecified (input was local file or non-HTTP source)
or unrecognized (server just got it totally wrong), then go by the
encoding given in the XML prefix of the document and default to
'iso-8859-1' as per the HTTP specification (RFC 2616).
-
+
Then, assuming we didn't find a character encoding in the HTTP headers
(and the HTTP Content-type allowed us to look in the body), we need
to sniff the first few bytes of the XML data and try to determine
@@ -3532,7 +3532,7 @@ def _getCharacterEncoding(http_headers, xml_data):
if true_encoding.lower() == 'gb2312':
true_encoding = 'gb18030'
return true_encoding, http_encoding, xml_encoding, sniffed_xml_encoding, acceptable_content_type
-
+
def _toUTF8(data, encoding):
'''Changes an XML data stream on the fly to specify a new encoding
@@ -3595,7 +3595,7 @@ def _stripDoctype(data):
start = re.search(_s2bytes('<\w'), data)
start = start and start.start() or -1
head,data = data[:start+1], data[start+1:]
-
+
entity_pattern = re.compile(_s2bytes(r'^\s*]*?)>'), re.MULTILINE)
entity_results=entity_pattern.findall(head)
head = entity_pattern.sub(_s2bytes(''), head)
@@ -3617,10 +3617,10 @@ def _stripDoctype(data):
data = doctype_pattern.sub(replacement, head) + data
return version, data, dict(replacement and [(k.decode('utf-8'), v.decode('utf-8')) for k, v in safe_pattern.findall(replacement)])
-
+
def parse(url_file_stream_or_string, etag=None, modified=None, agent=None, referrer=None, handlers=[], request_headers={}, response_headers={}):
'''Parse a feed from a URL, file, stream, or string.
-
+
request_headers, if given, is a dict from http header name to value to add
to the request; this overrides internally generated values.
'''
@@ -3861,7 +3861,7 @@ class TextSerializer(Serializer):
stream.write('\n')
except:
pass
-
+
class PprintSerializer(Serializer):
def write(self, stream=sys.stdout):
if self.results.has_key('href'):
@@ -3869,7 +3869,7 @@ class PprintSerializer(Serializer):
from pprint import pprint
pprint(self.results, stream)
stream.write('\n')
-
+
if __name__ == '__main__':
try:
from optparse import OptionParser
diff --git a/lib/gntp/core.py b/lib/gntp/core.py
index ee544d3d..957b5270 100644
--- a/lib/gntp/core.py
+++ b/lib/gntp/core.py
@@ -70,7 +70,7 @@ class _GNTPBase(object):
'SHA1': hashlib.sha1,
'SHA256': hashlib.sha256,
'SHA512': hashlib.sha512,
- }
+ }
self.headers = {}
self.resources = {}
diff --git a/lib/httplib2/__init__.py b/lib/httplib2/__init__.py
index 01151f7f..58b0de02 100755
--- a/lib/httplib2/__init__.py
+++ b/lib/httplib2/__init__.py
@@ -3,7 +3,7 @@ from __future__ import generators
httplib2
A caching http interface that supports ETags and gzip
-to conserve bandwidth.
+to conserve bandwidth.
Requires Python 2.3 or later
@@ -24,8 +24,8 @@ __contributors__ = ["Thomas Broyer (t.broyer@ltgt.net)",
__license__ = "MIT"
__version__ = "$Rev$"
-import re
-import sys
+import re
+import sys
import email
import email.Utils
import email.Message
@@ -85,7 +85,7 @@ def has_timeout(timeout): # python 2.6
return (timeout is not None)
__all__ = ['Http', 'Response', 'ProxyInfo', 'HttpLib2Error',
- 'RedirectMissingLocation', 'RedirectLimit', 'FailedToDecompressContent',
+ 'RedirectMissingLocation', 'RedirectLimit', 'FailedToDecompressContent',
'UnimplementedDigestAuthOptionError', 'UnimplementedHmacDigestAuthOptionError',
'debuglevel']
@@ -113,8 +113,8 @@ if not hasattr(httplib.HTTPResponse, 'getheaders'):
# All exceptions raised here derive from HttpLib2Error
class HttpLib2Error(Exception): pass
-# Some exceptions can be caught and optionally
-# be turned back into responses.
+# Some exceptions can be caught and optionally
+# be turned back into responses.
class HttpLib2ErrorWithResponse(HttpLib2Error):
def __init__(self, desc, response, content):
self.response = response
@@ -176,7 +176,7 @@ def urlnorm(uri):
raise RelativeURIError("Only absolute URIs are allowed. uri = %s" % uri)
authority = authority.lower()
scheme = scheme.lower()
- if not path:
+ if not path:
path = "/"
# Could do syntax based normalization of the URI before
# computing the digest. See Section 6.2.2 of Std 66.
@@ -228,7 +228,7 @@ def _parse_cache_control(headers):
parts_with_args = [tuple([x.strip().lower() for x in part.split("=", 1)]) for part in parts if -1 != part.find("=")]
parts_wo_args = [(name.strip().lower(), 1) for name in parts if -1 == name.find("=")]
retval = dict(parts_with_args + parts_wo_args)
- return retval
+ return retval
# Whether to use a strict mode to parse WWW-Authenticate headers
# Might lead to bad results in case of ill-formed header value,
@@ -254,10 +254,10 @@ def _parse_www_authenticate(headers, headername='www-authenticate'):
while authenticate:
# Break off the scheme at the beginning of the line
if headername == 'authentication-info':
- (auth_scheme, the_rest) = ('digest', authenticate)
+ (auth_scheme, the_rest) = ('digest', authenticate)
else:
(auth_scheme, the_rest) = authenticate.split(" ", 1)
- # Now loop over all the key value pairs that come after the scheme,
+ # Now loop over all the key value pairs that come after the scheme,
# being careful not to roll into the next scheme
match = www_auth.search(the_rest)
auth_params = {}
@@ -279,17 +279,17 @@ def _entry_disposition(response_headers, request_headers):
1. Cache-Control: max-stale
2. Age: headers are not used in the calculations.
- Not that this algorithm is simpler than you might think
+ Not that this algorithm is simpler than you might think
because we are operating as a private (non-shared) cache.
This lets us ignore 's-maxage'. We can also ignore
'proxy-invalidate' since we aren't a proxy.
- We will never return a stale document as
- fresh as a design decision, and thus the non-implementation
- of 'max-stale'. This also lets us safely ignore 'must-revalidate'
+ We will never return a stale document as
+ fresh as a design decision, and thus the non-implementation
+ of 'max-stale'. This also lets us safely ignore 'must-revalidate'
since we operate as if every server has sent 'must-revalidate'.
Since we are private we get to ignore both 'public' and
'private' parameters. We also ignore 'no-transform' since
- we don't do any transformations.
+ we don't do any transformations.
The 'no-store' parameter is handled at a higher level.
So the only Cache-Control parameters we look at are:
@@ -298,7 +298,7 @@ def _entry_disposition(response_headers, request_headers):
max-age
min-fresh
"""
-
+
retval = "STALE"
cc = _parse_cache_control(request_headers)
cc_response = _parse_cache_control(response_headers)
@@ -340,10 +340,10 @@ def _entry_disposition(response_headers, request_headers):
min_fresh = int(cc['min-fresh'])
except ValueError:
min_fresh = 0
- current_age += min_fresh
+ current_age += min_fresh
if freshness_lifetime > current_age:
retval = "FRESH"
- return retval
+ return retval
def _decompressContent(response, new_content):
content = new_content
@@ -408,10 +408,10 @@ def _wsse_username_token(cnonce, iso_now, password):
return base64.b64encode(_sha("%s%s%s" % (cnonce, iso_now, password)).digest()).strip()
-# For credentials we need two things, first
+# For credentials we need two things, first
# a pool of credential to try (not necesarily tied to BAsic, Digest, etc.)
# Then we also need a list of URIs that have already demanded authentication
-# That list is tricky since sub-URIs can take the same auth, or the
+# That list is tricky since sub-URIs can take the same auth, or the
# auth scheme may change as you descend the tree.
# So we also need each Auth instance to be able to tell us
# how close to the 'top' it is.
@@ -443,7 +443,7 @@ class Authentication(object):
or such returned from the last authorized response.
Over-rise this in sub-classes if necessary.
- Return TRUE is the request is to be retried, for
+ Return TRUE is the request is to be retried, for
example Digest may return stale=true.
"""
return False
@@ -461,7 +461,7 @@ class BasicAuthentication(Authentication):
class DigestAuthentication(Authentication):
- """Only do qop='auth' and MD5, since that
+ """Only do qop='auth' and MD5, since that
is all Apache currently implements"""
def __init__(self, credentials, host, request_uri, headers, response, content, http):
Authentication.__init__(self, credentials, host, request_uri, headers, response, content, http)
@@ -474,7 +474,7 @@ class DigestAuthentication(Authentication):
self.challenge['algorithm'] = self.challenge.get('algorithm', 'MD5').upper()
if self.challenge['algorithm'] != 'MD5':
raise UnimplementedDigestAuthOptionError( _("Unsupported value for algorithm: %s." % self.challenge['algorithm']))
- self.A1 = "".join([self.credentials[0], ":", self.challenge['realm'], ":", self.credentials[1]])
+ self.A1 = "".join([self.credentials[0], ":", self.challenge['realm'], ":", self.credentials[1]])
self.challenge['nc'] = 1
def request(self, method, request_uri, headers, content, cnonce = None):
@@ -482,17 +482,17 @@ class DigestAuthentication(Authentication):
H = lambda x: _md5(x).hexdigest()
KD = lambda s, d: H("%s:%s" % (s, d))
A2 = "".join([method, ":", request_uri])
- self.challenge['cnonce'] = cnonce or _cnonce()
- request_digest = '"%s"' % KD(H(self.A1), "%s:%s:%s:%s:%s" % (self.challenge['nonce'],
- '%08x' % self.challenge['nc'],
- self.challenge['cnonce'],
+ self.challenge['cnonce'] = cnonce or _cnonce()
+ request_digest = '"%s"' % KD(H(self.A1), "%s:%s:%s:%s:%s" % (self.challenge['nonce'],
+ '%08x' % self.challenge['nc'],
+ self.challenge['cnonce'],
self.challenge['qop'], H(A2)
- ))
+ ))
headers['Authorization'] = 'Digest username="%s", realm="%s", nonce="%s", uri="%s", algorithm=%s, response=%s, qop=%s, nc=%08x, cnonce="%s"' % (
- self.credentials[0],
+ self.credentials[0],
self.challenge['realm'],
self.challenge['nonce'],
- request_uri,
+ request_uri,
self.challenge['algorithm'],
request_digest,
self.challenge['qop'],
@@ -506,14 +506,14 @@ class DigestAuthentication(Authentication):
challenge = _parse_www_authenticate(response, 'www-authenticate').get('digest', {})
if 'true' == challenge.get('stale'):
self.challenge['nonce'] = challenge['nonce']
- self.challenge['nc'] = 1
+ self.challenge['nc'] = 1
return True
else:
updated_challenge = _parse_www_authenticate(response, 'authentication-info').get('digest', {})
if updated_challenge.has_key('nextnonce'):
self.challenge['nonce'] = updated_challenge['nextnonce']
- self.challenge['nc'] = 1
+ self.challenge['nc'] = 1
return False
@@ -562,11 +562,11 @@ class HmacDigestAuthentication(Authentication):
request_digest = "%s:%s:%s:%s:%s" % (method, request_uri, cnonce, self.challenge['snonce'], headers_val)
request_digest = hmac.new(self.key, request_digest, self.hashmod).hexdigest().lower()
headers['Authorization'] = 'HMACDigest username="%s", realm="%s", snonce="%s", cnonce="%s", uri="%s", created="%s", response="%s", headers="%s"' % (
- self.credentials[0],
+ self.credentials[0],
self.challenge['realm'],
self.challenge['snonce'],
cnonce,
- request_uri,
+ request_uri,
created,
request_digest,
keylist,
@@ -583,7 +583,7 @@ class WsseAuthentication(Authentication):
"""This is thinly tested and should not be relied upon.
At this time there isn't any third party server to test against.
Blogger and TypePad implemented this algorithm at one point
- but Blogger has since switched to Basic over HTTPS and
+ but Blogger has since switched to Basic over HTTPS and
TypePad has implemented it wrong, by never issuing a 401
challenge but instead requiring your client to telepathically know that
their endpoint is expecting WSSE profile="UsernameToken"."""
@@ -629,7 +629,7 @@ class GoogleLoginAuthentication(Authentication):
def request(self, method, request_uri, headers, content):
"""Modify the request headers to add the appropriate
Authorization header."""
- headers['authorization'] = 'GoogleLogin Auth=' + self.Auth
+ headers['authorization'] = 'GoogleLogin Auth=' + self.Auth
AUTH_SCHEME_CLASSES = {
@@ -644,13 +644,13 @@ AUTH_SCHEME_ORDER = ["hmacdigest", "googlelogin", "digest", "wsse", "basic"]
class FileCache(object):
"""Uses a local directory as a store for cached files.
- Not really safe to use if multiple threads or processes are going to
+ Not really safe to use if multiple threads or processes are going to
be running on the same cache.
"""
def __init__(self, cache, safe=safename): # use safe=lambda x: md5.new(x).hexdigest() for the old behavior
self.cache = cache
self.safe = safe
- if not os.path.exists(cache):
+ if not os.path.exists(cache):
os.makedirs(self.cache)
def get(self, key):
@@ -688,7 +688,7 @@ class Credentials(object):
def iter(self, domain):
for (cdomain, name, password) in self.credentials:
if cdomain == "" or domain == cdomain:
- yield (name, password)
+ yield (name, password)
class KeyCerts(Credentials):
"""Identical to Credentials except that
@@ -772,7 +772,7 @@ class HTTPSConnectionWithTimeout(httplib.HTTPSConnection):
sock.setproxy(*self.proxy_info.astuple())
else:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-
+
if has_timeout(self.timeout):
sock.settimeout(self.timeout)
sock.connect((self.host, self.port))
@@ -820,7 +820,7 @@ the same interface as FileCache."""
# If set to False then no redirects are followed, even safe ones.
self.follow_redirects = True
-
+
# Which HTTP methods do we apply optimistic concurrency to, i.e.
# which methods get an "if-match:" etag header added to them.
self.optimistic_concurrency_methods = ["PUT"]
@@ -831,7 +831,7 @@ the same interface as FileCache."""
self.ignore_etag = False
- self.force_exception_to_status_code = False
+ self.force_exception_to_status_code = False
self.timeout = timeout
@@ -908,12 +908,12 @@ the same interface as FileCache."""
auths = [(auth.depth(request_uri), auth) for auth in self.authorizations if auth.inscope(host, request_uri)]
auth = auths and sorted(auths)[0][1] or None
- if auth:
+ if auth:
auth.request(method, request_uri, headers, body)
(response, content) = self._conn_request(conn, request_uri, method, body, headers)
- if auth:
+ if auth:
if auth.response(response, body):
auth.request(method, request_uri, headers, body)
(response, content) = self._conn_request(conn, request_uri, method, body, headers )
@@ -921,7 +921,7 @@ the same interface as FileCache."""
if response.status == 401:
for authorization in self._auth_from_challenge(host, request_uri, headers, response, content):
- authorization.request(method, request_uri, headers, body)
+ authorization.request(method, request_uri, headers, body)
(response, content) = self._conn_request(conn, request_uri, method, body, headers, )
if response.status != 401:
self.authorizations.append(authorization)
@@ -944,7 +944,7 @@ the same interface as FileCache."""
if response.status == 301 and method in ["GET", "HEAD"]:
response['-x-permanent-redirect-url'] = response['location']
if not response.has_key('content-location'):
- response['content-location'] = absolute_uri
+ response['content-location'] = absolute_uri
_updateCache(headers, response, content, self.cache, cachekey)
if headers.has_key('if-none-match'):
del headers['if-none-match']
@@ -954,7 +954,7 @@ the same interface as FileCache."""
location = response['location']
old_response = copy.deepcopy(response)
if not old_response.has_key('content-location'):
- old_response['content-location'] = absolute_uri
+ old_response['content-location'] = absolute_uri
redirect_method = ((response.status == 303) and (method not in ["GET", "HEAD"])) and "GET" or method
(response, content) = self.request(location, redirect_method, body=body, headers = headers, redirections = redirections - 1)
response.previous = old_response
@@ -963,7 +963,7 @@ the same interface as FileCache."""
elif response.status in [200, 203] and method == "GET":
# Don't cache 206's since we aren't going to handle byte range requests
if not response.has_key('content-location'):
- response['content-location'] = absolute_uri
+ response['content-location'] = absolute_uri
_updateCache(headers, response, content, self.cache, cachekey)
return (response, content)
@@ -978,10 +978,10 @@ the same interface as FileCache."""
def request(self, uri, method="GET", body=None, headers=None, redirections=DEFAULT_MAX_REDIRECTS, connection_type=None):
""" Performs a single HTTP request.
-The 'uri' is the URI of the HTTP resource and can begin
+The 'uri' is the URI of the HTTP resource and can begin
with either 'http' or 'https'. The value of 'uri' must be an absolute URI.
-The 'method' is the HTTP method to perform, such as GET, POST, DELETE, etc.
+The 'method' is the HTTP method to perform, such as GET, POST, DELETE, etc.
There is no restriction on the methods allowed.
The 'body' is the entity body to be sent with the request. It is a string
@@ -990,11 +990,11 @@ object.
Any extra headers that are to be sent with the request should be provided in the
'headers' dictionary.
-The maximum number of redirect to follow before raising an
+The maximum number of redirect to follow before raising an
exception is 'redirections. The default is 5.
-The return value is a tuple of (response, content), the first
-being and instance of the 'Response' class, the second being
+The return value is a tuple of (response, content), the first
+being and instance of the 'Response' class, the second being
a string that contains the response entity body.
"""
try:
@@ -1085,13 +1085,13 @@ a string that contains the response entity body.
# Determine our course of action:
# Is the cached entry fresh or stale?
# Has the client requested a non-cached response?
- #
- # There seems to be three possible answers:
+ #
+ # There seems to be three possible answers:
# 1. [FRESH] Return the cache entry w/o doing a GET
# 2. [STALE] Do the GET (but add in cache validators if available)
# 3. [TRANSPARENT] Do a GET w/o any cache validators (Cache-Control: no-cache) on the request
- entry_disposition = _entry_disposition(info, headers)
-
+ entry_disposition = _entry_disposition(info, headers)
+
if entry_disposition == "FRESH":
if not cached_value:
info['status'] = '504'
@@ -1113,7 +1113,7 @@ a string that contains the response entity body.
if response.status == 304 and method == "GET":
# Rewrite the cache entry with the new end-to-end headers
- # Take all headers that are in response
+ # Take all headers that are in response
# and overwrite their values in info.
# unless they are hop-by-hop, or are listed in the connection header.
@@ -1125,14 +1125,14 @@ a string that contains the response entity body.
_updateCache(headers, merged_response, content, self.cache, cachekey)
response = merged_response
response.status = 200
- response.fromcache = True
+ response.fromcache = True
elif response.status == 200:
content = new_content
else:
self.cache.delete(cachekey)
- content = new_content
- else:
+ content = new_content
+ else:
cc = _parse_cache_control(headers)
if cc.has_key('only-if-cached'):
info['status'] = '504'
@@ -1146,7 +1146,7 @@ a string that contains the response entity body.
response = e.response
content = e.content
response.status = 500
- response.reason = str(e)
+ response.reason = str(e)
elif isinstance(e, socket.timeout) or (isinstance(e, socket.error) and 'timed out' in str(e)):
content = "Request Timeout"
response = Response( {
@@ -1156,24 +1156,24 @@ a string that contains the response entity body.
})
response.reason = "Request Timeout"
else:
- content = str(e)
+ content = str(e)
response = Response( {
"content-type": "text/plain",
"status": "400",
"content-length": len(content)
})
- response.reason = "Bad Request"
+ response.reason = "Bad Request"
else:
raise
-
+
return (response, content)
-
+
class Response(dict):
"""An object more like email.Message than httplib.HTTPResponse."""
-
+
"""Is this response from our local cache"""
fromcache = False
@@ -1189,27 +1189,27 @@ class Response(dict):
previous = None
def __init__(self, info):
- # info is either an email.Message or
+ # info is either an email.Message or
# an httplib.HTTPResponse object.
if isinstance(info, httplib.HTTPResponse):
- for key, value in info.getheaders():
- self[key.lower()] = value
+ for key, value in info.getheaders():
+ self[key.lower()] = value
self.status = info.status
self['status'] = str(self.status)
self.reason = info.reason
self.version = info.version
elif isinstance(info, email.Message.Message):
- for key, value in info.items():
- self[key] = value
+ for key, value in info.items():
+ self[key] = value
self.status = int(self['status'])
else:
- for key, value in info.iteritems():
- self[key] = value
+ for key, value in info.iteritems():
+ self[key] = value
self.status = int(self.get('status', self.status))
def __getattr__(self, name):
if name == 'dict':
- return self
- else:
- raise AttributeError, name
+ return self
+ else:
+ raise AttributeError, name
diff --git a/lib/httplib2/iri2uri.py b/lib/httplib2/iri2uri.py
index 70667edf..e4cda1d7 100755
--- a/lib/httplib2/iri2uri.py
+++ b/lib/httplib2/iri2uri.py
@@ -16,7 +16,7 @@ import urlparse
# Convert an IRI to a URI following the rules in RFC 3987
-#
+#
# The characters we need to enocde and escape are defined in the spec:
#
# iprivate = %xE000-F8FF / %xF0000-FFFFD / %x100000-10FFFD
@@ -49,7 +49,7 @@ escape_range = [
(0xF0000, 0xFFFFD ),
(0x100000, 0x10FFFD)
]
-
+
def encode(c):
retval = c
i = ord(c)
@@ -63,19 +63,19 @@ def encode(c):
def iri2uri(uri):
- """Convert an IRI to a URI. Note that IRIs must be
+ """Convert an IRI to a URI. Note that IRIs must be
passed in a unicode strings. That is, do not utf-8 encode
- the IRI before passing it into the function."""
+ the IRI before passing it into the function."""
if isinstance(uri ,unicode):
(scheme, authority, path, query, fragment) = urlparse.urlsplit(uri)
authority = authority.encode('idna')
# For each character in 'ucschar' or 'iprivate'
# 1. encode as utf-8
- # 2. then %-encode each octet of that utf-8
+ # 2. then %-encode each octet of that utf-8
uri = urlparse.urlunsplit((scheme, authority, path, query, fragment))
uri = "".join([encode(c) for c in uri])
return uri
-
+
if __name__ == "__main__":
import unittest
@@ -83,7 +83,7 @@ if __name__ == "__main__":
def test_uris(self):
"""Test that URIs are invariant under the transformation."""
- invariant = [
+ invariant = [
u"ftp://ftp.is.co.za/rfc/rfc1808.txt",
u"http://www.ietf.org/rfc/rfc2396.txt",
u"ldap://[2001:db8::7]/c=GB?objectClass?one",
@@ -94,7 +94,7 @@ if __name__ == "__main__":
u"urn:oasis:names:specification:docbook:dtd:xml:4.1.2" ]
for uri in invariant:
self.assertEqual(uri, iri2uri(uri))
-
+
def test_iri(self):
""" Test that the right type of escaping is done for each part of the URI."""
self.assertEqual("http://xn--o3h.com/%E2%98%84", iri2uri(u"http://\N{COMET}.com/\N{COMET}"))
@@ -107,4 +107,4 @@ if __name__ == "__main__":
unittest.main()
-
+
diff --git a/lib/mako/ext/autohandler.py b/lib/mako/ext/autohandler.py
index d56cbc1e..7bd1c71f 100644
--- a/lib/mako/ext/autohandler.py
+++ b/lib/mako/ext/autohandler.py
@@ -48,7 +48,7 @@ def autohandler(template, context, name='autohandler'):
if len(tokens) == 1:
break
tokens[-2:] = [name]
-
+
if not lookup.filesystem_checks:
return lookup._uri_cache.setdefault(
(autohandler, _template_uri, name), None)
@@ -62,4 +62,4 @@ def _file_exists(lookup, path):
return True
else:
return False
-
+
diff --git a/lib/mako/ext/preprocessors.py b/lib/mako/ext/preprocessors.py
index 5a15ff35..acd32e55 100644
--- a/lib/mako/ext/preprocessors.py
+++ b/lib/mako/ext/preprocessors.py
@@ -4,16 +4,16 @@
# This module is part of Mako and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
-"""preprocessing functions, used with the 'preprocessor'
+"""preprocessing functions, used with the 'preprocessor'
argument on Template, TemplateLookup"""
import re
def convert_comments(text):
"""preprocess old style comments.
-
+
example:
-
+
from mako.ext.preprocessors import convert_comments
t = Template(..., preprocessor=preprocess_comments)"""
return re.sub(r'(?<=\n)\s*#[^#]', "##", text)
diff --git a/lib/musicbrainzngs/musicbrainz.py b/lib/musicbrainzngs/musicbrainz.py
index 65454834..16492087 100644
--- a/lib/musicbrainzngs/musicbrainz.py
+++ b/lib/musicbrainzngs/musicbrainz.py
@@ -279,7 +279,7 @@ def auth(u, p):
global user, password
user = u
password = p
-
+
def hpauth(u, p):
"""Set the username and password to be used in subsequent queries to
the MusicBrainz XML API that require authentication.
@@ -574,7 +574,7 @@ def _mb_request(path, method='GET', auth_required=False, client_required=False,
whether exceptions should be raised if the client and
username/password are left unspecified, respectively.
"""
- global parser_fun
+ global parser_fun
if args is None:
args = {}
@@ -638,7 +638,7 @@ def _mb_request(path, method='GET', auth_required=False, client_required=False,
if hostname == '144.76.94.239:8181':
base64string = base64.encodestring('%s:%s' % (hpuser, hppassword)).replace('\n', '')
req.add_header("Authorization", "Basic %s" % base64string)
-
+
_log.debug("requesting with UA %s" % _useragent)
if body:
req.add_header('Content-Type', 'application/xml; charset=UTF-8')
@@ -908,7 +908,7 @@ def get_releases_by_discid(id, includes=[], toc=None, cdstubs=True):
The `toc` should have to same format as :attr:`discid.Disc.toc_string`.
If no toc matches in musicbrainz but a :musicbrainz:`CD Stub` does,
- the CD Stub will be returned. Prevent this from happening by
+ the CD Stub will be returned. Prevent this from happening by
passing `cdstubs=False`.
The result is a dict with either a 'disc' , a 'cdstub' key
diff --git a/lib/mutagen/easymp4.py b/lib/mutagen/easymp4.py
index 3abacccc..65e78b74 100644
--- a/lib/mutagen/easymp4.py
+++ b/lib/mutagen/easymp4.py
@@ -22,7 +22,7 @@ class EasyMP4Tags(DictMixin, Metadata):
strings, and values are a list of Unicode strings (and these lists
are always of length 0 or 1).
- If you need access to the full MP4 metadata feature set, you should use
+ If you need access to the full MP4 metadata feature set, you should use
MP4, not EasyMP4.
"""
diff --git a/lib/oauth2/__init__.py b/lib/oauth2/__init__.py
index 2b938909..cf77705d 100644
--- a/lib/oauth2/__init__.py
+++ b/lib/oauth2/__init__.py
@@ -85,11 +85,11 @@ def generate_verifier(length=8):
class Consumer(object):
"""A consumer of OAuth-protected services.
-
+
The OAuth consumer is a "third-party" service that wants to access
protected resources from an OAuth service provider on behalf of an end
user. It's kind of the OAuth client.
-
+
Usually a consumer must be registered with the service provider by the
developer of the consumer software. As part of that process, the service
provider gives the consumer a *key* and a *secret* with which the consumer
@@ -97,7 +97,7 @@ class Consumer(object):
key in each request to identify itself, but will use its secret only when
signing requests, to prove that the request is from that particular
registered consumer.
-
+
Once registered, the consumer can then use its consumer credentials to ask
the service provider for a request token, kicking off the OAuth
authorization process.
@@ -125,12 +125,12 @@ class Consumer(object):
class Token(object):
"""An OAuth credential used to request authorization or a protected
resource.
-
+
Tokens in OAuth comprise a *key* and a *secret*. The key is included in
requests to identify the token being used, but the secret is used only in
the signature, to prove that the requester is who the server gave the
token to.
-
+
When first negotiating the authorization, the consumer asks for a *request
token* that the live user authorizes with the service provider. The
consumer then exchanges the request token for an *access token* that can
@@ -175,7 +175,7 @@ class Token(object):
def to_string(self):
"""Returns this token as a plain string, suitable for storage.
-
+
The resulting string includes the token's secret, so you should never
send or store this string where a third party can read it.
"""
@@ -188,7 +188,7 @@ class Token(object):
if self.callback_confirmed is not None:
data['oauth_callback_confirmed'] = self.callback_confirmed
return urllib.urlencode(data)
-
+
@staticmethod
def from_string(s):
"""Deserializes a token from a string like one returned by
@@ -209,7 +209,7 @@ class Token(object):
try:
secret = params['oauth_token_secret'][0]
except Exception:
- raise ValueError("'oauth_token_secret' not found in "
+ raise ValueError("'oauth_token_secret' not found in "
"OAuth request.")
token = Token(key, secret)
@@ -225,45 +225,45 @@ class Token(object):
def setter(attr):
name = attr.__name__
-
+
def getter(self):
try:
return self.__dict__[name]
except KeyError:
raise AttributeError(name)
-
+
def deleter(self):
del self.__dict__[name]
-
+
return property(getter, attr, deleter)
class Request(dict):
-
+
"""The parameters and information for an HTTP request, suitable for
authorizing with OAuth credentials.
-
+
When a consumer wants to access a service's protected resources, it does
so using a signed HTTP request identifying itself (the consumer) with its
key, and providing an access token authorized by the end user to access
those resources.
-
+
"""
-
+
http_method = HTTP_METHOD
http_url = None
version = VERSION
-
+
def __init__(self, method=HTTP_METHOD, url=None, parameters=None):
if method is not None:
self.method = method
-
+
if url is not None:
self.url = url
-
+
if parameters is not None:
self.update(parameters)
-
+
@setter
def url(self, value):
parts = urlparse.urlparse(value)
@@ -280,33 +280,33 @@ class Request(dict):
value = '%s://%s%s' % (scheme, netloc, path)
self.__dict__['url'] = value
-
+
@setter
def method(self, value):
self.__dict__['method'] = value.upper()
-
+
def _get_timestamp_nonce(self):
return self['oauth_timestamp'], self['oauth_nonce']
-
+
def get_nonoauth_parameters(self):
"""Get any non-OAuth parameters."""
- return dict([(k, v) for k, v in self.iteritems()
+ return dict([(k, v) for k, v in self.iteritems()
if not k.startswith('oauth_')])
-
+
def to_header(self, realm=''):
"""Serialize as a header for an HTTPAuth request."""
- oauth_params = ((k, v) for k, v in self.items()
+ oauth_params = ((k, v) for k, v in self.items()
if k.startswith('oauth_'))
stringy_params = ((k, escape(str(v))) for k, v in oauth_params)
header_params = ('%s="%s"' % (k, v) for k, v in stringy_params)
params_header = ', '.join(header_params)
-
+
auth_header = 'OAuth realm="%s"' % realm
if params_header:
auth_header = "%s, %s" % (auth_header, params_header)
-
+
return {'Authorization': auth_header}
-
+
def to_postdata(self):
"""Serialize as post data for a POST request."""
return self.encode_postdata(self)
@@ -327,7 +327,7 @@ class Request(dict):
raise Error('Parameter not found: %s' % parameter)
return ret
-
+
def get_normalized_parameters(self):
"""Return a string that contains the parameters that must be signed."""
items = [(k, v) for k, v in self.items() if k != 'oauth_signature']
@@ -337,7 +337,7 @@ class Request(dict):
# (http://tools.ietf.org/html/draft-hammer-oauth-07#section-3.6)
# Spaces must be encoded with "%20" instead of "+"
return encoded_str.replace('+', '%20')
-
+
def sign_request(self, signature_method, consumer, token):
"""Set the signature parameter to the result of sign."""
@@ -349,24 +349,24 @@ class Request(dict):
self['oauth_signature_method'] = signature_method.name
self['oauth_signature'] = signature_method.sign(self, consumer, token)
-
+
@classmethod
def make_timestamp(cls):
"""Get seconds since epoch (UTC)."""
return str(int(time.time()))
-
+
@classmethod
def make_nonce(cls):
"""Generate pseudorandom number."""
return str(random.randint(0, 100000000))
-
+
@classmethod
def from_request(cls, http_method, http_url, headers=None, parameters=None,
query_string=None):
"""Combines multiple parameter sources."""
if parameters is None:
parameters = {}
-
+
# Headers
if headers and 'Authorization' in headers:
auth_header = headers['Authorization']
@@ -380,57 +380,57 @@ class Request(dict):
except:
raise Error('Unable to parse OAuth parameters from '
'Authorization header.')
-
+
# GET or POST query string.
if query_string:
query_params = cls._split_url_string(query_string)
parameters.update(query_params)
-
+
# URL parameters.
param_str = urlparse.urlparse(http_url)[4] # query
url_params = cls._split_url_string(param_str)
parameters.update(url_params)
-
+
if parameters:
return cls(http_method, http_url, parameters)
-
+
return None
-
+
@classmethod
def from_consumer_and_token(cls, consumer, token=None,
http_method=HTTP_METHOD, http_url=None, parameters=None):
if not parameters:
parameters = {}
-
+
defaults = {
'oauth_consumer_key': consumer.key,
'oauth_timestamp': cls.make_timestamp(),
'oauth_nonce': cls.make_nonce(),
'oauth_version': cls.version,
}
-
+
defaults.update(parameters)
parameters = defaults
-
+
if token:
parameters['oauth_token'] = token.key
-
+
return Request(http_method, http_url, parameters)
-
+
@classmethod
- def from_token_and_callback(cls, token, callback=None,
+ def from_token_and_callback(cls, token, callback=None,
http_method=HTTP_METHOD, http_url=None, parameters=None):
if not parameters:
parameters = {}
-
+
parameters['oauth_token'] = token.key
-
+
if callback:
parameters['oauth_callback'] = callback
-
+
return cls(http_method, http_url, parameters)
-
+
@staticmethod
def _split_header(header):
"""Turn Authorization: header into parameters."""
@@ -447,7 +447,7 @@ class Request(dict):
# Remove quotes and unescape the value.
params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"'))
return params
-
+
@staticmethod
def _split_url_string(param_str):
"""Turn URL string into parameters."""
@@ -460,7 +460,7 @@ class Request(dict):
class Server(object):
"""A skeletal implementation of a service provider, providing protected
resources to requests from authorized consumers.
-
+
This class implements the logic to check requests for authorization. You
can use it with your web server or web framework to protect certain
resources with OAuth.
@@ -536,7 +536,7 @@ class Server(object):
if not valid:
key, base = signature_method.signing_base(request, consumer, token)
- raise Error('Invalid signature. Expected signature base '
+ raise Error('Invalid signature. Expected signature base '
'string: %s' % base)
built = signature_method.sign(request, consumer, token)
@@ -567,7 +567,7 @@ class Client(httplib2.Http):
self.token = token
self.method = SignatureMethod_HMAC_SHA1()
- httplib2.Http.__init__(self, cache=cache, timeout=timeout,
+ httplib2.Http.__init__(self, cache=cache, timeout=timeout,
proxy_info=proxy_info)
def set_signature_method(self, method):
@@ -576,10 +576,10 @@ class Client(httplib2.Http):
self.method = method
- def request(self, uri, method="GET", body=None, headers=None,
+ def request(self, uri, method="GET", body=None, headers=None,
redirections=httplib2.DEFAULT_MAX_REDIRECTS, connection_type=None,
force_auth_header=False):
-
+
if not isinstance(headers, dict):
headers = {}
@@ -587,7 +587,7 @@ class Client(httplib2.Http):
parameters = dict(parse_qsl(body))
elif method == "GET":
parsed = urlparse.urlparse(uri)
- parameters = parse_qs(parsed.query)
+ parameters = parse_qs(parsed.query)
else:
parameters = None
@@ -614,14 +614,14 @@ class Client(httplib2.Http):
# don't call update twice.
headers.update(req.to_header())
- return httplib2.Http.request(self, uri, method=method, body=body,
- headers=headers, redirections=redirections,
+ return httplib2.Http.request(self, uri, method=method, body=body,
+ headers=headers, redirections=redirections,
connection_type=connection_type)
class SignatureMethod(object):
"""A way of signing requests.
-
+
The OAuth protocol lets consumers and service providers pick a way to sign
requests. This interface shows the methods expected by the other `oauth`
modules for signing requests. Subclass it and implement its methods to
@@ -657,7 +657,7 @@ class SignatureMethod(object):
class SignatureMethod_HMAC_SHA1(SignatureMethod):
name = 'HMAC-SHA1'
-
+
def signing_base(self, request, consumer, token):
sig = (
escape(request.method),
diff --git a/lib/pyItunes/Library.py b/lib/pyItunes/Library.py
index 2400c969..3118fb7b 100644
--- a/lib/pyItunes/Library.py
+++ b/lib/pyItunes/Library.py
@@ -36,6 +36,6 @@ class Library:
if attributes.get('Play Count'):
s.play_count = int(attributes.get('Play Count'))
if attributes.get('Location'):
- s.location = attributes.get('Location')
+ s.location = attributes.get('Location')
songs.append(s)
return songs
\ No newline at end of file
diff --git a/lib/pyItunes/Song.py b/lib/pyItunes/Song.py
index 27d44d79..c59759e6 100644
--- a/lib/pyItunes/Song.py
+++ b/lib/pyItunes/Song.py
@@ -42,5 +42,5 @@ class Song:
album_rating = None
play_count = None
location = None
-
+
#title = property(getTitle,setTitle)
\ No newline at end of file
diff --git a/lib/pyItunes/XMLLibraryParser.py b/lib/pyItunes/XMLLibraryParser.py
index 7e4b239a..6824aee7 100644
--- a/lib/pyItunes/XMLLibraryParser.py
+++ b/lib/pyItunes/XMLLibraryParser.py
@@ -5,7 +5,7 @@ class XMLLibraryParser:
s = f.read()
lines = s.split("\n")
self.dictionary = self.parser(lines)
-
+
def getValue(self,restOfLine):
value = re.sub("<.*?>","",restOfLine)
u = unicode(value,"utf-8")
diff --git a/lib/pygazelle/api.py b/lib/pygazelle/api.py
index 3bdd7c2e..91df1ad2 100644
--- a/lib/pygazelle/api.py
+++ b/lib/pygazelle/api.py
@@ -201,7 +201,7 @@ class GazelleAPI(object):
Returns the inbox Mailbox for the logged in user
"""
return Mailbox(self, 'inbox', page, sort)
-
+
def get_sentbox(self, page='1', sort='unread'):
"""
Returns the sentbox Mailbox for the logged in user
diff --git a/lib/pygazelle/inbox.py b/lib/pygazelle/inbox.py
index e5016286..a26c9a26 100644
--- a/lib/pygazelle/inbox.py
+++ b/lib/pygazelle/inbox.py
@@ -58,9 +58,9 @@ class Mailbox(object):
"""
This class represents the logged in user's inbox/sentbox
"""
- def __init__(self, parent_api, boxtype='inbox', page='1', sort='unread'):
+ def __init__(self, parent_api, boxtype='inbox', page='1', sort='unread'):
self.parent_api = parent_api
- self.boxtype = boxtype
+ self.boxtype = boxtype
self.current_page = page
self.total_pages = None
self.sort = sort
diff --git a/lib/pynma/__init__.py b/lib/pynma/__init__.py
index f90424eb..cbd82cfd 100644
--- a/lib/pynma/__init__.py
+++ b/lib/pynma/__init__.py
@@ -1,4 +1,4 @@
#!/usr/bin/python
-from pynma import PyNMA
+from pynma import PyNMA
diff --git a/lib/pynma/pynma.py b/lib/pynma/pynma.py
index fc7d8de2..037145c8 100644
--- a/lib/pynma/pynma.py
+++ b/lib/pynma/pynma.py
@@ -99,7 +99,7 @@ class PyNMA(object):
res = self.callapi('POST', ADD_PATH, datas)
results[datas['apikey']] = res
return results
-
+
def callapi(self, method, path, args):
headers = { 'User-Agent': USER_AGENT }
if method == "POST":
@@ -116,7 +116,7 @@ class PyNMA(object):
'message': str(e)
}
pass
-
+
return res
def _parse_reponse(self, response):
@@ -133,5 +133,5 @@ class PyNMA(object):
res['message'] = elem.firstChild.nodeValue
res['type'] = elem.tagName
return res
-
-
+
+
diff --git a/lib/requests/cookies.py b/lib/requests/cookies.py
index 831c49c6..3456a9fe 100644
--- a/lib/requests/cookies.py
+++ b/lib/requests/cookies.py
@@ -440,7 +440,7 @@ def merge_cookies(cookiejar, cookies):
"""
if not isinstance(cookiejar, cookielib.CookieJar):
raise ValueError('You can only merge into CookieJar')
-
+
if isinstance(cookies, dict):
cookiejar = cookiejar_from_dict(
cookies, cookiejar=cookiejar, overwrite=False)
diff --git a/lib/requests/packages/chardet/charsetgroupprober.py b/lib/requests/packages/chardet/charsetgroupprober.py
index 85e7a1c6..ddbeeea2 100644
--- a/lib/requests/packages/chardet/charsetgroupprober.py
+++ b/lib/requests/packages/chardet/charsetgroupprober.py
@@ -1,11 +1,11 @@
######################## BEGIN LICENSE BLOCK ########################
# The Original Code is Mozilla Communicator client code.
-#
+#
# The Initial Developer of the Original Code is
# Netscape Communications Corporation.
# Portions created by the Initial Developer are Copyright (C) 1998
# the Initial Developer. All Rights Reserved.
-#
+#
# Contributor(s):
# Mark Pilgrim - port to Python
#
@@ -13,12 +13,12 @@
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
-#
+#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
-#
+#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
diff --git a/lib/requests/packages/chardet/constants.py b/lib/requests/packages/chardet/constants.py
index e4d148b3..8895c94e 100644
--- a/lib/requests/packages/chardet/constants.py
+++ b/lib/requests/packages/chardet/constants.py
@@ -14,12 +14,12 @@
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
-#
+#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
-#
+#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
diff --git a/lib/requests/packages/chardet/euckrfreq.py b/lib/requests/packages/chardet/euckrfreq.py
index a179e4c2..1fa75883 100644
--- a/lib/requests/packages/chardet/euckrfreq.py
+++ b/lib/requests/packages/chardet/euckrfreq.py
@@ -13,12 +13,12 @@
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
-#
+#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
-#
+#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
@@ -35,14 +35,14 @@
#
# Idea Distribution Ratio = 0.98653 / (1-0.98653) = 73.24
# Random Distribution Ration = 512 / (2350-512) = 0.279.
-#
-# Typical Distribution Ratio
+#
+# Typical Distribution Ratio
EUCKR_TYPICAL_DISTRIBUTION_RATIO = 6.0
EUCKR_TABLE_SIZE = 2352
-# Char to FreqOrder table ,
+# Char to FreqOrder table ,
EUCKRCharToFreqOrder = ( \
13, 130, 120,1396, 481,1719,1720, 328, 609, 212,1721, 707, 400, 299,1722, 87,
1397,1723, 104, 536,1117,1203,1724,1267, 685,1268, 508,1725,1726,1727,1728,1398,
diff --git a/lib/requests/packages/chardet/euctwprober.py b/lib/requests/packages/chardet/euctwprober.py
index fe652fe3..178a3042 100644
--- a/lib/requests/packages/chardet/euctwprober.py
+++ b/lib/requests/packages/chardet/euctwprober.py
@@ -13,12 +13,12 @@
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
-#
+#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
-#
+#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
diff --git a/lib/requests/packages/chardet/gb2312prober.py b/lib/requests/packages/chardet/gb2312prober.py
index 0325a2d8..b7a181a2 100644
--- a/lib/requests/packages/chardet/gb2312prober.py
+++ b/lib/requests/packages/chardet/gb2312prober.py
@@ -13,12 +13,12 @@
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
-#
+#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
-#
+#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
diff --git a/lib/unidecode/__init__.py b/lib/unidecode/__init__.py
index 82eb5a3f..dd3d2310 100644
--- a/lib/unidecode/__init__.py
+++ b/lib/unidecode/__init__.py
@@ -39,7 +39,7 @@ def unidecode(string):
if codepoint < 0x80: # Basic ASCII
retval.append(str(char))
continue
-
+
if codepoint > 0xeffff:
continue # Characters in Private Use Area and above are ignored
diff --git a/lib/yaml/constructor.py b/lib/yaml/constructor.py
index 635faac3..abbff97b 100644
--- a/lib/yaml/constructor.py
+++ b/lib/yaml/constructor.py
@@ -287,7 +287,7 @@ class SafeConstructor(BaseConstructor):
return str(value).decode('base64')
except (binascii.Error, UnicodeEncodeError), exc:
raise ConstructorError(None, None,
- "failed to decode base64 data: %s" % exc, node.start_mark)
+ "failed to decode base64 data: %s" % exc, node.start_mark)
timestamp_regexp = re.compile(
ur'''^(?P
[0-9][0-9][0-9][0-9])
diff --git a/lib/yaml/emitter.py b/lib/yaml/emitter.py
index e5bcdccc..488337f2 100644
--- a/lib/yaml/emitter.py
+++ b/lib/yaml/emitter.py
@@ -674,7 +674,7 @@ class Emitter(object):
# Check for indicators.
if index == 0:
# Leading indicators are special characters.
- if ch in u'#,[]{}&*!|>\'\"%@`':
+ if ch in u'#,[]{}&*!|>\'\"%@`':
flow_indicators = True
block_indicators = True
if ch in u'?:':
diff --git a/lib/yaml/parser.py b/lib/yaml/parser.py
index f9e3057f..ffec9552 100644
--- a/lib/yaml/parser.py
+++ b/lib/yaml/parser.py
@@ -482,7 +482,7 @@ class Parser(object):
token = self.peek_token()
raise ParserError("while parsing a flow sequence", self.marks[-1],
"expected ',' or ']', but got %r" % token.id, token.start_mark)
-
+
if self.check_token(KeyToken):
token = self.peek_token()
event = MappingStartEvent(None, None, True,
diff --git a/lib/yaml/scanner.py b/lib/yaml/scanner.py
index 5228fad6..4e578581 100644
--- a/lib/yaml/scanner.py
+++ b/lib/yaml/scanner.py
@@ -314,7 +314,7 @@ class Scanner(object):
# Remove the saved possible key position at the current flow level.
if self.flow_level in self.possible_simple_keys:
key = self.possible_simple_keys[self.flow_level]
-
+
if key.required:
raise ScannerError("while scanning a simple key", key.mark,
"could not found expected ':'", self.get_mark())
@@ -363,11 +363,11 @@ class Scanner(object):
# Read the token.
mark = self.get_mark()
-
+
# Add STREAM-START.
self.tokens.append(StreamStartToken(mark, mark,
encoding=self.encoding))
-
+
def fetch_stream_end(self):
@@ -381,7 +381,7 @@ class Scanner(object):
# Read the token.
mark = self.get_mark()
-
+
# Add STREAM-END.
self.tokens.append(StreamEndToken(mark, mark))
@@ -389,7 +389,7 @@ class Scanner(object):
self.done = True
def fetch_directive(self):
-
+
# Set the current intendation to -1.
self.unwind_indent(-1)
@@ -516,7 +516,7 @@ class Scanner(object):
self.tokens.append(BlockEntryToken(start_mark, end_mark))
def fetch_key(self):
-
+
# Block context needs additional checks.
if not self.flow_level:
@@ -566,7 +566,7 @@ class Scanner(object):
# It must be a part of a complex key.
else:
-
+
# Block context needs additional checks.
# (Do we really need them? They will be catched by the parser
# anyway.)
@@ -1024,14 +1024,14 @@ class Scanner(object):
# Unfortunately, folding rules are ambiguous.
#
# This is the folding according to the specification:
-
+
if folded and line_break == u'\n' \
and leading_non_space and self.peek() not in u' \t':
if not breaks:
chunks.append(u' ')
else:
chunks.append(line_break)
-
+
# This is Clark Evans's interpretation (also in the spec
# examples):
#