From 22aef717d153c7277270d6e6273675794171f8fe Mon Sep 17 00:00:00 2001 From: Remy Date: Mon, 15 Aug 2011 02:32:32 -0700 Subject: [PATCH] Added librarysync, added bitrate and location to db and album template --- data/css/style.css | 8 +- data/interfaces/default/album.html | 19 ++-- data/interfaces/remix/album.html | 25 +++-- headphones/__init__.py | 11 +- headphones/helpers.py | 33 +++++- headphones/librarysync.py | 160 +++++++++++++++++++++++++++++ 6 files changed, 231 insertions(+), 25 deletions(-) create mode 100644 headphones/librarysync.py diff --git a/data/css/style.css b/data/css/style.css index 552c9a5b..b7023a7f 100755 --- a/data/css/style.css +++ b/data/css/style.css @@ -173,17 +173,19 @@ table#album_table td#have { vertical-align: middle; } img.albumArt { float: left; padding-right: 5px; } div#albumheader { padding-top: 48px; height: 200px; } -div#track_wrapper { padding-top: 20px; text-align: center; font-size: 16px; } +div#track_wrapper { margin-left: -50px; padding-top: 20px; font-size: 16px; width: 100%; } table#track_table th#number { text-align: right; min-width: 20px; } table#track_table th#name { text-align: center; min-width: 350px; } table#track_table th#duration { width: 175px; text-align: center; min-width: 100px; } -table#track_table th#have { width: 175px; text-align: center; min-width: 100px; } +table#track_table th#location { text-align: center; min-width: 200px; } +table#track_table th#bitrate { text-align: center; min-width: 75px; } table#track_table td#number { vertical-align: middle; text-align: right; } table#track_table td#name { vertical-align: middle; text-align: center; } table#track_table td#duration { vertical-align: middle; text-align: center; } -table#track_table td#have { vertical-align: middle; text-align: center; } +table#track_table td#location { vertical-align: middle; text-align: center; font-size: 11px; } +table#track_table td#location { vertical-align: middle; text-align: center; } table#history_table { background-color: white; width: 100%; } diff --git a/data/interfaces/default/album.html b/data/interfaces/default/album.html index da7fc4f8..355dd9b3 100644 --- a/data/interfaces/default/album.html +++ b/data/interfaces/default/album.html @@ -51,20 +51,24 @@ # Track Title Duration - + Local File + Bit Rate - <% - i = 0 - %> %for track in tracks: <% - i += 1 if track['Location']: grade = 'A' + location = track['Location'] else: grade = 'Z' + location = '' + + if track['BitRate']: + bitrate = str(track['BitRate']/1000) + ' kbps' + else: + bitrate = '' try: trackduration = helpers.convert_milliseconds(track['TrackDuration']) @@ -72,10 +76,11 @@ trackduration = 'n/a' %> - ${i} + ${track['TrackNumber']} ${track['TrackTitle']} ${trackduration} - ${track['Location']} + ${location} + ${bitrate} %endfor diff --git a/data/interfaces/remix/album.html b/data/interfaces/remix/album.html index da287746..355dd9b3 100644 --- a/data/interfaces/remix/album.html +++ b/data/interfaces/remix/album.html @@ -51,33 +51,36 @@ # Track Title Duration - + Local File + Bit Rate - <% - i = 0 - %> %for track in tracks: <% - i += 1 - have = myDB.select('SELECT TrackTitle from have WHERE ArtistName like ? AND AlbumTitle like ? AND TrackTitle like ?', [track['ArtistName'], track['AlbumTitle'], track['TrackTitle']]) - if len(have): + if track['Location']: grade = 'A' - check = 'checkmark' + location = track['Location'] else: grade = 'Z' - check = '' + location = '' + + if track['BitRate']: + bitrate = str(track['BitRate']/1000) + ' kbps' + else: + bitrate = '' + try: trackduration = helpers.convert_milliseconds(track['TrackDuration']) except: trackduration = 'n/a' %> - ${i} + ${track['TrackNumber']} ${track['TrackTitle']} ${trackduration} - ${check} + ${location} + ${bitrate} %endfor diff --git a/headphones/__init__.py b/headphones/__init__.py index ca776d30..1454fb79 100644 --- a/headphones/__init__.py +++ b/headphones/__init__.py @@ -11,7 +11,7 @@ from lib.configobj import ConfigObj import cherrypy -from headphones import updater, searcher, importer, versioncheck, logger, postprocessor, version, sab +from headphones import updater, searcher, importer, versioncheck, logger, postprocessor, version, sab, librarysync from headphones.common import * FULL_PATH = None @@ -439,7 +439,7 @@ def dbcheck(): c=conn.cursor() c.execute('CREATE TABLE IF NOT EXISTS artists (ArtistID TEXT UNIQUE, ArtistName TEXT, ArtistSortName TEXT, DateAdded TEXT, Status TEXT, IncludeExtras INTEGER, LatestAlbum TEXT, ReleaseDate TEXT, AlbumID TEXT, HaveTracks INTEGER, TotalTracks INTEGER)') c.execute('CREATE TABLE IF NOT EXISTS albums (ArtistID TEXT, ArtistName TEXT, AlbumTitle TEXT, AlbumASIN TEXT, ReleaseDate TEXT, DateAdded TEXT, AlbumID TEXT UNIQUE, Status TEXT, Type TEXT)') - c.execute('CREATE TABLE IF NOT EXISTS tracks (ArtistID TEXT, ArtistName TEXT, AlbumTitle TEXT, AlbumASIN TEXT, AlbumID TEXT, TrackTitle TEXT, TrackDuration, TrackID TEXT, TrackNumber INTEGER, Location TEXT)') + c.execute('CREATE TABLE IF NOT EXISTS tracks (ArtistID TEXT, ArtistName TEXT, AlbumTitle TEXT, AlbumASIN TEXT, AlbumID TEXT, TrackTitle TEXT, TrackDuration, TrackID TEXT, TrackNumber INTEGER, Location TEXT, BitRate INTEGER)') c.execute('CREATE TABLE IF NOT EXISTS snatched (AlbumID TEXT, Title TEXT, Size INTEGER, URL TEXT, DateAdded TEXT, Status TEXT, FolderName TEXT)') c.execute('CREATE TABLE IF NOT EXISTS have (ArtistName TEXT, AlbumTitle TEXT, TrackNumber TEXT, TrackTitle TEXT, TrackLength TEXT, BitRate TEXT, Genre TEXT, Date TEXT, TrackID TEXT)') c.execute('CREATE TABLE IF NOT EXISTS lastfmcloud (ArtistName TEXT, ArtistID TEXT, Count INTEGER)') @@ -494,7 +494,12 @@ def dbcheck(): try: c.execute('SELECT Location from tracks') except sqlite3.OperationalError: - c.execute('ALTER TABLE tracks ADD COLUMN Location TEXT') + c.execute('ALTER TABLE tracks ADD COLUMN Location TEXT') + + try: + c.execute('SELECT BitRate from tracks') + except sqlite3.OperationalError: + c.execute('ALTER TABLE tracks ADD COLUMN BitRate INTEGER') conn.commit() c.close() diff --git a/headphones/helpers.py b/headphones/helpers.py index 4ecc6941..c3246542 100644 --- a/headphones/helpers.py +++ b/headphones/helpers.py @@ -143,4 +143,35 @@ def extract_logline(s): message = match.group("message") return (timestamp, level, thread, message) else: - return None \ No newline at end of file + return None + +def extract_song_data(s): + + #headphones default format + music_dir = headphones.MUSIC_DIR + folder_format = headphones.FOLDER_FORMAT + file_format = headphones.FILE_FORMAT + + full_format = os.path.join(headphones.MUSIC_DIR) + pattern = re.compile(r'(?P.*?)\s\-\s(?P.*?)\s\[(?P.*?)\]', re.VERBOSE) + match = pattern.match(s) + + if match: + name = match.group("name") + album = match.group("album") + year = match.group("year") + return (name, album, year) + else: + logger.info("Couldn't parse " + s + " into a valid default format") + + #newzbin default format + pattern = re.compile(r'(?P.*?)\s\-\s(?P.*?)\s\((?P\d+?\))', re.VERBOSE) + match = pattern.match(s) + if match: + name = match.group("name") + album = match.group("album") + year = match.group("year") + return (name, album, year) + else: + logger.info("Couldn't parse " + s + " into a valid Newbin format") + return (name, album, year) \ No newline at end of file diff --git a/headphones/librarysync.py b/headphones/librarysync.py new file mode 100644 index 00000000..8a093b04 --- /dev/null +++ b/headphones/librarysync.py @@ -0,0 +1,160 @@ +import os +import glob + +from lib.beets.mediafile import MediaFile + +import headphones +from headphones import db, logger + +def LibraryScan(): + + if not headphones.MUSIC_DIR: + return + + unmatched_files = [] + new_artists = [] + + myDB = db.DBConnection() + + for r,d,f in os.walk(headphones.MUSIC_DIR): + for files in f: + # MEDIA_FORMATS = music file extensions, e.g. mp3, flac, etc + if any(files.endswith('.' + x) for x in headphones.MEDIA_FORMATS): + + file = unicode(os.path.join(r, files), "utf-8") + print repr(file) + # Try to read the metadata + try: + f = MediaFile(file) + except: + logger.error('Cannot read file: ' + file) + continue + + # Try to match on metadata first, + if f.mb_trackid: + print 'has track id:' + repr(f.mb_trackid) + # Wondering if theres a better way to do this -> do one thing if the row exists, + # do something else if it doesn't + track = myDB.action('SELECT TrackID from tracks WHERE TrackID=?', [f.mb_trackid]).fetchone() + + if not track: + if f.albumartist: + new_artists.append(f.albumartist) + elif f.artist: + new_artists.append(f.artist) + else: + unmatched_files.append(file) + + else: + myDB.action('UPDATE tracks SET Location=?, BitRate=? WHERE TrackID=?', [file, f.bitrate, track['TrackID']]) + continue + + elif f.albumartist and f.album and f.title: + + track = myDB.action('SELECT TrackID from tracks WHERE ArtistName LIKE ? AND AlbumTitle LIKE ? AND TrackTitle LIKE ?', [f.albumartist, f.album, f.title]).fetchone() + + if not track: + new_artists.append(f.albumartist) + + else: + myDB.action('UPDATE tracks SET Location=?, BitRate=? WHERE TrackID=?', [file, f.bitrate, track['TrackID']]) + + elif f.artist and f.album and f.title: + + track = myDB.action('SELECT TrackID from tracks WHERE ArtistName LIKE ? AND AlbumTitle LIKE ? AND TrackTitle LIKE ?', [f.artist, f.album, f.title]).fetchone() + + if not track: + new_artists.append(file) + else: + myDB.action('UPDATE tracks SET Location=?, BitRate=? WHERE TrackID=?', [file, f.bitrate, track['TrackID']]) + + else: + continue + + # Try to parse the unmatched files based on the folder & file formats in the config + for file in unmatched_files: + # Will do later + pass + + # Now check empty file paths to see if we can find a match based on their folder format + tracks = myDB.select('SELECT * from tracks WHERE Location=NULL') + for track in tracks: + release = myDB.action('SELECT * from albums WHERE AlbumID=?', [track['AlbumID']]).fetchone() + + try: + year = release['ReleaseDate'][:4] + except TypeError: + year = '' + + artist = release['ArtistName'].replace('/', '_') + album = release['AlbumTitle'].replace('/', '_') + + if release['ArtistName'].startswith('The '): + sortname = release['ArtistName'][4:] + else: + sortname = release['ArtistName'] + + if sortname.isdigit(): + firstchar = '0-9' + else: + firstchar = sortname[0] + + + albumvalues = { 'artist': artist, + 'album': album, + 'year': year, + 'first': firstchar, + } + + + folder = helpers.replace_all(headphones.FOLDER_FORMAT, albumvalues) + folder = folder.replace('./', '_/').replace(':','_').replace('?','_') + + if folder.endswith('.'): + folder = folder.replace(folder[len(folder)-1], '_') + + if not track['TrackNumber']: + tracknumber = '' + else: + tracknumber = '%02d' % track['TrackNumber'] + + trackvalues = { 'tracknumber': tracknumber, + 'title': track['TrackTitle'], + 'artist': release['ArtistName'], + 'album': release['AlbumTitle'], + 'year': year + } + + new_file_name = helpers.replace_all(headphones.FILE_FORMAT, trackvalues).replace('/','_') + '.*' + + new_file_name = new_file_name.replace('?','_').replace(':', '_') + + full_path_to_file = os.path.join(headphones.MUSIC_DIR, folder, new_file_name) + + print 'Full path to file is: ' + repr(full_path_to_file) + + if glob.glob(full_path_to_file): + myDB.action('UPDATE tracks SET Location=? WHERE TrackID=?', [full_path_to_file, track['TrackID']]) + + # Try to insert the appropriate track id so we don't have to keep doing this + try: + f = MediaFile(full_path_to_file) + f.mb_trackid = track['TrackID'] + myDB.action('UPDATE tracks SET BitRate=? WHERE TrackID=?', [f.bitrate, track['TrackID']]) + f.save() + except: + logger.error('Error embedding track id into: %s' % full_path_to_file) + + # Lastly, clean up any old paths that don't exist + tracks = myDB.select('SELECT Location, TrackID from tracks WHERE Location IS NOT NULL') + for track in tracks: + if not os.path.isfile(track['Location']): + myDB.action('UPDATE tracks SET Location=? WHERE TrackID=?', [None, track['TrackID']]) + + # Clean up the new artist list + unique_artists = {}.fromkeys(new_artists).keys() + current_artists = myDB.action('SELECT ArtistName from artists').fetchall() + + artist_list = [f for f in unique_artists if f not in current_artists] + + logger.info('Found %i artists to import: ' % len(artist_list)) \ No newline at end of file