From 75ba22ebdd0ca8676caac74a449ec500c46a40c5 Mon Sep 17 00:00:00 2001 From: Remy Date: Sat, 16 Jul 2011 02:25:57 -0700 Subject: [PATCH] Some more big changes: no more 503 errors, add as many artists as you want at the same time, no more DB is locked errors, no more chinese/japanese results in the searches, version number displayed at the bottom, add artists while importing, some other stuff --- data/css/style.css | 3 + headphones/db.py | 84 +++++++++++++ headphones/helpers.py | 24 +++- headphones/importer.py | 155 +++++++++++++++++++++++ headphones/itunesimport.py | 133 -------------------- headphones/logger.py | 2 +- headphones/mb.py | 217 +++++++++++++++++++++++++------- headphones/searcher.py | 26 ++-- headphones/templates.py | 2 +- headphones/updater.py | 66 +++++----- headphones/webserve.py | 250 ++++++++++++++----------------------- headphones/webstart.py | 4 + lib/beets/mediafile.py | 2 +- lib/musicbrainz2/utils.py | 2 +- 14 files changed, 576 insertions(+), 394 deletions(-) create mode 100644 headphones/db.py create mode 100644 headphones/importer.py delete mode 100644 headphones/itunesimport.py diff --git a/data/css/style.css b/data/css/style.css index 3aba9cad..e7f51eb7 100644 --- a/data/css/style.css +++ b/data/css/style.css @@ -96,6 +96,9 @@ h1{ .updatebar{ text-align: center; } +.version{ + font-size: 12px; + } a:link { color: #5E2612; text-decoration: none; diff --git a/headphones/db.py b/headphones/db.py new file mode 100644 index 00000000..502195dc --- /dev/null +++ b/headphones/db.py @@ -0,0 +1,84 @@ +# Stolen from sick beards db.py. + +from __future__ import with_statement + +import os +import sqlite3 +import threading +import time + +import headphones + +from headphones import logger + +db_lock = threading.Lock() + +def dbFilename(filename="headphones.db"): + + return os.path.join(headphones.DATA_DIR, filename) + +class DBConnection: + + def __init__(self, filename="headphones.db"): + + self.filename = filename + self.connection = sqlite3.connect(dbFilename(filename), 20) + self.connection.row_factory = sqlite3.Row + + def action(self, query, args=None): + + with db_lock: + + if query == None: + return + + sqlResult = None + attempt = 0 + + while attempt < 5: + try: + if args == None: + #logger.debug(self.filename+": "+query) + sqlResult = self.connection.execute(query) + else: + #logger.debug(self.filename+": "+query+" with args "+str(args)) + sqlResult = self.connection.execute(query, args) + self.connection.commit() + break + except sqlite3.OperationalError, e: + if "unable to open database file" in e.message or "database is locked" in e.message: + logger.warn('Database Error: %s' % e) + attempt += 1 + time.sleep(1) + else: + logger.error('Database error: %s' % e) + raise + except sqlite3.DatabaseError, e: + logger.error('Fatal Error executing %s :: %s' % (command, 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())) + ")" + self.action(query, valueDict.values() + keyDict.values()) \ No newline at end of file diff --git a/headphones/helpers.py b/headphones/helpers.py index 560402d1..03bea96c 100644 --- a/headphones/helpers.py +++ b/headphones/helpers.py @@ -1,6 +1,11 @@ +import time +from operator import itemgetter +import datetime + def multikeysort(items, columns): - from operator import itemgetter + comparers = [ ((itemgetter(col[1:].strip()), -1) if col.startswith('-') else (itemgetter(col.strip()), 1)) for col in columns] + def comparer(left, right): for fn, mult in comparers: result = cmp(fn(left), fn(right)) @@ -8,6 +13,7 @@ def multikeysort(items, columns): return mult * result else: return 0 + return sorted(items, cmp=comparer) def checked(variable): @@ -55,4 +61,18 @@ def latinToAscii(unicrap): pass else: r += str(i) - return r \ No newline at end of file + return r + +def convert_milliseconds(ms): + + seconds = ms/1000 + gmtime = time.gmtime(seconds) + + minutes = time.strftime("%M:%S", gmtime) + + return minutes + +def today(): + today = datetime.date.today() + yyyymmdd = datetime.date.isoformat(today) + return yyyymmdd \ No newline at end of file diff --git a/headphones/importer.py b/headphones/importer.py new file mode 100644 index 00000000..67f6f606 --- /dev/null +++ b/headphones/importer.py @@ -0,0 +1,155 @@ +from lib.pyItunes import * +from lib.configobj import ConfigObj +import time +import os +from lib.beets.mediafile import MediaFile + +import headphones +from headphones import logger, helpers, db, mb + +various_artists_mbid = '89ad4ac3-39f7-470e-963a-56509c546377' + +def scanMusic(dir=None): + + if not dir: + dir = headphones.MUSIC_DIR + + results = [] + + for r,d,f in os.walk(unicode(dir)): + for files in f: + if any(files.endswith(x) for x in (".mp3", ".flac", ".aac", ".ogg", ".ape")): + results.append(os.path.join(r,files)) + + logger.info(u'%i music files found' % len(results)) + + if results: + + lst = [] + + myDB = db.DBConnection() + myDB.action('''DELETE from have''') + + for song in results: + try: + f = MediaFile(song) + except: + logger.info("Could not read file: '" + song + "'") + else: + if f.albumartist: + artist = f.albumartist + elif f.artist: + artist = f.artist + else: + continue + + myDB.action('INSERT INTO have VALUES( ?, ?, ?, ?, ?, ?, ?, ?, ?)', [artist, f.album, f.track, f.title, f.length, f.bitrate, f.genre, f.date, f.mb_trackid]) + lst.append(artist) + + artistlist = {}.fromkeys(lst).keys() + logger.info(u"Preparing to import %i artists" % len(artistlist)) + artistlist_to_mbids(artistlist) + +def itunesImport(pathtoxml): + + if os.path.splitext(pathtoxml)[1] == '.xml': + logger.info(u"Loading xml file from"+ pathtoxml) + pl = XMLLibraryParser(pathtoxml) + l = Library(pl.dictionary) + lst = [] + for song in l.songs: + lst.append(song.artist) + rawlist = {}.fromkeys(lst).keys() + artistlist = [f for f in rawlist if f != None] + + else: + rawlist = os.listdir(pathtoxml) + logger.info(u"Loading artists from directory:" +pathtoxml) + exclude = ['.ds_store', 'various artists', 'untitled folder', 'va'] + artistlist = [f for f in rawlist if f.lower() not in exclude] + + artistlist_to_mbids(artistlist) + + +def artistlist_to_mbids(artistlist): + + for artist in artistlist: + + results = mb.findArtist(artist, limit=1) + artistid = results[0]['id'] + if artistid != various_artists_mbid: + addArtisttoDB(artistid) + + +def addArtisttoDB(artistid): + + if artistid == various_artists_mbid: + logger.warn('Cannot import Various Artists.') + return + + myDB = db.DBConnection() + + artistlist = myDB.select('SELECT ArtistID, ArtistName from artists WHERE ArtistID=?', [artistid]) + + if any(artistid in x for x in artistlist): + logger.info(artistlist[0][1] + u" is already in the database, skipping") + return + + artist = mb.getArtist(artistid) + + if artist['artist_name'].startswith('The '): + sortname = artist['artist_name'][4:] + else: + sortname = artist['artist_name'] + + + + controlValueDict = {"ArtistID": artistid} + newValueDict = {"ArtistName": artist['artist_name'], + "ArtistSortName": sortname, + "DateAdded": helpers.today(), + "Status": "Loading"} + + myDB.upsert("artists", newValueDict, controlValueDict) + + for rg in artist['releasegroups']: + + rgid = rg['id'] + + try: + releaseid = mb.getReleaseGroup(rgid) + except Exception, e: + logger.info('Unable to get release information for %s - it may not be a valid release group' % rg['title']) + continue + + release = mb.getRelease(releaseid) + + logger.info(u"Now adding album: " + release['title']+ " to the database") + controlValueDict = {"AlbumID": release['id']} + newValueDict = {"ArtistID": artistid, + "ArtistName": artist['artist_name'], + "AlbumTitle": rg['title'], + "AlbumASIN": release['asin'], + "ReleaseDate": release['date'], + "DateAdded": helpers.today(), + "Status": "Skipped" + } + + myDB.upsert("albums", newValueDict, controlValueDict) + + latestrelease = myDB.select("SELECT ReleaseDate, DateAdded from albums WHERE AlbumID=?", [release['id']]) + + if latestrelease[0][0] > latestrelease[0][1]: + logger.info(release['title'] + u" is an upcoming album. Setting its status to 'Wanted'...") + controlValueDict = {"AlbumID": release['id']} + newValueDict = {"Status": "Wanted"} + myDB.upsert("albums", newValueDict, controlValueDict) + + for track in release['tracks']: + + myDB.action('INSERT INTO tracks VALUES( ?, ?, ?, ?, ?, ?, ?, ?)', [artistid, artist['artist_name'], rg['title'], release['asin'], release['id'], track['title'], track['duration'], track['id']]) + + controlValueDict = {"ArtistID": artistid} + newValueDict = {"Status": "Active"} + + myDB.upsert("artists", newValueDict, controlValueDict) \ No newline at end of file diff --git a/headphones/itunesimport.py b/headphones/itunesimport.py deleted file mode 100644 index 292505eb..00000000 --- a/headphones/itunesimport.py +++ /dev/null @@ -1,133 +0,0 @@ -from lib.pyItunes import * -from lib.configobj import ConfigObj -import lib.musicbrainz2.webservice as ws -import lib.musicbrainz2.model as m -import lib.musicbrainz2.utils as u -from headphones.mb import getReleaseGroup -import string -import time -import os -import sqlite3 -from lib.beets.mediafile import MediaFile - -import headphones -from headphones import logger, helpers - -def scanMusic(dir=None): - - if not dir: - dir = headphones.MUSIC_DIR - - results = [] - - for r,d,f in os.walk(unicode(dir)): - for files in f: - if any(files.endswith(x) for x in (".mp3", ".flac", ".aac", ".ogg", ".ape")): - results.append(os.path.join(r,files)) - - logger.info(u'%i music files found' % len(results)) - - if results: - - lst = [] - - # open db connection to write songs you have - conn=sqlite3.connect(headphones.DB_FILE) - c=conn.cursor() - c.execute('''DELETE from have''') - - for song in results: - try: - f = MediaFile(song) - except: - logger.info("Could not read file: '" + song + "'") - else: - if not f.artist: - pass - else: - c.execute('INSERT INTO have VALUES( ?, ?, ?, ?, ?, ?, ?, ?, ?)', (f.artist, f.album, f.track, f.title, f.length, f.bitrate, f.genre, f.date, f.mb_trackid)) - lst.append(f.artist) - - conn.commit() - c.close() - - artistlist = {}.fromkeys(lst).keys() - logger.info(u"Preparing to import %i artists" % len(artistlist)) - importartist(artistlist) - - - - -def itunesImport(pathtoxml): - if os.path.splitext(pathtoxml)[1] == '.xml': - logger.info(u"Loading xml file from"+ pathtoxml) - pl = XMLLibraryParser(pathtoxml) - l = Library(pl.dictionary) - lst = [] - for song in l.songs: - lst.append(song.artist) - rawlist = {}.fromkeys(lst).keys() - artistlist = [f for f in rawlist if f != None] - importartist(artistlist) - else: - rawlist = os.listdir(pathtoxml) - logger.info(u"Loading artists from directory:" +pathtoxml) - exclude = ['.ds_store', 'various artists', 'untitled folder', 'va'] - artistlist = [f for f in rawlist if f.lower() not in exclude] - importartist(artistlist) - - - -def importartist(artistlist): - for name in artistlist: - logger.info(u"Querying MusicBrainz for: "+name) - artistResults = ws.Query().getArtists(ws.ArtistFilter(string.replace(name, '&', '%38'), limit=1)) - time.sleep(1) - for result in artistResults: - if result.artist.name == 'Various Artists': - logger.info(u"Top result is Various Artists. Skipping.") - else: - logger.info(u"Found best match: "+result.artist.name+". Gathering album information...") - artistid = u.extractUuid(result.artist.id) - inc = ws.ArtistIncludes(releases=(m.Release.TYPE_OFFICIAL, m.Release.TYPE_ALBUM), releaseGroups=True) - artist = ws.Query().getArtistById(artistid, inc) - time.sleep(1) - conn=sqlite3.connect(headphones.DB_FILE) - c=conn.cursor() - c.execute('SELECT ArtistID from artists') - artistlist = c.fetchall() - if any(artistid in x for x in artistlist): - logger.info(result.artist.name + u" is already in the database, skipping") - else: - if artist.name.startswith('The '): - sortname = artist.name[4:] - else: - sortname = artist.name - c.execute('INSERT INTO artists VALUES( ?, ?, ?, CURRENT_DATE, ?)', (artistid, artist.name, sortname, 'Active')) - for rg in artist.getReleaseGroups(): - rgid = u.extractUuid(rg.id) - - releaseid = getReleaseGroup(rgid) - time.sleep(1) - - inc = ws.ReleaseIncludes(artist=True, releaseEvents= True, tracks= True, releaseGroup=True) - results = ws.Query().getReleaseById(releaseid, inc) - time.sleep(1) - logger.info(u"Now adding album: " + results.title+ " to the database") - c.execute('INSERT INTO albums VALUES( ?, ?, ?, ?, ?, CURRENT_DATE, ?, ?)', (artistid, results.artist.name, results.title, results.asin, results.getEarliestReleaseDate(), u.extractUuid(results.id), 'Skipped')) - conn.commit() - c.execute('SELECT ReleaseDate, DateAdded from albums WHERE AlbumID="%s"' % u.extractUuid(results.id)) - - latestrelease = c.fetchall() - - if latestrelease[0][0] > latestrelease[0][1]: - logger.info(results.title + u" is an upcoming album. Setting its status to 'Wanted'...") - c.execute('UPDATE albums SET Status = "Wanted" WHERE AlbumID="%s"' % u.extractUuid(results.id)) - else: - pass - - for track in results.tracks: - c.execute('INSERT INTO tracks VALUES( ?, ?, ?, ?, ?, ?, ?, ?)', (artistid, results.artist.name, results.title, results.asin, u.extractUuid(results.id), track.title, track.duration, u.extractUuid(track.id))) - - conn.commit() - c.close() \ No newline at end of file diff --git a/headphones/logger.py b/headphones/logger.py index f7b1d4b0..f861a4c3 100644 --- a/headphones/logger.py +++ b/headphones/logger.py @@ -37,7 +37,7 @@ class RotatingLogger(object): if not quiet: consolehandler = logging.StreamHandler() - consolehandler.setLevel(logging.DEBUG) + consolehandler.setLevel(logging.INFO) consoleformatter = logging.Formatter('%(asctime)s - %(levelname)s :: %(message)s', '%d-%b-%Y %H:%M:%S') diff --git a/headphones/mb.py b/headphones/mb.py index 3abf5696..f023ff23 100644 --- a/headphones/mb.py +++ b/headphones/mb.py @@ -1,4 +1,7 @@ +from __future__ import with_statement + import time +import threading import lib.musicbrainz2.webservice as ws import lib.musicbrainz2.model as m @@ -6,70 +9,194 @@ import lib.musicbrainz2.utils as u from lib.musicbrainz2.webservice import WebServiceError +from headphones import logger from headphones.helpers import multikeysort q = ws.Query() - +mb_lock = threading.Lock() def findArtist(name, limit=1): - artistlist = [] - - artistResults = q.getArtists(ws.ArtistFilter(name=name, limit=limit)) - - for result in artistResults: - - artistid = u.extractUuid(result.artist.id) - artistlist.append([result.artist.name, artistid]) + with mb_lock: + + artistlist = [] + attempt = 0 - return artistlist + while attempt < 5: + + try: + artistResults = q.getArtists(ws.ArtistFilter(query=name, limit=limit)) + break + except WebServiceError, e: + logger.warn('Attempt to retrieve information from MusicBrainz failed: %s' % e) + attempt += 1 + time.sleep(1) + + time.sleep(1) + + for result in artistResults: + + artistlist.append({ + 'name': result.artist.name, + 'uniquename': result.artist.getUniqueName(), + 'id': u.extractUuid(result.artist.id), + 'url': result.artist.id, + 'score': result.score + }) + + return artistlist def getArtist(artistid): - - rglist = [] - - #Get all official release groups - inc = ws.ArtistIncludes(releases=(m.Release.TYPE_OFFICIAL, m.Release.TYPE_ALBUM), ratings=False, releaseGroups=True) - artist = q.getArtistById(artistid, inc) + with mb_lock: - for rg in artist.getReleaseGroups(): + artist_dict = {} + + #Get all official release groups + inc = ws.ArtistIncludes(releases=(m.Release.TYPE_OFFICIAL, m.Release.TYPE_ALBUM), releaseGroups=True) - rgid = u.extractUuid(rg.id) - rglist.append([rg.title, rgid]) - - return rglist + attempt = 0 + + while attempt < 5: + + try: + artist = q.getArtistById(artistid, inc) + break + except WebServiceError, e: + logger.warn('Attempt to retrieve information from MusicBrainz failed: %s' % e) + attempt += 1 + time.sleep(1) + + time.sleep(1) + + artist_dict['artist_name'] = artist.name + artist_dict['artist_sortname'] = artist.sortName + artist_dict['artist_uniquename'] = artist.getUniqueName() + artist_dict['artist_type'] = u.extractFragment(artist.type) + artist_dict['artist_begindate'] = artist.beginDate + artist_dict['artist_endDate'] = artist.endDate + + releasegroups = [] + + for rg in artist.getReleaseGroups(): + + releasegroups.append({ + 'title': rg.title, + 'id': u.extractUuid(rg.id), + 'url': rg.id, + 'type': u.getReleaseTypeName(rg.type) + }) + + artist_dict['releasegroups'] = releasegroups + + return artist_dict def getReleaseGroup(rgid): - - releaselist = [] + """ + Returns the best release out of any given release group + """ + with mb_lock: - inc = ws.ReleaseGroupIncludes(releases=True) - releaseGroup = q.getReleaseGroupById(rgid, inc) - time.sleep(1) - # I think for now we have to make separate queries for each release, in order - # to get more detailed release info (ASIN, track count, etc.) - for release in releaseGroup.releases: - - releaseid = u.extractUuid(release.id) - inc = ws.ReleaseIncludes(tracks=True) + releaselist = [] + + inc = ws.ReleaseGroupIncludes(releases=True) + + attempt = 0 + + while attempt < 5: + + try: + releaseGroup = q.getReleaseGroupById(rgid, inc) + break + except WebServiceError, e: + logger.warn('Attempt to retrieve information from MusicBrainz failed: %s' % e) + attempt += 1 + time.sleep(1) + + time.sleep(1) + # I think for now we have to make separate queries for each release, in order + # to get more detailed release info (ASIN, track count, etc.) + for release in releaseGroup.releases: + + inc = ws.ReleaseIncludes(tracks=True) + + attempt = 0 + + while attempt < 5: + + try: + releaseResult = q.getReleaseById(release.id, inc) + break + except WebServiceError, e: + logger.warn('Attempt to retrieve information for %s from MusicBrainz failed: %s' % (releaseResult.title, e)) + attempt += 1 + time.sleep(1) + + if not releaseResult: + continue + + time.sleep(1) + + release_dict = { + 'asin': bool(releaseResult.asin), + 'tracks': len(releaseResult.getTracks()), + 'releaseid': u.extractUuid(releaseResult.id) + } + + releaselist.append(release_dict) + + a = multikeysort(releaselist, ['-asin', '-tracks']) + + releaseid = a[0]['releaseid'] + + return releaseid + +def getRelease(releaseid): + """ + Deep release search to get track info + """ + with mb_lock: + + release = {} + + inc = ws.ReleaseIncludes(tracks=True, releaseEvents=True) + + attempt = 0 + + while attempt < 5: + + try: + results = q.getReleaseById(releaseid, inc) + break + except WebServiceError, e: + logger.warn('Attempt to retrieve information from MusicBrainz failed: %s' % e) + attempt += 1 + time.sleep(1) - releaseResult = q.getReleaseById(releaseid, inc) time.sleep(1) - release_dict = { - 'asin': bool(releaseResult.asin), - 'tracks': len(releaseResult.getTracks()), - 'releaseid': u.extractUuid(releaseResult.id) - } + release['title'] = results.title + release['id'] = u.extractUuid(results.id) + release['asin'] = results.asin + release['date'] = results.getEarliestReleaseDate() - releaselist.append(release_dict) - - a = multikeysort(releaselist, ['-asin', '-tracks']) - - releaseid = a[0]['releaseid'] + tracks = [] + + i = 1 + for track in results.tracks: + tracks.append({ + 'number': i, + 'title': track.title, + 'id': u.extractUuid(track.id), + 'url': track.id, + 'duration': track.duration + }) + i += 1 + + release['tracks'] = tracks + + return release - return releaseid def getExtras(artistid): @@ -91,4 +218,4 @@ def getExtras(artistid): print results.title print u.getReleaseTypeName(results.releaseGroup.type) - \ No newline at end of file + \ No newline at end of file diff --git a/headphones/searcher.py b/headphones/searcher.py index 78543132..33abe979 100644 --- a/headphones/searcher.py +++ b/headphones/searcher.py @@ -1,23 +1,19 @@ import urllib import string import lib.feedparser as feedparser -import sqlite3 import os, re import headphones -from headphones import logger +from headphones import logger, db def searchNZB(albumid=None): - conn=sqlite3.connect(headphones.DB_FILE) - c=conn.cursor() + myDB = db.DBConnection() if albumid: - c.execute('SELECT ArtistName, AlbumTitle, AlbumID, ReleaseDate from albums WHERE Status="Wanted" AND AlbumID="%s"' % albumid) + results = myDB.select('SELECT ArtistName, AlbumTitle, AlbumID, ReleaseDate from albums WHERE Status="Wanted" AND AlbumID=?', [albumid]) else: - c.execute('SELECT ArtistName, AlbumTitle, AlbumID, ReleaseDate from albums WHERE Status="Wanted"') - - results = c.fetchall() + results = myDB.select('SELECT ArtistName, AlbumTitle, AlbumID, ReleaseDate from albums WHERE Status="Wanted"') for albums in results: @@ -176,10 +172,9 @@ def searchNZB(albumid=None): logger.error(u"Unable to send link. Are you sure the host address is correct?") break - c.execute('UPDATE albums SET status = "Snatched" WHERE AlbumID="%s"' % albums[2]) - c.execute('INSERT INTO snatched VALUES( ?, ?, ?, ?, CURRENT_DATE, ?)', (albums[2], bestqual[0], bestqual[1], bestqual[2], "Snatched")) - conn.commit() - c.close() + myDB.action('UPDATE albums SET status = "Snatched" WHERE AlbumID=?', [albums[2]]) + myDB.action('INSERT INTO snatched VALUES( ?, ?, ?, ?, CURRENT_DATE, ?)', [albums[2], bestqual[0], bestqual[1], bestqual[2], "Snatched"]) + elif headphones.BLACKHOLE: @@ -192,10 +187,9 @@ def searchNZB(albumid=None): logger.error('Couldn\'t retrieve NZB: %s' % e) break - c.execute('UPDATE albums SET status = "Snatched" WHERE AlbumID="%s"' % albums[2]) - c.execute('INSERT INTO snatched VALUES( ?, ?, ?, ?, CURRENT_DATE, ?)', (albums[2], bestqual[0], bestqual[1], bestqual[2], "Snatched")) - conn.commit() - c.close() + myDB.action('UPDATE albums SET status = "Snatched" WHERE AlbumID=?', [albums[2]]) + myDB.action('INSERT INTO snatched VALUES( ?, ?, ?, ?, CURRENT_DATE, ?)', [albums[2], bestqual[0], bestqual[1], bestqual[2], "Snatched"]) + diff --git a/headphones/templates.py b/headphones/templates.py index 4dabac0d..1871cf7e 100644 --- a/headphones/templates.py +++ b/headphones/templates.py @@ -47,7 +47,7 @@ _footer = ''' - +
Version: %s
''' diff --git a/headphones/updater.py b/headphones/updater.py index 3329d5c6..14bd270c 100644 --- a/headphones/updater.py +++ b/headphones/updater.py @@ -2,20 +2,17 @@ import lib.musicbrainz2.webservice as ws import lib.musicbrainz2.model as m import lib.musicbrainz2.utils as u from headphones.mb import getReleaseGroup -import sqlite3 import time import os import headphones -from headphones import logger +from headphones import logger, db, mb def dbUpdate(): - conn=sqlite3.connect(headphones.DB_FILE) - c=conn.cursor() - c.execute('SELECT ArtistID, ArtistName from artists WHERE Status="Active"') - - activeartists = c.fetchall() + myDB = db.DBConnection() + + activeartists = myDB.select('SELECT ArtistID, ArtistName from artists WHERE Status="Active"') i = 0 @@ -25,53 +22,46 @@ def dbUpdate(): artistname = activeartists[i][1] logger.info(u"Updating album information for artist: " + artistname) - c.execute('SELECT AlbumID from albums WHERE ArtistID="%s"' % artistid) - albumlist = c.fetchall() + artist = mb.getArtist(artistid) - inc = ws.ArtistIncludes(releases=(m.Release.TYPE_OFFICIAL, m.Release.TYPE_ALBUM), releaseGroups=True) - artist = ws.Query().getArtistById(artistid, inc) - time.sleep(1) - - for rg in artist.getReleaseGroups(): + for rg in artist['releasegroups']: + + rgid = rg['id'] + + releaseid = mb.getReleaseGroup(rgid) + + results = mb.getRelease(releaseid) + + albumlist = myDB.select('SELECT AlbumID from albums WHERE ArtistID=?', [artistid]) - rgid = u.extractUuid(rg.id) - releaseid = getReleaseGroup(rgid) - inc = ws.ReleaseIncludes(artist=True, releaseEvents= True, tracks= True, releaseGroup=True) - results = ws.Query().getReleaseById(releaseid, inc) - time.sleep(1) if any(releaseid in x for x in albumlist): - logger.info(results.title + " already exists in the database. Updating ASIN, Release Date, Tracks") + logger.info(results['title'] + " already exists in the database. Updating ASIN, Release Date, Tracks") - c.execute('UPDATE albums SET AlbumASIN="%s", ReleaseDate="%s" WHERE AlbumID="%s"' % (results.asin, results.getEarliestReleaseDate(), u.extractUuid(results.id))) + myDB.action('UPDATE albums SET AlbumASIN=?, ReleaseDate=? WHERE AlbumID=?', [results['asin'], results['date'], results['id']]) - for track in results.tracks: - c.execute('UPDATE tracks SET TrackDuration="%s" WHERE AlbumID="%s" AND TrackID="%s"' % (track.duration, u.extractUuid(results.id), u.extractUuid(track.id))) - conn.commit() + for track in results['tracks']: + + myDB.action('UPDATE tracks SET TrackDuration=? WHERE AlbumID=? AND TrackID=?', [track['duration'], results['id'], track['id']]) + else: - logger.info(u"New album found! Adding "+results.title+"to the database...") - c.execute('INSERT INTO albums VALUES( ?, ?, ?, ?, ?, CURRENT_DATE, ?, ?)', (artistid, results.artist.name, results.title, results.asin, results.getEarliestReleaseDate(), u.extractUuid(results.id), 'Skipped')) - conn.commit() - c.execute('SELECT ReleaseDate, DateAdded from albums WHERE AlbumID="%s"' % u.extractUuid(results.id)) - - latestrelease = c.fetchall() + logger.info(u"New album found! Adding "+results['title']+"to the database...") + + myDB.action('INSERT INTO albums VALUES( ?, ?, ?, ?, ?, CURRENT_DATE, ?, ?)', [artistid, artist['artist_name'], rg['title'], results['asin'], results['date'], results['id'], 'Skipped']) + + latestrelease = myDB.select('SELECT ReleaseDate, DateAdded from albums WHERE AlbumID=?', [results['id']]) if latestrelease[0][0] > latestrelease[0][1]: - c.execute('UPDATE albums SET Status = "Wanted" WHERE AlbumID="%s"' % u.extractUuid(results.id)) + myDB.action('UPDATE albums SET Status = "Wanted" WHERE AlbumID=?', results['id']) else: pass - for track in results.tracks: + for track in results['tracks']: - c.execute('INSERT INTO tracks VALUES( ?, ?, ?, ?, ?, ?, ?, ?)', (artistid, results.artist.name, results.title, results.asin, u.extractUuid(results.id), track.title, track.duration, u.extractUuid(track.id))) - conn.commit() + myDB.action('INSERT INTO tracks VALUES( ?, ?, ?, ?, ?, ?, ?, ?)', [artistid, artist['artist_name'], rg['title'], results['asin'], results['id'], track['title'], track['duration'], track['id']]) i += 1 - - conn.commit() - c.close() - conn.close() diff --git a/headphones/webserve.py b/headphones/webserve.py index 7b756eb9..3f0149b7 100644 --- a/headphones/webserve.py +++ b/headphones/webserve.py @@ -6,15 +6,13 @@ import lib.musicbrainz2.webservice as ws import lib.musicbrainz2.model as m import lib.musicbrainz2.utils as u -import string import time import datetime -import sqlite3 import threading import headphones from headphones.mb import getReleaseGroup -from headphones import templates, logger, searcher +from headphones import templates, logger, searcher, db, importer, helpers, mb from headphones.helpers import checked @@ -34,11 +32,8 @@ class WebInterface(object): ''' % (headphones.CURRENT_VERSION, headphones.LATEST_VERSION, headphones.COMMITS_BEHIND)) page.append(templates._logobar) page.append(templates._nav) - - conn=sqlite3.connect(headphones.DB_FILE) - c=conn.cursor() - c.execute('SELECT ArtistName, ArtistID, Status from artists order by ArtistSortName collate nocase') - results = c.fetchall() + myDB = db.DBConnection() + results = myDB.select('SELECT ArtistName, ArtistID, Status from artists order by ArtistSortName collate nocase') if len(results): i = 0 page.append('''
@@ -49,14 +44,11 @@ class WebInterface(object): ''') while i < len(results): - c.execute('''SELECT AlbumTitle, ReleaseDate, DateAdded, AlbumID from albums WHERE ArtistID='%s' order by ReleaseDate DESC''' % results[i][1]) - latestalbum = c.fetchall() - c.execute('''SELECT TrackTitle from tracks WHERE ArtistID="%s"''' % results[i][1]) - totaltracks = len(c.fetchall()) - c.execute('''SELECT TrackTitle from have WHERE ArtistName like "%s"''' % results[i][0]) - havetracks = len(c.fetchall()) + latestalbum = myDB.select('SELECT AlbumTitle, ReleaseDate, DateAdded, AlbumID from albums WHERE ArtistID=? order by ReleaseDate DESC', [results[i][1]]) + totaltracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID=?', [results[i][1]])) + havetracks = len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ?', [results[i][0]])) try: - percent = (havetracks*100)/totaltracks + percent = (havetracks*100.0)/totaltracks if percent > 100: percent = 100 except ZeroDivisionError: @@ -74,6 +66,8 @@ class WebInterface(object): releaseDate = "" if results[i][2] == 'Paused': newStatus = '''%s(resume)''' % (results[i][2], results[i][1]) + elif results[i][2] == 'Loading': + newStatus = '''Loading...''' else: newStatus = '''%s(pause)''' % (results[i][2], results[i][1]) page.append(''' ''' % (results[i][1], results[i][0], results[i][1], results[i][1], newStatus, newalbumName, releaseDate, percent)) i = i+1 - c.close() + page.append('''
Have
%s @@ -83,9 +77,9 @@ class WebInterface(object):
''') - page.append(templates._footer) + page.append(templates._footer % headphones.CURRENT_VERSION) else: page.append("""
Add some artists to the database!
""") @@ -97,12 +91,10 @@ class WebInterface(object): page = [templates._header] page.append(templates._logobar) page.append(templates._nav) - conn=sqlite3.connect(headphones.DB_FILE) - c=conn.cursor() - c.execute('''SELECT ArtistName from artists WHERE ArtistID="%s"''' % ArtistID) - artistname = c.fetchall() - c.execute('''SELECT AlbumTitle, ReleaseDate, AlbumID, Status, ArtistName, AlbumASIN from albums WHERE ArtistID="%s" order by ReleaseDate DESC''' % ArtistID) - results = c.fetchall() + myDB = db.DBConnection() + + results = myDB.select('SELECT AlbumTitle, ReleaseDate, AlbumID, Status, ArtistName, AlbumASIN from albums WHERE ArtistID=? order by ReleaseDate DESC', [ArtistID]) + i = 0 page.append('''

%s

@@ -112,12 +104,10 @@ class WebInterface(object): - ''' % (artistname[0])) + ''' % (results[0][4])) while i < len(results): - c.execute('''SELECT TrackTitle from tracks WHERE AlbumID="%s"''' % results[i][2]) - totaltracks = len(c.fetchall()) - c.execute('''SELECT TrackTitle from have WHERE ArtistName like ? AND AlbumTitle like ?''', (results[i][4], results[i][0])) - havetracks = len(c.fetchall()) + totaltracks = len(myDB.select('SELECT TrackTitle from tracks WHERE AlbumID=?', [results[i][2]])) + havetracks = len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ? AND AlbumTitle like ?', [results[i][4], results[i][0]])) try: percent = (havetracks*100)/totaltracks if percent > 100: @@ -141,9 +131,9 @@ class WebInterface(object): ''' % (results[i][5], results[i][2], results[i][0], results[i][2], results[i][1], newStatus, percent)) i = i+1 - c.close() + page.append('''
Release Date Status Have
%s
''') - page.append(templates._footer) + page.append(templates._footer % headphones.CURRENT_VERSION) return page artistPage.exposed = True @@ -152,10 +142,8 @@ class WebInterface(object): page = [templates._header] page.append(templates._logobar) page.append(templates._nav) - conn=sqlite3.connect(headphones.DB_FILE) - c=conn.cursor() - c.execute('''SELECT ArtistID, ArtistName, AlbumTitle, TrackTitle, TrackDuration, TrackID, AlbumASIN from tracks WHERE AlbumID="%s"''' % AlbumID) - results = c.fetchall() + myDB = db.DBConnection() + results = myDB.select('SELECT ArtistID, ArtistName, AlbumTitle, TrackTitle, TrackDuration, TrackID, AlbumASIN from tracks WHERE AlbumID=?', [AlbumID]) if results[0][6]: albumart = '''


''' % results[0][6] @@ -172,14 +160,14 @@ class WebInterface(object): ''' % (results[0][0], results[0][1], results[0][2], AlbumID, results[0][0], albumart)) while i < len(results): - c.execute('''SELECT TrackTitle from have WHERE ArtistName like ? AND AlbumTitle like ? AND TrackTitle like ?''', (results[i][1], results[i][2], results[i][3])) - trackmatches = c.fetchall() + trackmatches = myDB.select('SELECT TrackTitle from have WHERE ArtistName like ? AND AlbumTitle like ? AND TrackTitle like ?', [results[i][1], results[i][2], results[i][3]]) + if len(trackmatches): have = '' else: have = '' if results[i][4]: - duration = time.strftime("%M:%S", time.gmtime(int(results[i][4])/1000)) + duration = helpers.convert_milliseconds(results[i][4]) else: duration = 'n/a' page.append('''%s @@ -187,9 +175,9 @@ class WebInterface(object): %s %s''' % (i+1, results[i][3], results[i][5], duration, have)) i = i+1 - c.close() + page.append('''''') - page.append(templates._footer) + page.append(templates._footer % headphones.CURRENT_VERSION) return page albumPage.exposed = True @@ -203,7 +191,7 @@ class WebInterface(object): if len(name) == 0 or name == 'Add an artist': raise cherrypy.HTTPRedirect("home") else: - artistResults = ws.Query().getArtists(ws.ArtistFilter(string.replace(name, '&', '%38'), limit=8)) + artistResults = mb.findArtist(name, limit=10) if len(artistResults) == 0: logger.info(u"No results found for " + name) page.append('''

