From a5fd5c6881bac7428e215adc0c7d03c2f56ef3ee Mon Sep 17 00:00:00 2001 From: rembo10 Date: Wed, 31 Oct 2012 14:49:01 -0400 Subject: [PATCH] Use original nzb titles for downloading, with mbid appended --- Headphones.py | 1 + headphones/__init__.py | 5 ++-- headphones/helpers.py | 59 +++++++++++++++++++++++++++++++++---- headphones/postprocessor.py | 55 ++++++++++++++++++++++++++++------ headphones/searcher.py | 3 +- 5 files changed, 105 insertions(+), 18 deletions(-) diff --git a/Headphones.py b/Headphones.py index ff469ebd..a3896a0f 100644 --- a/Headphones.py +++ b/Headphones.py @@ -41,6 +41,7 @@ def main(): headphones.ARGS = sys.argv[1:] # From sickbeard + headphones.SYS_PLATFORM = sys.platform headphones.SYS_ENCODING = None try: diff --git a/headphones/__init__.py b/headphones/__init__.py index ec6f5ac4..c2c5ce37 100644 --- a/headphones/__init__.py +++ b/headphones/__init__.py @@ -36,6 +36,7 @@ PROG_DIR = None ARGS = None SIGNAL = None +SYS_PLATFORM = None SYS_ENCODING = None VERBOSE = 1 @@ -251,7 +252,7 @@ def initialize(): with INIT_LOCK: - global __INITIALIZED__, FULL_PATH, PROG_DIR, VERBOSE, DAEMON, DATA_DIR, CONFIG_FILE, CFG, CONFIG_VERSION, LOG_DIR, CACHE_DIR, \ + global __INITIALIZED__, FULL_PATH, PROG_DIR, VERBOSE, DAEMON, SYS_PLATFORM, DATA_DIR, CONFIG_FILE, CFG, CONFIG_VERSION, LOG_DIR, CACHE_DIR, \ HTTP_PORT, HTTP_HOST, HTTP_USERNAME, HTTP_PASSWORD, HTTP_ROOT, HTTP_PROXY, LAUNCH_BROWSER, API_ENABLED, API_KEY, GIT_PATH, \ CURRENT_VERSION, LATEST_VERSION, CHECK_GITHUB, CHECK_GITHUB_ON_STARTUP, CHECK_GITHUB_INTERVAL, MUSIC_DIR, DESTINATION_DIR, \ LOSSLESS_DESTINATION_DIR, PREFERRED_QUALITY, PREFERRED_BITRATE, DETECT_BITRATE, ADD_ARTISTS, CORRECT_METADATA, MOVE_FILES, \ @@ -759,7 +760,7 @@ def start(): # Start our scheduled background tasks from headphones import updater, searcher, librarysync, postprocessor - SCHED.add_interval_job(updater.dbUpdate, hours=48) + SCHED.add_interval_job(updater.dbUpdate, hours=24) SCHED.add_interval_job(searcher.searchforalbum, minutes=SEARCH_INTERVAL) SCHED.add_interval_job(librarysync.libraryScan, minutes=LIBRARYSCAN_INTERVAL) diff --git a/headphones/helpers.py b/headphones/helpers.py index 957d9717..2b80f93b 100644 --- a/headphones/helpers.py +++ b/headphones/helpers.py @@ -164,8 +164,6 @@ def cleanTitle(title): return title def extract_data(s): - - from headphones import logger #headphones default format pattern = re.compile(r'(?P.*?)\s\-\s(?P.*?)\s\[(?P.*?)\]', re.VERBOSE) @@ -176,8 +174,6 @@ def extract_data(s): 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) @@ -188,8 +184,7 @@ def extract_data(s): year = match.group("year") return (name, album, year) else: - logger.info("Couldn't parse " + s + " into a valid Newbin format") - return (name, album, year) + return (None, None, None) def extract_logline(s): # Default log format @@ -268,3 +263,55 @@ def smartMove(src, dest, delete=True): return True except Exception, e: logger.warn('Error moving file %s: %s' % (filename.decode(headphones.SYS_ENCODING, 'replace'), str(e).decode(headphones.SYS_ENCODING, 'replace'))) + +######################### +#Sab renaming functions # +######################### + +# TODO: Grab config values from sab to know when these options are checked. For now we'll just iterate through all combinations + +def sab_replace_dots(name): + return name.replace('.',' ') +def sab_replace_spaces(name): + return name.replace(' ','_') + +def sab_sanitize_foldername(name): + """ Return foldername with dodgy chars converted to safe ones + Remove any leading and trailing dot and space characters + """ + CH_ILLEGAL = r'\/<>?*|"' + CH_LEGAL = r'++{}!@#`' + + FL_ILLEGAL = CH_ILLEGAL + ':\x92"' + FL_LEGAL = CH_LEGAL + "-''" + + uFL_ILLEGAL = FL_ILLEGAL.decode('latin-1') + uFL_LEGAL = FL_LEGAL.decode('latin-1') + + if not name: + return name + if isinstance(name, unicode): + illegal = uFL_ILLEGAL + legal = uFL_LEGAL + else: + illegal = FL_ILLEGAL + legal = FL_LEGAL + + lst = [] + for ch in name.strip(): + if ch in illegal: + ch = legal[illegal.find(ch)] + lst.append(ch) + else: + lst.append(ch) + name = ''.join(lst) + + name = name.strip('. ') + if not name: + name = 'unknown' + + #maxlen = cfg.folder_max_length() + #if len(name) > maxlen: + # name = name[:maxlen] + + return name diff --git a/headphones/postprocessor.py b/headphones/postprocessor.py index 1942ec5d..c514bdf8 100644 --- a/headphones/postprocessor.py +++ b/headphones/postprocessor.py @@ -20,6 +20,7 @@ import time import threading import music_encoder import urllib, shutil, re +import uuid from headphones import notifiers import lib.beets as beets from lib.beets import autotag @@ -27,6 +28,7 @@ from lib.beets.mediafile import MediaFile import headphones from headphones import db, albumart, librarysync, lyrics, logger, helpers +from headphones.helpers import sab_replace_dots, sab_replace_spaces postprocessor_lock = threading.Lock() @@ -40,15 +42,27 @@ def checkFolder(): for album in snatched: if album['FolderName']: + + # Need to check for variations due to sab renaming. Ideally we'd check the sab config via api to + # figure out which options are checked, but oh well + + nzb_album_possibilities = [ album['FolderName'], + sab_replace_dots(album['FolderName']), + sab_replace_spaces(album['FolderName']), + sab_replace_dots(sab_replace_spaces(album['FolderName'])) + ] - nzb_album_path = os.path.join(headphones.DOWNLOAD_DIR, album['FolderName']).encode(headphones.SYS_ENCODING) torrent_album_path = os.path.join(headphones.DOWNLOAD_TORRENT_DIR, album['FolderName']).encode(headphones.SYS_ENCODING) - if os.path.exists(nzb_album_path): - logger.debug('Found %s in NZB download folder. Verifying....' % album['FolderName']) - verify(album['AlbumID'], nzb_album_path) + for nzb_folder_name in nzb_album_possibilities: + + nzb_album_path = os.path.join(headphones.DOWNLOAD_DIR, nzb_folder_name).encode(headphones.SYS_ENCODING) - elif os.path.exists(torrent_album_path): + if os.path.exists(nzb_album_path): + logger.debug('Found %s in NZB download folder. Verifying....' % album['FolderName']) + verify(album['AlbumID'], nzb_album_path) + + if os.path.exists(torrent_album_path): logger.debug('Found %s in torrent download folder. Verifying....' % album['FolderName']) verify(album['AlbumID'], torrent_album_path) @@ -789,20 +803,21 @@ def forcePostProcess(): 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 + myDB = db.DBConnection() + for folder in folders: folder_basename = os.path.basename(folder).decode(headphones.SYS_ENCODING, 'replace') logger.info('Processing: %s' % folder_basename) - + try: name, album, year = helpers.extract_data(folder_basename) except: - logger.info("Couldn't parse " + folder_basename + " into any valid format.") - continue + name = None + 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'])) @@ -819,3 +834,25 @@ def forcePostProcess(): verify(rgid, folder) else: logger.info('No match found on MusicBrainz for: %s - %s' % (name, album)) + continue + + else: + try: + possible_rgid = folder_basename[-36:] + # re pattern match: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} + rgid = uuid.UUID(possible_rgid) + + except: + logger.info("Couldn't parse " + folder_basename + " into any valid format. If adding albums from another source, they must be in an 'Artist - Album [Year]' format, or end with the musicbrainz release group id") + continue + + + if rgid: + rgid = possible_rgid + release = myDB.action('SELECT ArtistName, AlbumTitle, AlbumID from albums WHERE AlbumID=?', [rgid]).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 a (possibly) valid Musicbrainz identifier in album folder name - continuing post-processing') + verify(rgid, folder) diff --git a/headphones/searcher.py b/headphones/searcher.py index 1bf0eb74..055ddde6 100644 --- a/headphones/searcher.py +++ b/headphones/searcher.py @@ -513,7 +513,8 @@ def searchNZB(albumid=None, new=False, losslessOnly=False): if data and bestqual: logger.info(u'Found best result: %s - %s' % (bestqual[2], bestqual[0], helpers.bytes_to_mb(bestqual[1]))) - nzb_folder_name = '%s - %s [%s]' % (helpers.latinToAscii(albums[0]).encode('UTF-8').replace('/', '_'), helpers.latinToAscii(albums[1]).encode('UTF-8').replace('/', '_'), year) + # Get rid of any dodgy chars here so we can prevent sab from renaming our downloads + nzb_folder_name = helpers.sab_sanitize_foldername(bestqual[0]) + '.' + albums[2] if headphones.SAB_HOST and not headphones.BLACKHOLE: nzb = classes.NZBDataSearchResult()