diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html
index 75219f33..643af20e 100644
--- a/data/interfaces/default/config.html
+++ b/data/interfaces/default/config.html
@@ -618,7 +618,10 @@ m<%inherit file="base.html"/>
diff --git a/headphones/__init__.py b/headphones/__init__.py
index 3126e793..0e06ba4c 100644
--- a/headphones/__init__.py
+++ b/headphones/__init__.py
@@ -183,6 +183,7 @@ XBMC_NOTIFY = False
NMA_ENABLED = False
NMA_APIKEY = None
NMA_PRIORITY = None
+NMA_ONSNATCH = None
SYNOINDEX_ENABLED = False
MIRRORLIST = ["musicbrainz.org","headphones","custom"]
MIRROR = None
@@ -254,7 +255,7 @@ def initialize():
ENCODERFOLDER, ENCODER, BITRATE, SAMPLINGFREQUENCY, MUSIC_ENCODER, ADVANCEDENCODER, ENCODEROUTPUTFORMAT, ENCODERQUALITY, \
ENCODERVBRCBR, ENCODERLOSSLESS, DELETE_LOSSLESS_FILES, PROWL_ENABLED, PROWL_PRIORITY, PROWL_KEYS, PROWL_ONSNATCH, MIRRORLIST, \
MIRROR, CUSTOMHOST, CUSTOMPORT, CUSTOMSLEEP, HPUSER, HPPASS, XBMC_ENABLED, XBMC_HOST, XBMC_USERNAME, XBMC_PASSWORD, XBMC_UPDATE, \
- XBMC_NOTIFY, NMA_ENABLED, NMA_APIKEY, NMA_PRIORITY, SYNOINDEX_ENABLED, ALBUM_COMPLETION_PCT, PREFERRED_BITRATE_HIGH_BUFFER, \
+ XBMC_NOTIFY, NMA_ENABLED, NMA_APIKEY, NMA_PRIORITY, NMA_ONSNATCH, SYNOINDEX_ENABLED, ALBUM_COMPLETION_PCT, PREFERRED_BITRATE_HIGH_BUFFER, \
PREFERRED_BITRATE_LOW_BUFFER
if __INITIALIZED__:
@@ -401,6 +402,7 @@ def initialize():
NMA_ENABLED = bool(check_setting_int(CFG, 'NMA', 'nma_enabled', 0))
NMA_APIKEY = check_setting_str(CFG, 'NMA', 'nma_apikey', '')
NMA_PRIORITY = check_setting_int(CFG, 'NMA', 'nma_priority', 0)
+ NMA_ONSNATCH = bool(check_setting_int(CFG, 'NMA', 'nma_onsnatch', 0))
SYNOINDEX_ENABLED = bool(check_setting_int(CFG, 'Synoindex', 'synoindex_enabled', 0))
@@ -676,6 +678,7 @@ def config_write():
new_config['NMA']['nma_enabled'] = int(NMA_ENABLED)
new_config['NMA']['nma_apikey'] = NMA_APIKEY
new_config['NMA']['nma_priority'] = NMA_PRIORITY
+ new_config['NMA']['nma_onsnatch'] = int(PROWL_ONSNATCH)
new_config['Synoindex'] = {}
new_config['Synoindex']['synoindex_enabled'] = int(SYNOINDEX_ENABLED)
diff --git a/headphones/importer.py b/headphones/importer.py
index 571360e9..d8ceb281 100644
--- a/headphones/importer.py
+++ b/headphones/importer.py
@@ -41,7 +41,7 @@ def artistlist_to_mbids(artistlist, forced=False):
for artist in artistlist:
- if not artist:
+ if not artist and not (artist == ' '):
continue
results = mb.findArtist(artist, limit=1)
@@ -103,6 +103,9 @@ def addArtisttoDB(artistid, extrasonly=False):
logger.warn('Cannot import Various Artists.')
return
+ # We'll use this to see if we should update the 'LastUpdated' time stamp
+ errors = False
+
myDB = db.DBConnection()
# Delete from blacklist if it's on there
@@ -171,6 +174,7 @@ def addArtisttoDB(artistid, extrasonly=False):
continue
if not releaselist:
+ errors = True
continue
# This will be used later to build a hybrid release
@@ -186,10 +190,12 @@ def addArtisttoDB(artistid, extrasonly=False):
try:
releasedict = mb.getRelease(releaseid, include_artist_info=False)
except Exception, e:
+ errors = True
logger.info('Unable to get release information for %s: %s' % (release['id'], e))
continue
if not releasedict:
+ errors = True
continue
controlValueDict = {"ReleaseID": release['id']}
@@ -342,17 +348,19 @@ def addArtisttoDB(artistid, extrasonly=False):
if headphones.AUTOWANT_ALL:
newValueDict['Status'] = "Wanted"
-
- #start a search for the album
- import searcher
- searcher.searchforalbum(albumid=rg['id'])
-
elif album['ReleaseDate'] > helpers.today() and headphones.AUTOWANT_UPCOMING:
newValueDict['Status'] = "Wanted"
else:
newValueDict['Status'] = "Skipped"
myDB.upsert("albums", newValueDict, controlValueDict)
+
+ #start a search for the album if it's new and autowant_all is selected:
+ # Should this run in a background thread? Don't know if we want to have a bunch of
+ # simultaneous threads running
+ if not rg_exists and headphones.AUTOWANT_ALL:
+ from headphones import searcher
+ searcher.searchforalbum(albumid=rg['id'])
myDB.action('DELETE from tracks WHERE AlbumID=?', [rg['id']])
tracks = myDB.action('SELECT * from alltracks WHERE ReleaseID=?', [releaseid]).fetchall()
@@ -412,14 +420,18 @@ def addArtisttoDB(artistid, extrasonly=False):
"TotalTracks": totaltracks,
"HaveTracks": havetracks}
- newValueDict['LastUpdated'] = helpers.now()
+ if not errors:
+ newValueDict['LastUpdated'] = helpers.now()
myDB.upsert("artists", newValueDict, controlValueDict)
logger.info(u"Seeing if we need album art for: " + artist['artist_name'])
cache.getThumb(ArtistID=artistid)
- logger.info(u"Updating complete for: " + artist['artist_name'])
+ if errors:
+ logger.info("Finished updating artist: " + artist['artist_name'] + " but with errors, so not marking it as updated in the database")
+ else:
+ logger.info(u"Updating complete for: " + artist['artist_name'])
def addReleaseById(rid):
diff --git a/headphones/librarysync.py b/headphones/librarysync.py
index 1a513564..7001c14c 100644
--- a/headphones/librarysync.py
+++ b/headphones/librarysync.py
@@ -33,7 +33,7 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None):
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))
+ logger.warn('Cannot find directory: %s. Not scanning' % dir.decode(headphones.SYS_ENCODING, 'replace'))
return
myDB = db.DBConnection()
@@ -48,7 +48,7 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None):
myDB.action('DELETE from have')
- logger.info('Scanning music directory: %s' % dir)
+ logger.info('Scanning music directory: %s' % dir.decode(headphones.SYS_ENCODING, 'replace'))
new_artists = []
bitrates = []
@@ -105,7 +105,7 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None):
# Now we start track matching
total_number_of_songs = len(song_list)
- logger.info("Found " + str(total_number_of_songs) + " tracks in: '" + dir + "'. Matching tracks to the appropriate releases....")
+ logger.info("Found " + str(total_number_of_songs) + " 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
@@ -299,7 +299,7 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None):
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)
+ logger.info('Completed matching tracks from directory: %s' % dir.decode(headphones.SYS_ENCODING, 'replace'))
if not append:
diff --git a/headphones/music_encoder.py b/headphones/music_encoder.py
index 71da9ba5..2cff0505 100644
--- a/headphones/music_encoder.py
+++ b/headphones/music_encoder.py
@@ -49,41 +49,41 @@ def encode(albumPath):
if (headphones.ENCODERLOSSLESS):
if (music.lower().endswith('.flac')):
musicFiles.append(os.path.join(r, music))
- musicTemp = os.path.normpath(os.path.splitext(music)[0]+'.'+headphones.ENCODEROUTPUTFORMAT)
+ musicTemp = os.path.normpath(os.path.splitext(music)[0]+'.'+headphones.ENCODEROUTPUTFORMAT.encode(headphones.SYS_ENCODING))
musicTempFiles.append(os.path.join(tempDirEncode, musicTemp))
else:
logger.debug('Music "%s" is already encoded' % (music))
else:
musicFiles.append(os.path.join(r, music))
- musicTemp = os.path.normpath(os.path.splitext(music)[0]+'.'+headphones.ENCODEROUTPUTFORMAT)
+ musicTemp = os.path.normpath(os.path.splitext(music)[0]+'.'+headphones.ENCODEROUTPUTFORMAT.encode(headphones.SYS_ENCODING))
musicTempFiles.append(os.path.join(tempDirEncode, musicTemp))
if headphones.ENCODER=='lame':
- encoder=os.path.join(headphones.ENCODERFOLDER,'lame')
+ encoder=os.path.join(headphones.ENCODERFOLDER.encode(headphones.SYS_ENCODING),'lame')
elif headphones.ENCODER=='ffmpeg':
- encoder=os.path.join(headphones.ENCODERFOLDER,'ffmpeg')
+ encoder=os.path.join(headphones.ENCODERFOLDER.encode(headphones.SYS_ENCODING),'ffmpeg')
i=0
for music in musicFiles:
infoMusic=MediaFile(music)
if headphones.ENCODER == 'lame':
if not any(music.lower().endswith('.' + x) for x in ["mp3", "wav"]):
- logger.warn('Lame cant encode "%s" format for "%s", use ffmpeg' % (os.path.splitext(music)[1],music))
+ logger.warn(u'Lame cant encode "%s" format for "%s", use ffmpeg' % (os.path.splitext(music)[1].decode(headphones.SYS_ENCODING, 'replace'),music.decode(headphones.SYS_ENCODING, 'replace')))
else:
if (music.lower().endswith('.mp3') and (infoMusic.bitrate/1000<=headphones.BITRATE)):
- logger.info('Music "%s" has bitrate<="%skbit" will not be reencoded' % (music,headphones.BITRATE))
+ logger.info('Music "%s" has bitrate<="%skbit" will not be reencoded' % (music.decode(headphones.SYS_ENCODING, 'replace'),headphones.BITRATE))
else:
command(encoder,music,musicTempFiles[i],albumPath)
ifencoded=1
else:
if headphones.ENCODEROUTPUTFORMAT=='ogg':
if music.lower().endswith('.ogg'):
- logger.warn('Can not reencode .ogg music "%s"' % (music))
+ logger.warn('Can not reencode .ogg music "%s"' % (music.decode(headphones.SYS_ENCODING, 'replace')))
else:
command(encoder,music,musicTempFiles[i],albumPath)
ifencoded=1
elif (headphones.ENCODEROUTPUTFORMAT=='mp3' or headphones.ENCODEROUTPUTFORMAT=='m4a'):
if (music.lower().endswith('.'+headphones.ENCODEROUTPUTFORMAT) and (infoMusic.bitrate/1000<=headphones.BITRATE)):
- logger.info('Music "%s" has bitrate<="%skbit" will not be reencoded' % (music,headphones.BITRATE))
+ logger.info('Music "%s" has bitrate<="%skbit" will not be reencoded' % (music.decode(headphones.SYS_ENCODING, 'replace'),headphones.BITRATE))
else:
command(encoder,music,musicTempFiles[i],albumPath)
ifencoded=1
@@ -97,7 +97,7 @@ def encode(albumPath):
musicFinalFiles.append(os.path.join(r, music))
if ifencoded==0:
- logger.info('Encoding for folder "%s" is not needed' % (albumPath))
+ logger.info('Encoding for folder "%s" is not needed' % (albumPath.decode(headphones.SYS_ENCODING, 'replace')))
return musicFinalFiles
diff --git a/headphones/notifiers.py b/headphones/notifiers.py
index 3c16b647..3df88485 100644
--- a/headphones/notifiers.py
+++ b/headphones/notifiers.py
@@ -171,14 +171,17 @@ class NMA:
return response
- def notify(self, artist, album):
+ def notify(self, artist=None, album=None, snatched_nzb=None):
apikey = self.apikey
priority = self.priority
- event = artist + ' - ' + album + ' complete!'
-
- description = "Headphones has downloaded and postprocessed: " + artist + ' [' + album + ']'
+ if snatched_nzb:
+ event = snatched_nzb + " snatched!"
+ description = "Headphones has snatched: " + snatched_nzb + " and has sent it to SABnzbd+"
+ else:
+ event = artist + ' - ' + album + ' complete!'
+ description = "Headphones has downloaded and postprocessed: " + artist + ' [' + album + ']'
data = { 'apikey': apikey, 'application':'Headphones', 'event': event, 'description': description, 'priority': priority}
@@ -223,4 +226,4 @@ class Synoindex:
def notify_multiple(self, path_list):
if isinstance(path_list, list):
for path in path_list:
- self.notify(path)
\ No newline at end of file
+ self.notify(path)
diff --git a/headphones/postprocessor.py b/headphones/postprocessor.py
index f9bfc833..ba4f5169 100644
--- a/headphones/postprocessor.py
+++ b/headphones/postprocessor.py
@@ -160,7 +160,7 @@ def verify(albumid, albumpath):
try:
f = MediaFile(downloaded_track)
except Exception, e:
- logger.info("Exception from MediaFile for: " + downloaded_track + " : " + str(e))
+ logger.info(u"Exception from MediaFile for: " + downloaded_track.decode(headphones.SYS_ENCODING, 'replace') + u" : " + unicode(e))
continue
metaartist = helpers.latinToAscii(f.artist.lower()).encode('UTF-8')
@@ -222,13 +222,13 @@ def verify(albumid, albumpath):
doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list)
return
- logger.warn('Could not identify album: %s. It may not be the intended album.' % albumpath)
+ logger.warn(u'Could not identify album: %s. It may not be the intended album.' % albumpath.decode(headphones.SYS_ENCODING, 'replace'))
myDB.action('UPDATE snatched SET status = "Unprocessed" WHERE AlbumID=?', [albumid])
processed = re.search(r' \(Unprocessed\)(?:\[\d+\])?', albumpath)
if not processed:
renameUnprocessedFolder(albumpath)
else:
- logger.info("Already marked as unprocessed: " + albumpath)
+ logger.info(u"Already marked as unprocessed: " + albumpath.decode(headphones.SYS_ENCODING, 'replace'))
def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list):
@@ -266,12 +266,13 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list)
if headphones.RENAME_FILES:
renameFiles(albumpath, downloaded_track_list, release)
- if headphones.MOVE_FILES and headphones.DESTINATION_DIR:
- albumpaths = moveFiles(albumpath, release, tracks)
-
if headphones.MOVE_FILES and not headphones.DESTINATION_DIR:
logger.error('No DESTINATION_DIR has been set. Set "Destination Directory" to the parent directory you want to move the files to')
- pass
+ albumpaths = [albumpath]
+ elif headphones.MOVE_FILES and headphones.DESTINATION_DIR:
+ albumpaths = moveFiles(albumpath, release, tracks)
+ else:
+ albumpaths = [albumpath]
myDB = db.DBConnection()
myDB.action('UPDATE albums SET status = "Downloaded" WHERE AlbumID=?', [albumid])
@@ -281,9 +282,9 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list)
for albumpath in albumpaths:
librarysync.libraryScan(dir=albumpath, append=True, ArtistID=release['ArtistID'], ArtistName=release['ArtistName'])
- logger.info('Post-processing for %s - %s complete' % (release['ArtistName'], release['AlbumTitle']))
+ logger.info(u'Post-processing for %s - %s complete' % (release['ArtistName'], release['AlbumTitle']))
- if headphones.PROWL_ONSNATCH:
+ if headphones.PROWL_ENABLED:
pushmessage = release['ArtistName'] + ' - ' + release['AlbumTitle']
logger.info(u"Prowl request")
prowl = notifiers.PROWL()
@@ -312,7 +313,7 @@ def embedAlbumArt(artwork, downloaded_track_list):
try:
f = MediaFile(downloaded_track)
except:
- logger.error('Could not read %s. Not adding album art' % downloaded_track)
+ logger.error(u'Could not read %s. Not adding album art' % downloaded_track.decode(headphones.SYS_ENCODING, 'replace'))
continue
logger.debug('Adding album art to: %s' % downloaded_track)
@@ -336,7 +337,7 @@ def cleanupFiles(albumpath):
try:
os.remove(os.path.join(r, files))
except Exception, e:
- logger.error('Could not remove file: %s. Error: %s' % (files, e))
+ logger.error(u'Could not remove file: %s. Error: %s' % (files.decode(headphones.SYS_ENCODING, 'replace'), e))
def moveFiles(albumpath, release, tracks):
@@ -486,9 +487,9 @@ def moveFiles(albumpath, release, tracks):
try:
os.remove(file_to_move)
except Exception, e:
- logger.error("Error deleting file '" + file_to_move.decode(headphones.SYS_ENCODING) + "' from source directory")
+ logger.error("Error deleting file '" + file_to_move.decode(headphones.SYS_ENCODING, 'replace') + "' from source directory")
else:
- logger.error("Error copying '" + file_to_move.decode(headphones.SYS_ENCODING) + "'. Not deleting from download directory")
+ logger.error("Error copying '" + file_to_move.decode(headphones.SYS_ENCODING, 'replace') + "'. Not deleting from download directory")
elif make_lossless_folder and not make_lossy_folder:
@@ -519,13 +520,13 @@ def moveFiles(albumpath, release, tracks):
try:
os.chmod(os.path.normpath(temp_f).encode(headphones.SYS_ENCODING), int(headphones.FOLDER_PERMISSIONS, 8))
except Exception, e:
- logger.error("Error trying to change permissions on folder: %s" % temp_f)
+ logger.error("Error trying to change permissions on folder: %s" % temp_f.decode(headphones.SYS_ENCODING, 'replace'))
# If we failed to move all the files out of the directory, this will fail too
try:
shutil.rmtree(albumpath)
except Exception, e:
- logger.error('Could not remove directory: %s. %s' % (albumpath, e))
+ logger.error('Could not remove directory: %s. %s' % (albumpath.decode(headphones.SYS_ENCODING, 'replace'), e))
destination_paths = []
@@ -552,10 +553,10 @@ def correctMetadata(albumid, release, downloaded_track_list):
elif any(downloaded_track.lower().endswith('.' + x.lower()) for x in headphones.LOSSY_MEDIA_FORMATS):
lossy_items.append(beets.library.Item.from_path(downloaded_track))
else:
- logger.warn("Skipping: " + downloaded_track.decode(headphones.SYS_ENCODING) + " because it is not a mutagen friendly file format")
+ logger.warn("Skipping: " + downloaded_track.decode(headphones.SYS_ENCODING, 'replace') + " because it is not a mutagen friendly file format")
except Exception, e:
- logger.error("Beets couldn't create an Item from: " + downloaded_track.decode(headphones.SYS_ENCODING) + " - not a media file?" + str(e))
+ logger.error("Beets couldn't create an Item from: " + downloaded_track.decode(headphones.SYS_ENCODING, 'replace') + " - not a media file?" + str(e))
for items in [lossy_items, lossless_items]:
@@ -586,9 +587,9 @@ def correctMetadata(albumid, release, downloaded_track_list):
for item in items:
try:
item.write()
- logger.info("Successfully applied metadata to: " + item.path.decode(headphones.SYS_ENCODING))
+ logger.info("Successfully applied metadata to: " + item.path.decode(headphones.SYS_ENCODING, 'replace'))
except Exception, e:
- logger.warn("Error writing metadata to " + item.path.decode(headphones.SYS_ENCODING) + ": " + str(e))
+ logger.warn("Error writing metadata to " + item.path.decode(headphones.SYS_ENCODING, 'replace') + ": " + str(e))
def embedLyrics(downloaded_track_list):
logger.info('Adding lyrics')
@@ -600,14 +601,14 @@ def embedLyrics(downloaded_track_list):
try:
f = MediaFile(downloaded_track)
except:
- logger.error('Could not read %s. Not checking lyrics' % downloaded_track)
+ logger.error('Could not read %s. Not checking lyrics' % downloaded_track.decode(headphones.SYS_ENCODING, 'replace'))
if f.albumartist and f.title:
metalyrics = lyrics.getLyrics(f.albumartist, f.title)
elif f.artist and f.title:
metalyrics = lyrics.getLyrics(f.artist, f.title)
else:
- logger.info('No artist/track metadata found for track: %s. Not fetching lyrics' % downloaded_track)
+ logger.info('No artist/track metadata found for track: %s. Not fetching lyrics' % downloaded_track.decode(headphones.SYS_ENCODING, 'replace'))
metalyrics = None
if lyrics:
@@ -627,7 +628,7 @@ def renameFiles(albumpath, downloaded_track_list, release):
try:
f = MediaFile(downloaded_track)
except:
- logger.info("MediaFile couldn't parse: " + downloaded_track.decode(headphones.SYS_ENCODING))
+ logger.info("MediaFile couldn't parse: " + downloaded_track.decode(headphones.SYS_ENCODING, 'replace'))
continue
if not f.track:
@@ -637,7 +638,7 @@ def renameFiles(albumpath, downloaded_track_list, release):
if not f.title:
- basename = unicode(os.path.basename(downloaded_track), headphones.SYS_ENCODING, errors='replace')
+ basename = os.path.basename(downloaded_track.decode(headphones.SYS_ENCODING, 'replace'))
title = os.path.splitext(basename)[0]
ext = os.path.splitext(basename)[1]
@@ -678,7 +679,7 @@ def renameFiles(albumpath, downloaded_track_list, release):
try:
os.rename(downloaded_track, new_file)
except Exception, e:
- logger.error('Error renaming file: %s. Error: %s' % (downloaded_track.decode(headphones.SYS_ENCODING), e))
+ logger.error('Error renaming file: %s. Error: %s' % (downloaded_track.decode(headphones.SYS_ENCODING, 'replace'), e))
continue
def renameUnprocessedFolder(albumpath):
@@ -698,66 +699,55 @@ def renameUnprocessedFolder(albumpath):
return
def forcePostProcess():
-
- if not headphones.DOWNLOAD_DIR:
- logger.error('No DOWNLOAD_DIR has been set. Set "Music Download Directory:" to your SAB download directory on the settings page.')
- return
+
+ download_dirs = []
+ if headphones.DOWNLOAD_DIR:
+ download_dirs.append(headphones.DOWNLOAD_DIR.encode(headphones.SYS_ENCODING))
+ if headphones.DOWNLOAD_TORRENT_DIR:
+ download_dirs.append(headphones.DOWNLOAD_TORRENT_DIR.encode(headphones.SYS_ENCODING))
+
+ logger.info('Checking to see if there are any folders to process in download_dir(s): %s' % str(download_dirs).decode(headphones.SYS_ENCODING, 'replace'))
+ # Get a list of folders in the download_dir
+ folders = []
+ for download_dir in download_dirs:
+ for folder in os.listdir(download_dir):
+ path_to_folder = os.path.join(download_dir, folder)
+ if os.path.isdir(path_to_folder):
+ folders.append(path_to_folder)
+
+ if len(folders):
+ logger.info('Found %i folders to process' % len(folders))
else:
- processing = "nzb"
- processing_next = "torrent"
- while processing != "done":
- if headphones.DOWNLOAD_DIR and processing == "nzb":
- download_dir = headphones.DOWNLOAD_DIR.encode('utf-8')
- if not headphones.DOWNLOAD_TORRENT_DIR:
- processing_next = "done"
- if headphones.DOWNLOAD_TORRENT_DIR and processing == "torrent":
- download_dir = headphones.DOWNLOAD_TORRENT_DIR.encode('utf-8')
- processing_next = "done"
- if not headphones.DOWNLOAD_DIR and processing == "nzb":
- download_dir = headphones.DOWNLOAD_TORRENT_DIR.encode('utf-8')
- processing_next = "done"
-
- logger.info('Checking to see if there are any folders to process in download_dir: %s' % download_dir)
- # Get a list of folders in the download_dir
- folders = [d for d in os.listdir(download_dir) if os.path.isdir(os.path.join(download_dir, d))]
+ logger.info('Found no folders to process in: %s' % str(download_dirs).decode(headphones.SYS_ENCODING, 'replace'))
+
+ # Parse the folder names to get artist album info
+ for folder in folders:
+
+ folder_basename = os.path.basename(folder).decode(headphones.SYS_ENCODING, 'replace')
+
+ logger.info('Processing: %s' % folder_basename)
- if len(folders):
- logger.info('Found %i folders to process' % len(folders))
- pass
+ try:
+ name, album, year = helpers.extract_data(folder_basename)
+ except:
+ logger.info("Couldn't parse " + folder_basename + " into any valid format.")
+ continue
+ if name and album and year:
+
+ myDB = db.DBConnection()
+ release = myDB.action('SELECT AlbumID, ArtistName, AlbumTitle from albums WHERE ArtistName LIKE ? and AlbumTitle LIKE ?', [name, album]).fetchone()
+ if release:
+ logger.info('Found a match in the database: %s - %s. Verifying to make sure it is the correct album' % (release['ArtistName'], release['AlbumTitle']))
+ verify(release['AlbumID'], folder)
else:
- logger.info('Found no folders to process in: %s' % download_dir)
- return
-
- # Parse the folder names to get artist album info
- for folder in folders:
-
- albumpath = os.path.join(download_dir, folder)
- folder = unicode(folder, headphones.SYS_ENCODING, errors='replace')
-
- logger.info('Processing: %s' % folder)
-
+ logger.info('Querying MusicBrainz for the release group id for: %s - %s' % (name, album))
+ from headphones import mb
try:
- name, album, year = helpers.extract_data(folder)
+ rgid = mb.findAlbumID(helpers.latinToAscii(name), helpers.latinToAscii(album))
except:
- logger.info("Couldn't parse " + folder + " into any valid format.")
+ logger.error('Can not get release information for this album')
continue
- if name and album and year:
-
- myDB = db.DBConnection()
- release = myDB.action('SELECT AlbumID, ArtistName, AlbumTitle from albums WHERE ArtistName LIKE ? and AlbumTitle LIKE ?', [name, album]).fetchone()
- if release:
- logger.info('Found a match in the database: %s - %s. Verifying to make sure it is the correct album' % (release['ArtistName'], release['AlbumTitle']))
- verify(release['AlbumID'], albumpath)
- else:
- logger.info('Querying MusicBrainz for the release group id for: %s - %s' % (name, album))
- from headphones import mb
- try:
- rgid = mb.findAlbumID(helpers.latinToAscii(name), helpers.latinToAscii(album))
- except:
- logger.error('Can not get release information for this album')
- continue
- if rgid:
- verify(rgid, albumpath)
- else:
- logger.info('No match found on MusicBrainz for: %s - %s' % (name, album))
- processing = processing_next
+ if rgid:
+ verify(rgid, folder)
+ else:
+ logger.info('No match found on MusicBrainz for: %s - %s' % (name, album))
diff --git a/headphones/sab.py b/headphones/sab.py
index 6d57214c..9b0e2bd1 100644
--- a/headphones/sab.py
+++ b/headphones/sab.py
@@ -27,7 +27,7 @@ import urllib2, cookielib
from headphones.common import USER_AGENT
from headphones import logger
-from headphones import notifiers
+from headphones import notifiers, helpers
def sendNZB(nzb):
@@ -64,8 +64,10 @@ def sendNZB(nzb):
# if we get a raw data result we want to upload it to SAB
elif nzb.resultType == "nzbdata":
+ # Sanitize the file a bit, since we can only use ascii chars with MultiPartPostHandler
+ nzbdata = helpers.latinToAscii(nzb.extraInfo[0])
params['mode'] = 'addfile'
- multiPartParams = {"nzbfile": (nzb.name+".nzb", nzb.extraInfo[0])}
+ multiPartParams = {"nzbfile": (nzb.name+".nzb", nzbdata)}
if not headphones.SAB_HOST.startswith('http'):
headphones.SAB_HOST = 'http://' + headphones.SAB_HOST
@@ -78,7 +80,7 @@ def sendNZB(nzb):
try:
if nzb.resultType == "nzb":
- f = urllib.urlopen(url)
+ f = urllib.urlopen(url)
elif nzb.resultType == "nzbdata":
cookies = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookies),
@@ -97,7 +99,11 @@ def sendNZB(nzb):
except httplib.InvalidURL, e:
logger.error(u"Invalid SAB host, check your config. Current host: %s" % headphones.SAB_HOST)
return False
-
+
+ except Exception, e:
+ logger.error(u"Error: " + str(e))
+ return False
+
if f == None:
logger.info(u"No data returned from SABnzbd, NZB not sent")
return False
@@ -118,10 +124,14 @@ def sendNZB(nzb):
if sabText == "ok":
logger.info(u"NZB sent to SAB successfully")
- if headphones.PROWL_ONSNATCH:
- logger.info(u"Prowl request")
+ if headphones.PROWL_ENABLED and headphones.PROWL_ONSNATCH:
+ logger.info(u"Sending Prowl notification")
prowl = notifiers.PROWL()
prowl.notify(nzb.name,"Download started")
+ if headphones.NMA_ENABLED and headphones.NMA_ONSNATCH:
+ logger.debug(u"Sending NMA notification")
+ nma = notifiers.NMA()
+ nma.notify(snatched_nzb=nzb.name)
return True
elif sabText == "Missing authentication":
diff --git a/headphones/webserve.py b/headphones/webserve.py
index 14f29a80..799d84f3 100644
--- a/headphones/webserve.py
+++ b/headphones/webserve.py
@@ -511,6 +511,7 @@ class WebInterface(object):
"nma_enabled": checked(headphones.NMA_ENABLED),
"nma_apikey": headphones.NMA_APIKEY,
"nma_priority": int(headphones.NMA_PRIORITY),
+ "nma_onsnatch": checked(headphones.NMA_ONSNATCH),
"synoindex_enabled": checked(headphones.SYNOINDEX_ENABLED),
"mirror_list": headphones.MIRRORLIST,
"mirror": headphones.MIRROR,
@@ -550,7 +551,7 @@ class WebInterface(object):
interface=None, log_dir=None, music_encoder=0, encoder=None, bitrate=None, samplingfrequency=None, encoderfolder=None, advancedencoder=None,
encoderoutputformat=None, encodervbrcbr=None, encoderquality=None, encoderlossless=0, delete_lossless_files=0, prowl_enabled=0, prowl_onsnatch=0,
prowl_keys=None, prowl_priority=0, xbmc_enabled=0, xbmc_host=None, xbmc_username=None, xbmc_password=None, xbmc_update=0, xbmc_notify=0, nma_enabled=False,
- nma_apikey=None, nma_priority=0, synoindex_enabled=False, mirror=None, customhost=None, customport=None, customsleep=None, hpuser=None, hppass=None,
+ nma_apikey=None, nma_priority=0, nma_onsnatch=0, synoindex_enabled=False, mirror=None, customhost=None, customport=None, customsleep=None, hpuser=None, hppass=None,
preferred_bitrate_high_buffer=None, preferred_bitrate_low_buffer=None, **kwargs):
headphones.HTTP_HOST = http_host
@@ -639,6 +640,7 @@ class WebInterface(object):
headphones.NMA_ENABLED = nma_enabled
headphones.NMA_APIKEY = nma_apikey
headphones.NMA_PRIORITY = nma_priority
+ headphones.NMA_ONSNATCH = nma_onsnatch
headphones.SYNOINDEX_ENABLED = synoindex_enabled
headphones.MIRROR = mirror
headphones.CUSTOMHOST = customhost
diff --git a/init-alt.freebsd b/init-alt.freebsd
new file mode 100644
index 00000000..16fd5f97
--- /dev/null
+++ b/init-alt.freebsd
@@ -0,0 +1,88 @@
+#!/bin/sh
+#
+# PROVIDE: headphones
+# REQUIRE: sabnzbd
+# KEYWORD: shutdown
+#
+# Add the following lines to /etc/rc.conf.local or /etc/rc.conf
+# to enable this service:
+#
+# headphones_enable (bool): Set to NO by default.
+# Set it to YES to enable it.
+# headphones_user: The user account Headphones daemon runs as what
+# you want it to be. It uses '_sabnzbd' user by
+# default. Do not sets it as empty or it will run
+# as root.
+# headphones_dir: Directory where Headphones lives.
+# Default: /usr/local/headphones
+# headphones_chdir: Change to this directory before running Headphones.
+# Default is same as headphones_dir.
+# headphones_pid: The name of the pidfile to create.
+# Default is headphones.pid in headphones_dir.
+
+. /etc/rc.subr
+
+name="headphones"
+rcvar=${name}_enable
+
+load_rc_config ${name}
+
+: ${headphones_enable:="NO"}
+: ${headphones_user:="_sabnzbd"}
+: ${headphones_dir:="/usr/local/headphones"}
+: ${headphones_chdir:="${headphones_dir}"}
+: ${headphones_pid:="${headphones_dir}/headphones.pid"}
+: ${headphones_conf:="${headphones_dir}/config.ini"}
+
+WGET="/usr/local/bin/wget" # You need wget for this script to safely shutdown Headphones.
+if [ -e "${headphones_conf}" ]; then
+ HOST=`grep -A64 "\[General\]" "${headphones_conf}"|egrep "^http_host"|perl -wple 's/^http_host = (.*)$/$1/'`
+ PORT=`grep -A64 "\[General\]" "${headphones_conf}"|egrep "^http_port"|perl -wple 's/^http_port = (.*)$/$1/'`
+fi
+
+status_cmd="${name}_status"
+stop_cmd="${name}_stop"
+
+command="${headphones_dir}/Headphones.py"
+command_args="--daemon --quiet --nolaunch --port ${PORT} --pidfile ${headphones_pid} --config ${headphones_conf}"
+
+# Check for wget and refuse to start without it.
+if [ ! -x "${WGET}" ]; then
+ warn "Headphones not started: You need wget to safely shut down Headphones."
+ exit 1
+fi
+
+# Ensure user is root when running this script.
+if [ `id -u` != "0" ]; then
+ echo "Oops, you should be root before running this!"
+ exit 1
+fi
+
+verify_headphones_pid() {
+ # Make sure the pid corresponds to the Headphones process.
+ pid=`cat ${headphones_pid} 2>/dev/null`
+ ps -p ${pid} | grep -q "python ${headphones_dir}/Headphones.py"
+ return $?
+}
+
+# Try to stop Headphones cleanly by calling shutdown over http.
+headphones_stop() {
+ if [ ! -e "${headphones_conf}" ]; then
+ echo "Headphones' settings file does not exist. Try starting Headphones, as this should create the file."
+ exit 1
+ fi
+ echo "Stopping $name"
+ verify_headphones_pid
+ ${WGET} -O - -q --user=${SBUSR} --password=${SBPWD} "http://${HOST}:${PORT}/shutdown/" >/dev/null
+
+ if [ -n "${pid}" ]; then
+ wait_for_pids ${pid}
+ echo "Stopped $name"
+ fi
+}
+
+headphones_status() {
+ verify_headphones_pid && echo "$name is running as ${pid}" || echo "$name is not running"
+}
+
+run_rc_command "$1"