No results! Go back

''') @@ -211,20 +199,13 @@ class WebInterface(object): elif len(artistResults) > 1: page.append('''

Search returned multiple artists. Click the artist you want to add:

''') for result in artistResults: - artist = result.artist - detail = artist.getDisambiguation() - if detail: - disambiguation = '(%s)' % detail - else: - disambiguation = '' - page.append('''

%s %s (more info)

''' % (u.extractUuid(artist.id), artist.name, disambiguation, u.extractUuid(artist.id))) + page.append('''

%s (more info)

''' % (result['id'], result['uniquename'], result['id'])) page.append('''
''') return page else: for result in artistResults: - artist = result.artist - logger.info(u"Found one artist matching your search term: " + artist.name +" ("+ artist.id+")") - raise cherrypy.HTTPRedirect("addArtist?artistid=%s" % u.extractUuid(artist.id)) + logger.info(u"Found one artist matching your search term: " + result['name'] +" ("+ result['id']+")") + raise cherrypy.HTTPRedirect("addArtist?artistid=%s" % result['id']) findArtist.exposed = True @@ -232,123 +213,83 @@ class WebInterface(object): page = [templates._header] page.append(templates._logobar) page.append(templates._nav) - inc = ws.ArtistIncludes(releases=(m.Release.TYPE_OFFICIAL, m.Release.TYPE_ALBUM), releaseGroups=True) - artist = ws.Query().getArtistById(artistid, inc) - page.append('''Artist Name: %s
''' % artist.name) - page.append('''Unique ID: %s

Albums:
''' % u.extractUuid(artist.id)) - for rg in artist.getReleaseGroups(): - page.append('''%s
''' % rg.title) + artist = mb.getArtist(artistid) + page.append('''

Artist Information:

''') + page.append('''

Artist Name: %s
''' % artist['artist_name']) + page.append('''Unique ID: %s

Albums:
''' % artist['artist_id']) + for rg in artist['releasegroups']: + page.append('''%s
''' % rg['title']) return page artistInfo.exposed = True def addArtist(self, artistid): - inc = ws.ArtistIncludes(releases=(m.Release.TYPE_OFFICIAL, m.Release.TYPE_ALBUM), releaseGroups=True) - artist = ws.Query().getArtistById(artistid, inc) - time.sleep(1) - conn=sqlite3.connect(headphones.DB_FILE) - c=conn.cursor() - c.execute('SELECT ArtistID from artists') - artistlist = c.fetchall() - if any(artistid in x for x in artistlist): - page = [templates._header] - page.append - page.append('''%s has already been added. Go back.''' % artist.name) - logger.info(artist.name + u" is already in the database!") - c.close() - return page - else: - logger.info(u"Adding " + artist.name + " to the database.") - if artist.name.startswith('The '): - sortname = artist.name[4:] - else: - sortname = artist.name - c.execute('INSERT INTO artists VALUES( ?, ?, ?, CURRENT_DATE, ?)', (artistid, artist.name, sortname, 'Active')) - for rg in artist.getReleaseGroups(): - rgid = u.extractUuid(rg.id) - - releaseid = getReleaseGroup(rgid) - - inc = ws.ReleaseIncludes(artist=True, releaseEvents= True, tracks= True, releaseGroup=True) - results = ws.Query().getReleaseById(releaseid, inc) - time.sleep(1) - logger.info(u"Now adding album: " + results.title+ " to the database") - c.execute('INSERT INTO albums VALUES( ?, ?, ?, ?, ?, CURRENT_DATE, ?, ?)', (artistid, results.artist.name, results.title, results.asin, results.getEarliestReleaseDate(), u.extractUuid(results.id), 'Skipped')) - c.execute('SELECT ReleaseDate, DateAdded from albums WHERE AlbumID="%s"' % u.extractUuid(results.id)) - latestrelease = c.fetchall() - - if latestrelease[0][0] > latestrelease[0][1]: - logger.info(results.title + u" is an upcoming album. Setting its status to 'Wanted'...") - c.execute('UPDATE albums SET Status = "Wanted" WHERE AlbumID="%s"' % u.extractUuid(results.id)) - else: - pass - - for track in results.tracks: - c.execute('INSERT INTO tracks VALUES( ?, ?, ?, ?, ?, ?, ?, ?)', (artistid, results.artist.name, results.title, results.asin, u.extractUuid(results.id), track.title, track.duration, u.extractUuid(track.id))) - - conn.commit() - c.close() - raise cherrypy.HTTPRedirect("home") + threading.Thread(target=importer.addArtisttoDB, args=[artistid]).start() + time.sleep(2) + raise cherrypy.HTTPRedirect("home") addArtist.exposed = True def pauseArtist(self, ArtistID): - conn=sqlite3.connect(headphones.DB_FILE) - c=conn.cursor() logger.info(u"Pausing artist: " + ArtistID) - c.execute('UPDATE artists SET status = "Paused" WHERE ArtistId="%s"' % ArtistID) - conn.commit() - c.close() + myDB = db.DBConnection() + controlValueDict = {'ArtistID': ArtistID} + newValueDict = {'Status': 'Paused'} + myDB.upsert("artists", newValueDict, controlValueDict) + raise cherrypy.HTTPRedirect("home") pauseArtist.exposed = True def resumeArtist(self, ArtistID): - conn=sqlite3.connect(headphones.DB_FILE) - c=conn.cursor() + logger.info(u"Resuming artist: " + ArtistID) - c.execute('UPDATE artists SET status = "Active" WHERE ArtistId="%s"' % ArtistID) - conn.commit() - c.close() + myDB = db.DBConnection() + controlValueDict = {'ArtistID': ArtistID} + newValueDict = {'Status': 'Active'} + myDB.upsert("artists", newValueDict, controlValueDict) + raise cherrypy.HTTPRedirect("home") resumeArtist.exposed = True def deleteArtist(self, ArtistID): - conn=sqlite3.connect(headphones.DB_FILE) - c=conn.cursor() + logger.info(u"Deleting all traces of artist: " + ArtistID) - c.execute('''DELETE from artists WHERE ArtistID="%s"''' % ArtistID) - c.execute('''DELETE from albums WHERE ArtistID="%s"''' % ArtistID) - c.execute('''DELETE from tracks WHERE ArtistID="%s"''' % ArtistID) - conn.commit() - c.close() + myDB = db.DBConnection() + myDB.action('DELETE from artists WHERE ArtistID=?', [ArtistID]) + myDB.action('DELETE from albums WHERE ArtistID=?', [ArtistID]) + myDB.action('DELETE from tracks WHERE ArtistID=?', [ArtistID]) + raise cherrypy.HTTPRedirect("home") deleteArtist.exposed = True def queueAlbum(self, AlbumID, ArtistID): - conn=sqlite3.connect(headphones.DB_FILE) - c=conn.cursor() + logger.info(u"Marking album: " + AlbumID + "as wanted...") - c.execute('UPDATE albums SET status = "Wanted" WHERE AlbumID="%s"' % AlbumID) - conn.commit() - c.close() + myDB = db.DBConnection() + controlValueDict = {'AlbumID': AlbumID} + newValueDict = {'Status': 'Wanted'} + myDB.upsert("albums", newValueDict, controlValueDict) + import searcher searcher.searchNZB(AlbumID) + raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID) queueAlbum.exposed = True def unqueueAlbum(self, AlbumID, ArtistID): - conn=sqlite3.connect(headphones.DB_FILE) - c=conn.cursor() + logger.info(u"Marking album: " + AlbumID + "as skipped...") - c.execute('UPDATE albums SET status = "Skipped" WHERE AlbumID="%s"' % AlbumID) - conn.commit() - c.close() + myDB = db.DBConnection() + controlValueDict = {'AlbumID': AlbumID} + newValueDict = {'Status': 'Skipped'} + myDB.upsert("albums", newValueDict, controlValueDict) + raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID) unqueueAlbum.exposed = True @@ -357,14 +298,11 @@ class WebInterface(object): page = [templates._header] page.append(templates._logobar) page.append(templates._nav) - today = datetime.date.today() - todaysql = datetime.date.isoformat(today) - conn=sqlite3.connect(headphones.DB_FILE) - c=conn.cursor() - c.execute('''SELECT AlbumTitle, ReleaseDate, DateAdded, AlbumASIN, AlbumID, ArtistName, ArtistID from albums WHERE ReleaseDate > date('now') order by ReleaseDate DESC''') - albums = c.fetchall() - c.execute('''SELECT AlbumTitle, ReleaseDate, DateAdded, AlbumASIN, AlbumID, ArtistName, ArtistID from albums WHERE Status="Wanted"''') - wanted = c.fetchall() + myDB = db.DBConnection() + albums = myDB.select("SELECT AlbumTitle, ReleaseDate, DateAdded, AlbumASIN, AlbumID, ArtistName, ArtistID from albums WHERE ReleaseDate > date('now') order by ReleaseDate DESC") + + wanted = myDB.select("SELECT AlbumTitle, ReleaseDate, DateAdded, AlbumASIN, AlbumID, ArtistName, ArtistID from albums WHERE Status='Wanted'") + page.append('''

@@ -413,7 +351,7 @@ class WebInterface(object): i += 1 page.append('''
''') if len(albums): - page.append(templates._footer) + page.append(templates._footer % headphones.CURRENT_VERSION) return page upcoming.exposed = True @@ -455,38 +393,40 @@ class WebInterface(object): Force Check for Wanted Albums

Force Update Active Artists

Check for Headphones Updates


''' % (path2, path)) - page.append(templates._footer) + page.append(templates._footer % headphones.CURRENT_VERSION) return page manage.exposed = True def importItunes(self, path): headphones.PATH_TO_XML = path headphones.config_write() - from headphones import itunesimport - itunesimport.itunesImport(path) + threading.Thread(target=importer.itunesImport, args=[path]).start() + time.sleep(10) raise cherrypy.HTTPRedirect("home") importItunes.exposed = True def musicScan(self, path): - from headphones import itunesimport headphones.MUSIC_DIR = path headphones.config_write() try: - itunesimport.scanMusic(path) + threading.Thread(target=importer.scanMusic, args=[path]).start() except Exception, e: logger.error('Unable to complete the scan: %s' % e) + time.sleep(10) raise cherrypy.HTTPRedirect("home") musicScan.exposed = True def forceUpdate(self): from headphones import updater - updater.dbUpdate() + threading.Thread(target=updater.dbUpdate).start() + time.sleep(5) raise cherrypy.HTTPRedirect("home") forceUpdate.exposed = True def forceSearch(self): from headphones import searcher - searcher.searchNZB() + threading.Thread(target=searcher.searchNZB).start() + time.sleep(5) raise cherrypy.HTTPRedirect("home") forceSearch.exposed = True @@ -500,10 +440,9 @@ class WebInterface(object): page = [templates._header] page.append(templates._logobar) page.append(templates._nav) - conn=sqlite3.connect(headphones.DB_FILE) - c=conn.cursor() - c.execute('''SELECT AlbumID, Title TEXT, Size INTEGER, URL TEXT, DateAdded TEXT, Status TEXT from snatched order by DateAdded DESC''') - snatched = c.fetchall() + myDB = db.DBConnection() + snatched = myDB.select('''SELECT AlbumID, Title TEXT, Size INTEGER, URL TEXT, DateAdded TEXT, Status TEXT from snatched order by DateAdded DESC''') + page.append('''
@@ -527,17 +466,16 @@ class WebInterface(object): i += 1 page.append('''
''') if len(snatched): - page.append(templates._footer) + page.append(templates._footer % headphones.CURRENT_VERSION) return page history.exposed = True def clearhistory(self): - conn=sqlite3.connect(headphones.DB_FILE) - c=conn.cursor() + logger.info(u"Clearing history") - c.execute('''DELETE from snatched''') - conn.commit() - c.close() + myDB = db.DBConnection() + myDB.action('''DELETE from snatched''') + raise cherrypy.HTTPRedirect("history") clearhistory.exposed = True @@ -577,7 +515,7 @@ class WebInterface(object): checked(headphones.CLEANUP_FILES), checked(headphones.ADD_ALBUM_ART) )) - #page.append(templates._footer) + page.append(templates._footer % headphones.CURRENT_VERSION) return page config.exposed = True diff --git a/headphones/webstart.py b/headphones/webstart.py index f8b3c2e7..474676b2 100644 --- a/headphones/webstart.py +++ b/headphones/webstart.py @@ -33,6 +33,10 @@ def initialize(options={}): '/js':{ 'tools.staticdir.on': True, 'tools.staticdir.dir': "js" + }, + '/favicon.ico':{ + 'tools.staticfile.on': True, + 'tools.staticfile.filename': "images/favicon.ico" } } diff --git a/lib/beets/mediafile.py b/lib/beets/mediafile.py index 77108be4..588434f2 100644 --- a/lib/beets/mediafile.py +++ b/lib/beets/mediafile.py @@ -788,7 +788,7 @@ class MediaFile(object): etc = StorageStyle('compilation') ) albumartist = MediaField( - mp3 = StorageStyle('TXXX', id3_desc=u'Album Artist'), + mp3 = StorageStyle('TPE2'), mp4 = StorageStyle( '----:com.apple.iTunes:Album Artist', as_type=str), diff --git a/lib/musicbrainz2/utils.py b/lib/musicbrainz2/utils.py index 0eff7be8..644ad9fe 100644 --- a/lib/musicbrainz2/utils.py +++ b/lib/musicbrainz2/utils.py @@ -141,7 +141,7 @@ def getReleaseTypeName(releaseType): @see: L{musicbrainz2.model.Release} """ - from musicbrainz2.data.releasetypenames import releaseTypeNames + from lib.musicbrainz2.data.releasetypenames import releaseTypeNames return releaseTypeNames.get(releaseType)