From d95fc59b4597854e83743bb41aba3cfb19184b78 Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Thu, 27 Dec 2018 08:24:05 -0500 Subject: [PATCH 1/3] Remove superfluous classes --- TorrentToMedia.py | 11 +- core/auto_process/comics.py | 107 +++-- core/auto_process/games.py | 103 +++-- core/auto_process/movies.py | 790 ++++++++++++++++++------------------ core/auto_process/music.py | 406 +++++++++--------- core/auto_process/tv.py | 651 ++++++++++++++--------------- nzbToMedia.py | 12 +- 7 files changed, 1042 insertions(+), 1038 deletions(-) diff --git a/TorrentToMedia.py b/TorrentToMedia.py index 79f6de76..0a6ba62a 100755 --- a/TorrentToMedia.py +++ b/TorrentToMedia.py @@ -10,6 +10,7 @@ import sys import core from core import logger, main_db +from core.auto_process import comics, games, movies, music, tv from core.user_scripts import external_script from core.utils import char_replace, convert_to_ascii, plex_update, replace_links from six import text_type @@ -237,21 +238,21 @@ def process_torrent(input_directory, input_name, input_category, input_hash, inp result = external_script(output_destination, input_name, input_category, section) elif section_name in ['CouchPotato', 'Radarr']: - result = core.Movie().process(section_name, output_destination, input_name, + result = movies.process(section_name, output_destination, input_name, status, client_agent, input_hash, input_category) elif section_name in ['SickBeard', 'NzbDrone', 'Sonarr']: if input_hash: input_hash = input_hash.upper() - result = core.TV().process(section_name, output_destination, input_name, + result = tv.process(section_name, output_destination, input_name, status, client_agent, input_hash, input_category) elif section_name in ['HeadPhones', 'Lidarr']: - result = core.Music().process(section_name, output_destination, input_name, + result = music.process(section_name, output_destination, input_name, status, client_agent, input_category) elif section_name == 'Mylar': - result = core.Comic().process(section_name, output_destination, input_name, + result = comics.process(section_name, output_destination, input_name, status, client_agent, input_category) elif section_name == 'Gamez': - result = core.Game().process(section_name, output_destination, input_name, + result = games.process(section_name, output_destination, input_name, status, client_agent, input_category) plex_update(input_category) diff --git a/core/auto_process/comics.py b/core/auto_process/comics.py index 40accce1..3eb8a247 100644 --- a/core/auto_process/comics.py +++ b/core/auto_process/comics.py @@ -11,67 +11,66 @@ from core.utils import convert_to_ascii, remote_dir, server_responding requests.packages.urllib3.disable_warnings() -class Comic(object): - def process(self, section, dir_name, input_name=None, status=0, client_agent='manual', input_category=None): - apc_version = "2.04" - comicrn_version = "1.01" +def process(section, dir_name, input_name=None, status=0, client_agent='manual', input_category=None): + apc_version = "2.04" + comicrn_version = "1.01" - cfg = dict(core.CFG[section][input_category]) + cfg = dict(core.CFG[section][input_category]) - host = cfg["host"] - port = cfg["port"] - apikey = cfg["apikey"] - ssl = int(cfg.get("ssl", 0)) - web_root = cfg.get("web_root", "") - remote_path = int(cfg.get("remote_path"), 0) - protocol = "https://" if ssl else "http://" + host = cfg["host"] + port = cfg["port"] + apikey = cfg["apikey"] + ssl = int(cfg.get("ssl", 0)) + web_root = cfg.get("web_root", "") + remote_path = int(cfg.get("remote_path"), 0) + protocol = "https://" if ssl else "http://" - url = "{0}{1}:{2}{3}/api".format(protocol, host, port, web_root) - if not server_responding(url): - logger.error("Server did not respond. Exiting", section) - return [1, "{0}: Failed to post-process - {1} did not respond.".format(section, section)] + url = "{0}{1}:{2}{3}/api".format(protocol, host, port, web_root) + if not server_responding(url): + logger.error("Server did not respond. Exiting", section) + return [1, "{0}: Failed to post-process - {1} did not respond.".format(section, section)] - input_name, dir_name = convert_to_ascii(input_name, dir_name) - clean_name, ext = os.path.splitext(input_name) - if len(ext) == 4: # we assume this was a standard extension. - input_name = clean_name + input_name, dir_name = convert_to_ascii(input_name, dir_name) + clean_name, ext = os.path.splitext(input_name) + if len(ext) == 4: # we assume this was a standard extension. + input_name = clean_name - params = { - 'cmd': 'forceProcess', - 'apikey': apikey, - 'nzb_folder': remote_dir(dir_name) if remote_path else dir_name, - } + params = { + 'cmd': 'forceProcess', + 'apikey': apikey, + 'nzb_folder': remote_dir(dir_name) if remote_path else dir_name, + } - if input_name is not None: - params['nzb_name'] = input_name - params['failed'] = int(status) - params['apc_version'] = apc_version - params['comicrn_version'] = comicrn_version + if input_name is not None: + params['nzb_name'] = input_name + params['failed'] = int(status) + params['apc_version'] = apc_version + params['comicrn_version'] = comicrn_version - success = False + success = False - logger.debug("Opening URL: {0}".format(url), section) - try: - r = requests.post(url, params=params, stream=True, verify=False, timeout=(30, 300)) - except requests.ConnectionError: - logger.error("Unable to open URL", section) - return [1, "{0}: Failed to post-process - Unable to connect to {1}".format(section, section)] - if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: - logger.error("Server returned status {0}".format(r.status_code), section) - return [1, "{0}: Failed to post-process - Server returned status {1}".format(section, r.status_code)] + logger.debug("Opening URL: {0}".format(url), section) + try: + r = requests.post(url, params=params, stream=True, verify=False, timeout=(30, 300)) + except requests.ConnectionError: + logger.error("Unable to open URL", section) + return [1, "{0}: Failed to post-process - Unable to connect to {1}".format(section, section)] + if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: + logger.error("Server returned status {0}".format(r.status_code), section) + return [1, "{0}: Failed to post-process - Server returned status {1}".format(section, r.status_code)] - result = r.content - if not type(result) == list: - result = result.split('\n') - for line in result: - if line: - logger.postprocess("{0}".format(line), section) - if "Post Processing SUCCESSFUL" in line: - success = True + result = r.content + if not type(result) == list: + result = result.split('\n') + for line in result: + if line: + logger.postprocess("{0}".format(line), section) + if "Post Processing SUCCESSFUL" in line: + success = True - if success: - logger.postprocess("SUCCESS: This issue has been processed successfully", section) - return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] - else: - logger.warning("The issue does not appear to have successfully processed. Please check your Logs", section) - return [1, "{0}: Failed to post-process - Returned log from {1} was not as expected.".format(section, section)] + if success: + logger.postprocess("SUCCESS: This issue has been processed successfully", section) + return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] + else: + logger.warning("The issue does not appear to have successfully processed. Please check your Logs", section) + return [1, "{0}: Failed to post-process - Returned log from {1} was not as expected.".format(section, section)] diff --git a/core/auto_process/games.py b/core/auto_process/games.py index 28183095..38d88782 100644 --- a/core/auto_process/games.py +++ b/core/auto_process/games.py @@ -12,67 +12,66 @@ from core.utils import convert_to_ascii, server_responding requests.packages.urllib3.disable_warnings() -class Game(object): - def process(self, section, dir_name, input_name=None, status=0, client_agent='manual', input_category=None): - status = int(status) +def process(section, dir_name, input_name=None, status=0, client_agent='manual', input_category=None): + status = int(status) - cfg = dict(core.CFG[section][input_category]) + cfg = dict(core.CFG[section][input_category]) - host = cfg["host"] - port = cfg["port"] - apikey = cfg["apikey"] - library = cfg.get("library") - ssl = int(cfg.get("ssl", 0)) - web_root = cfg.get("web_root", "") - protocol = "https://" if ssl else "http://" + host = cfg["host"] + port = cfg["port"] + apikey = cfg["apikey"] + library = cfg.get("library") + ssl = int(cfg.get("ssl", 0)) + web_root = cfg.get("web_root", "") + protocol = "https://" if ssl else "http://" - url = "{0}{1}:{2}{3}/api".format(protocol, host, port, web_root) - if not server_responding(url): - logger.error("Server did not respond. Exiting", section) - return [1, "{0}: Failed to post-process - {1} did not respond.".format(section, section)] + url = "{0}{1}:{2}{3}/api".format(protocol, host, port, web_root) + if not server_responding(url): + logger.error("Server did not respond. Exiting", section) + return [1, "{0}: Failed to post-process - {1} did not respond.".format(section, section)] - input_name, dir_name = convert_to_ascii(input_name, dir_name) + input_name, dir_name = convert_to_ascii(input_name, dir_name) - fields = input_name.split("-") + fields = input_name.split("-") - gamez_id = fields[0].replace("[", "").replace("]", "").replace(" ", "") + gamez_id = fields[0].replace("[", "").replace("]", "").replace(" ", "") - download_status = 'Downloaded' if status == 0 else 'Wanted' + download_status = 'Downloaded' if status == 0 else 'Wanted' - params = { - 'api_key': apikey, - 'mode': 'UPDATEREQUESTEDSTATUS', - 'db_id': gamez_id, - 'status': download_status - } + params = { + 'api_key': apikey, + 'mode': 'UPDATEREQUESTEDSTATUS', + 'db_id': gamez_id, + 'status': download_status + } - logger.debug("Opening URL: {0}".format(url), section) + logger.debug("Opening URL: {0}".format(url), section) + try: + r = requests.get(url, params=params, verify=False, timeout=(30, 300)) + except requests.ConnectionError: + logger.error("Unable to open URL") + return [1, "{0}: Failed to post-process - Unable to connect to {1}".format(section, section)] + + result = r.json() + logger.postprocess("{0}".format(result), section) + if library: + logger.postprocess("moving files to library: {0}".format(library), section) try: - r = requests.get(url, params=params, verify=False, timeout=(30, 300)) - except requests.ConnectionError: - logger.error("Unable to open URL") - return [1, "{0}: Failed to post-process - Unable to connect to {1}".format(section, section)] + shutil.move(dir_name, os.path.join(library, input_name)) + except Exception: + logger.error("Unable to move {0} to {1}".format(dir_name, os.path.join(library, input_name)), section) + return [1, "{0}: Failed to post-process - Unable to move files".format(section)] + else: + logger.error("No library specified to move files to. Please edit your configuration.", section) + return [1, "{0}: Failed to post-process - No library defined in {1}".format(section, section)] - result = r.json() - logger.postprocess("{0}".format(result), section) - if library: - logger.postprocess("moving files to library: {0}".format(library), section) - try: - shutil.move(dir_name, os.path.join(library, input_name)) - except Exception: - logger.error("Unable to move {0} to {1}".format(dir_name, os.path.join(library, input_name)), section) - return [1, "{0}: Failed to post-process - Unable to move files".format(section)] - else: - logger.error("No library specified to move files to. Please edit your configuration.", section) - return [1, "{0}: Failed to post-process - No library defined in {1}".format(section, section)] - - if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: - logger.error("Server returned status {0}".format(r.status_code), section) - return [1, "{0}: Failed to post-process - Server returned status {1}".format(section, r.status_code)] - elif result['success']: - logger.postprocess("SUCCESS: Status for {0} has been set to {1} in Gamez".format(gamez_id, download_status), section) - return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] - else: - logger.error("FAILED: Status for {0} has NOT been updated in Gamez".format(gamez_id), section) - return [1, "{0}: Failed to post-process - Returned log from {1} was not as expected.".format(section, section)] + if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: + logger.error("Server returned status {0}".format(r.status_code), section) + return [1, "{0}: Failed to post-process - Server returned status {1}".format(section, r.status_code)] + elif result['success']: + logger.postprocess("SUCCESS: Status for {0} has been set to {1} in Gamez".format(gamez_id, download_status), section) + return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] + else: + logger.error("FAILED: Status for {0} has NOT been updated in Gamez".format(gamez_id), section) + return [1, "{0}: Failed to post-process - Returned log from {1} was not as expected.".format(section, section)] diff --git a/core/auto_process/movies.py b/core/auto_process/movies.py index 152353e0..a82529fd 100644 --- a/core/auto_process/movies.py +++ b/core/auto_process/movies.py @@ -14,250 +14,228 @@ from core.utils import convert_to_ascii, find_download, find_imdbid, import_subs requests.packages.urllib3.disable_warnings() -class Movie(object): - def process(self, section, dir_name, input_name=None, status=0, client_agent="manual", download_id="", input_category=None, failure_link=None): +def process(section, dir_name, input_name=None, status=0, client_agent="manual", download_id="", input_category=None, failure_link=None): - cfg = dict(core.CFG[section][input_category]) + cfg = dict(core.CFG[section][input_category]) - host = cfg["host"] - port = cfg["port"] - apikey = cfg["apikey"] + host = cfg["host"] + port = cfg["port"] + apikey = cfg["apikey"] + if section == "CouchPotato": + method = cfg["method"] + else: + method = None + # added importMode for Radarr config + if section == "Radarr": + import_mode = cfg.get("importMode", "Move") + else: + import_mode = None + delete_failed = int(cfg["delete_failed"]) + wait_for = int(cfg["wait_for"]) + ssl = int(cfg.get("ssl", 0)) + web_root = cfg.get("web_root", "") + remote_path = int(cfg.get("remote_path", 0)) + protocol = "https://" if ssl else "http://" + omdbapikey = cfg.get("omdbapikey", "") + status = int(status) + if status > 0 and core.NOEXTRACTFAILED: + extract = 0 + else: + extract = int(cfg.get("extract", 0)) + + imdbid = find_imdbid(dir_name, input_name, omdbapikey) + if section == "CouchPotato": + base_url = "{0}{1}:{2}{3}/api/{4}/".format(protocol, host, port, web_root, apikey) + if section == "Radarr": + base_url = "{0}{1}:{2}{3}/api/command".format(protocol, host, port, web_root) + url2 = "{0}{1}:{2}{3}/api/config/downloadClient".format(protocol, host, port, web_root) + headers = {'X-Api-Key': apikey} + if not apikey: + logger.info('No CouchPotato or Radarr apikey entered. Performing transcoder functions only') + release = None + elif server_responding(base_url): if section == "CouchPotato": - method = cfg["method"] + release = get_release(base_url, imdbid, download_id) else: - method = None - # added importMode for Radarr config - if section == "Radarr": - import_mode = cfg.get("importMode", "Move") - else: - import_mode = None - delete_failed = int(cfg["delete_failed"]) - wait_for = int(cfg["wait_for"]) - ssl = int(cfg.get("ssl", 0)) - web_root = cfg.get("web_root", "") - remote_path = int(cfg.get("remote_path", 0)) - protocol = "https://" if ssl else "http://" - omdbapikey = cfg.get("omdbapikey", "") - status = int(status) - if status > 0 and core.NOEXTRACTFAILED: - extract = 0 - else: - extract = int(cfg.get("extract", 0)) - - imdbid = find_imdbid(dir_name, input_name, omdbapikey) - if section == "CouchPotato": - base_url = "{0}{1}:{2}{3}/api/{4}/".format(protocol, host, port, web_root, apikey) - if section == "Radarr": - base_url = "{0}{1}:{2}{3}/api/command".format(protocol, host, port, web_root) - url2 = "{0}{1}:{2}{3}/api/config/downloadClient".format(protocol, host, port, web_root) - headers = {'X-Api-Key': apikey} - if not apikey: - logger.info('No CouchPotato or Radarr apikey entered. Performing transcoder functions only') release = None - elif server_responding(base_url): - if section == "CouchPotato": - release = self.get_release(base_url, imdbid, download_id) - else: - release = None - else: - logger.error("Server did not respond. Exiting", section) - return [1, "{0}: Failed to post-process - {1} did not respond.".format(section, section)] + else: + logger.error("Server did not respond. Exiting", section) + return [1, "{0}: Failed to post-process - {1} did not respond.".format(section, section)] - # pull info from release found if available - release_id = None - media_id = None - downloader = None - release_status_old = None - if release: - try: - release_id = list(release.keys())[0] - media_id = release[release_id]['media_id'] - download_id = release[release_id]['download_info']['id'] - downloader = release[release_id]['download_info']['downloader'] - release_status_old = release[release_id]['status'] - except Exception: - pass + # pull info from release found if available + release_id = None + media_id = None + downloader = None + release_status_old = None + if release: + try: + release_id = list(release.keys())[0] + media_id = release[release_id]['media_id'] + download_id = release[release_id]['download_info']['id'] + downloader = release[release_id]['download_info']['downloader'] + release_status_old = release[release_id]['status'] + except Exception: + pass - if not os.path.isdir(dir_name) and os.path.isfile(dir_name): # If the input directory is a file, assume single file download and split dir/name. - dir_name = os.path.split(os.path.normpath(dir_name))[0] + if not os.path.isdir(dir_name) and os.path.isfile(dir_name): # If the input directory is a file, assume single file download and split dir/name. + dir_name = os.path.split(os.path.normpath(dir_name))[0] - specific_path = os.path.join(dir_name, str(input_name)) - clean_name = os.path.splitext(specific_path) - if clean_name[1] == ".nzb": - specific_path = clean_name[0] - if os.path.isdir(specific_path): - dir_name = specific_path + specific_path = os.path.join(dir_name, str(input_name)) + clean_name = os.path.splitext(specific_path) + if clean_name[1] == ".nzb": + specific_path = clean_name[0] + if os.path.isdir(specific_path): + dir_name = specific_path - process_all_exceptions(input_name, dir_name) + process_all_exceptions(input_name, dir_name) + input_name, dir_name = convert_to_ascii(input_name, dir_name) + + if not list_media_files(dir_name, media=True, audio=False, meta=False, archives=False) and list_media_files(dir_name, media=False, audio=False, meta=False, archives=True) and extract: + logger.debug('Checking for archives to extract in directory: {0}'.format(dir_name)) + core.extract_files(dir_name) input_name, dir_name = convert_to_ascii(input_name, dir_name) - if not list_media_files(dir_name, media=True, audio=False, meta=False, archives=False) and list_media_files(dir_name, media=False, audio=False, meta=False, archives=True) and extract: - logger.debug('Checking for archives to extract in directory: {0}'.format(dir_name)) - core.extract_files(dir_name) - input_name, dir_name = convert_to_ascii(input_name, dir_name) + good_files = 0 + num_files = 0 + # Check video files for corruption + for video in list_media_files(dir_name, media=True, audio=False, meta=False, archives=False): + num_files += 1 + if transcoder.is_video_good(video, status): + import_subs(video) + good_files += 1 + if num_files and good_files == num_files: + if status: + logger.info("Status shown as failed from Downloader, but {0} valid video files found. Setting as success.".format(good_files), section) + status = 0 + elif num_files and good_files < num_files: + logger.info("Status shown as success from Downloader, but corrupt video files found. Setting as failed.", section) + if 'NZBOP_VERSION' in os.environ and os.environ['NZBOP_VERSION'][0:5] >= '14.0': + print('[NZB] MARK=BAD') + if failure_link: + failure_link += '&corrupt=true' + status = 1 + elif client_agent == "manual": + logger.warning("No media files found in directory {0} to manually process.".format(dir_name), section) + return [0, ""] # Success (as far as this script is concerned) + else: + logger.warning("No media files found in directory {0}. Processing this as a failed download".format(dir_name), section) + status = 1 + if 'NZBOP_VERSION' in os.environ and os.environ['NZBOP_VERSION'][0:5] >= '14.0': + print('[NZB] MARK=BAD') - good_files = 0 - num_files = 0 - # Check video files for corruption - for video in list_media_files(dir_name, media=True, audio=False, meta=False, archives=False): - num_files += 1 - if transcoder.is_video_good(video, status): - import_subs(video) - good_files += 1 - if num_files and good_files == num_files: - if status: - logger.info("Status shown as failed from Downloader, but {0} valid video files found. Setting as success.".format(good_files), section) - status = 0 - elif num_files and good_files < num_files: - logger.info("Status shown as success from Downloader, but corrupt video files found. Setting as failed.", section) - if 'NZBOP_VERSION' in os.environ and os.environ['NZBOP_VERSION'][0:5] >= '14.0': - print('[NZB] MARK=BAD') - if failure_link: - failure_link += '&corrupt=true' - status = 1 - elif client_agent == "manual": - logger.warning("No media files found in directory {0} to manually process.".format(dir_name), section) - return [0, ""] # Success (as far as this script is concerned) - else: - logger.warning("No media files found in directory {0}. Processing this as a failed download".format(dir_name), section) - status = 1 - if 'NZBOP_VERSION' in os.environ and os.environ['NZBOP_VERSION'][0:5] >= '14.0': - print('[NZB] MARK=BAD') + if status == 0: + if core.TRANSCODE == 1: + result, new_dir_name = transcoder.transcode_directory(dir_name) + if result == 0: + logger.debug("Transcoding succeeded for files in {0}".format(dir_name), section) + dir_name = new_dir_name - if status == 0: - if core.TRANSCODE == 1: - result, new_dir_name = transcoder.transcode_directory(dir_name) - if result == 0: - logger.debug("Transcoding succeeded for files in {0}".format(dir_name), section) - dir_name = new_dir_name - - chmod_directory = int(str(cfg.get("chmodDirectory", "0")), 8) - logger.debug("Config setting 'chmodDirectory' currently set to {0}".format(oct(chmod_directory)), section) - if chmod_directory: - logger.info("Attempting to set the octal permission of '{0}' on directory '{1}'".format(oct(chmod_directory), dir_name), section) - core.rchmod(dir_name, chmod_directory) - else: - logger.error("Transcoding failed for files in {0}".format(dir_name), section) - return [1, "{0}: Failed to post-process - Transcoding failed".format(section)] - for video in list_media_files(dir_name, media=True, audio=False, meta=False, archives=False): - if not release and ".cp(tt" not in video and imdbid: - video_name, video_ext = os.path.splitext(video) - video2 = "{0}.cp({1}){2}".format(video_name, imdbid, video_ext) - if not (client_agent in [core.TORRENT_CLIENTAGENT, 'manual'] and core.USELINK == 'move-sym'): - logger.debug('Renaming: {0} to: {1}'.format(video, video2)) - os.rename(video, video2) - - if not apikey: # If only using Transcoder functions, exit here. - logger.info('No CouchPotato or Radarr apikey entered. Processing completed.') - return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] - - params = {} - if download_id and release_id: - params['downloader'] = downloader or client_agent - params['download_id'] = download_id - - params['media_folder'] = remote_dir(dir_name) if remote_path else dir_name - - if section == "CouchPotato": - if method == "manage": - command = "manage.update" - params = {} - else: - command = "renamer.scan" - - url = "{0}{1}".format(base_url, command) - logger.debug("Opening URL: {0} with PARAMS: {1}".format(url, params), section) - logger.postprocess("Starting {0} scan for {1}".format(method, input_name), section) - - if section == "Radarr": - payload = {'name': 'DownloadedMoviesScan', 'path': params['media_folder'], 'downloadClientId': download_id, 'importMode': import_mode} - if not download_id: - payload.pop("downloadClientId") - logger.debug("Opening URL: {0} with PARAMS: {1}".format(base_url, payload), section) - logger.postprocess("Starting DownloadedMoviesScan scan for {0}".format(input_name), section) - - try: - if section == "CouchPotato": - r = requests.get(url, params=params, verify=False, timeout=(30, 1800)) - else: - r = requests.post(base_url, data=json.dumps(payload), headers=headers, stream=True, verify=False, timeout=(30, 1800)) - except requests.ConnectionError: - logger.error("Unable to open URL", section) - return [1, "{0}: Failed to post-process - Unable to connect to {1}".format(section, section)] - - result = r.json() - if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: - logger.error("Server returned status {0}".format(r.status_code), section) - return [1, "{0}: Failed to post-process - Server returned status {1}".format(section, r.status_code)] - elif section == "CouchPotato" and result['success']: - logger.postprocess("SUCCESS: Finished {0} scan for folder {1}".format(method, dir_name), section) - if method == "manage": - return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] - elif section == "Radarr": - logger.postprocess("Radarr response: {0}".format(result['state'])) - try: - res = json.loads(r.content) - scan_id = int(res['id']) - logger.debug("Scan started with id: {0}".format(scan_id), section) - started = True - except Exception as e: - logger.warning("No scan id was returned due to: {0}".format(e), section) - scan_id = None + chmod_directory = int(str(cfg.get("chmodDirectory", "0")), 8) + logger.debug("Config setting 'chmodDirectory' currently set to {0}".format(oct(chmod_directory)), section) + if chmod_directory: + logger.info("Attempting to set the octal permission of '{0}' on directory '{1}'".format(oct(chmod_directory), dir_name), section) + core.rchmod(dir_name, chmod_directory) else: - logger.error("FAILED: {0} scan was unable to finish for folder {1}. exiting!".format(method, dir_name), - section) - return [1, "{0}: Failed to post-process - Server did not return success".format(section)] + logger.error("Transcoding failed for files in {0}".format(dir_name), section) + return [1, "{0}: Failed to post-process - Transcoding failed".format(section)] + for video in list_media_files(dir_name, media=True, audio=False, meta=False, archives=False): + if not release and ".cp(tt" not in video and imdbid: + video_name, video_ext = os.path.splitext(video) + video2 = "{0}.cp({1}){2}".format(video_name, imdbid, video_ext) + if not (client_agent in [core.TORRENT_CLIENTAGENT, 'manual'] and core.USELINK == 'move-sym'): + logger.debug('Renaming: {0} to: {1}'.format(video, video2)) + os.rename(video, video2) + if not apikey: # If only using Transcoder functions, exit here. + logger.info('No CouchPotato or Radarr apikey entered. Processing completed.') + return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] + + params = {} + if download_id and release_id: + params['downloader'] = downloader or client_agent + params['download_id'] = download_id + + params['media_folder'] = remote_dir(dir_name) if remote_path else dir_name + + if section == "CouchPotato": + if method == "manage": + command = "manage.update" + params = {} + else: + command = "renamer.scan" + + url = "{0}{1}".format(base_url, command) + logger.debug("Opening URL: {0} with PARAMS: {1}".format(url, params), section) + logger.postprocess("Starting {0} scan for {1}".format(method, input_name), section) + + if section == "Radarr": + payload = {'name': 'DownloadedMoviesScan', 'path': params['media_folder'], 'downloadClientId': download_id, 'importMode': import_mode} + if not download_id: + payload.pop("downloadClientId") + logger.debug("Opening URL: {0} with PARAMS: {1}".format(base_url, payload), section) + logger.postprocess("Starting DownloadedMoviesScan scan for {0}".format(input_name), section) + + try: + if section == "CouchPotato": + r = requests.get(url, params=params, verify=False, timeout=(30, 1800)) + else: + r = requests.post(base_url, data=json.dumps(payload), headers=headers, stream=True, verify=False, timeout=(30, 1800)) + except requests.ConnectionError: + logger.error("Unable to open URL", section) + return [1, "{0}: Failed to post-process - Unable to connect to {1}".format(section, section)] + + result = r.json() + if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: + logger.error("Server returned status {0}".format(r.status_code), section) + return [1, "{0}: Failed to post-process - Server returned status {1}".format(section, r.status_code)] + elif section == "CouchPotato" and result['success']: + logger.postprocess("SUCCESS: Finished {0} scan for folder {1}".format(method, dir_name), section) + if method == "manage": + return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] + elif section == "Radarr": + logger.postprocess("Radarr response: {0}".format(result['state'])) + try: + res = json.loads(r.content) + scan_id = int(res['id']) + logger.debug("Scan started with id: {0}".format(scan_id), section) + started = True + except Exception as e: + logger.warning("No scan id was returned due to: {0}".format(e), section) + scan_id = None else: - core.FAILED = True - logger.postprocess("FAILED DOWNLOAD DETECTED FOR {0}".format(input_name), section) - if failure_link: - report_nzb(failure_link, client_agent) + logger.error("FAILED: {0} scan was unable to finish for folder {1}. exiting!".format(method, dir_name), + section) + return [1, "{0}: Failed to post-process - Server did not return success".format(section)] - if section == "Radarr": - logger.postprocess("FAILED: The download failed. Sending failed download to {0} for CDH processing".format(section), section) - return [1, "{0}: Download Failed. Sending back to {1}".format(section, section)] # Return as failed to flag this in the downloader. + else: + core.FAILED = True + logger.postprocess("FAILED DOWNLOAD DETECTED FOR {0}".format(input_name), section) + if failure_link: + report_nzb(failure_link, client_agent) - if delete_failed and os.path.isdir(dir_name) and not os.path.dirname(dir_name) == dir_name: - logger.postprocess("Deleting failed files and folder {0}".format(dir_name), section) - remove_dir(dir_name) + if section == "Radarr": + logger.postprocess("FAILED: The download failed. Sending failed download to {0} for CDH processing".format(section), section) + return [1, "{0}: Download Failed. Sending back to {1}".format(section, section)] # Return as failed to flag this in the downloader. - if not release_id and not media_id: - logger.error("Could not find a downloaded movie in the database matching {0}, exiting!".format(input_name), - section) - return [1, "{0}: Failed to post-process - Failed download not found in {1}".format(section, section)] + if delete_failed and os.path.isdir(dir_name) and not os.path.dirname(dir_name) == dir_name: + logger.postprocess("Deleting failed files and folder {0}".format(dir_name), section) + remove_dir(dir_name) - if release_id: - logger.postprocess("Setting failed release {0} to ignored ...".format(input_name), section) + if not release_id and not media_id: + logger.error("Could not find a downloaded movie in the database matching {0}, exiting!".format(input_name), + section) + return [1, "{0}: Failed to post-process - Failed download not found in {1}".format(section, section)] - url = "{url}release.ignore".format(url=base_url) - params = {'id': release_id} + if release_id: + logger.postprocess("Setting failed release {0} to ignored ...".format(input_name), section) - logger.debug("Opening URL: {0} with PARAMS: {1}".format(url, params), section) + url = "{url}release.ignore".format(url=base_url) + params = {'id': release_id} - try: - r = requests.get(url, params=params, verify=False, timeout=(30, 120)) - except requests.ConnectionError: - logger.error("Unable to open URL {0}".format(url), section) - return [1, "{0}: Failed to post-process - Unable to connect to {1}".format(section, section)] - - result = r.json() - if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: - logger.error("Server returned status {0}".format(r.status_code), section) - return [1, "{0}: Failed to post-process - Server returned status {1}".format(section, r.status_code)] - elif result['success']: - logger.postprocess("SUCCESS: {0} has been set to ignored ...".format(input_name), section) - else: - logger.warning("FAILED: Unable to set {0} to ignored!".format(input_name), section) - return [1, "{0}: Failed to post-process - Unable to set {1} to ignored".format(section, input_name)] - - logger.postprocess("Trying to snatch the next highest ranked release.", section) - - url = "{0}movie.searcher.try_next".format(base_url) - logger.debug("Opening URL: {0}".format(url), section) + logger.debug("Opening URL: {0} with PARAMS: {1}".format(url, params), section) try: - r = requests.get(url, params={'media_id': media_id}, verify=False, timeout=(30, 600)) + r = requests.get(url, params=params, verify=False, timeout=(30, 120)) except requests.ConnectionError: logger.error("Unable to open URL {0}".format(url), section) return [1, "{0}: Failed to post-process - Unable to connect to {1}".format(section, section)] @@ -267,200 +245,224 @@ class Movie(object): logger.error("Server returned status {0}".format(r.status_code), section) return [1, "{0}: Failed to post-process - Server returned status {1}".format(section, r.status_code)] elif result['success']: - logger.postprocess("SUCCESS: Snatched the next highest release ...", section) - return [0, "{0}: Successfully snatched next highest release".format(section)] + logger.postprocess("SUCCESS: {0} has been set to ignored ...".format(input_name), section) else: - logger.postprocess("SUCCESS: Unable to find a new release to snatch now. CP will keep searching!", section) - return [0, "{0}: No new release found now. {1} will keep searching".format(section, section)] + logger.warning("FAILED: Unable to set {0} to ignored!".format(input_name), section) + return [1, "{0}: Failed to post-process - Unable to set {1} to ignored".format(section, input_name)] - # Added a release that was not in the wanted list so confirm rename successful by finding this movie media.list. - if not release: - download_id = None # we don't want to filter new releases based on this. + logger.postprocess("Trying to snatch the next highest ranked release.", section) - # we will now check to see if CPS has finished renaming before returning to TorrentToMedia and unpausing. - timeout = time.time() + 60 * wait_for - while time.time() < timeout: # only wait 2 (default) minutes, then return. - logger.postprocess("Checking for status change, please stand by ...", section) - if section == "CouchPotato": - release = self.get_release(base_url, imdbid, download_id, release_id) - scan_id = None - else: - release = None - if release: - try: - release_id = list(release.keys())[0] - title = release[release_id]['title'] - release_status_new = release[release_id]['status'] - if release_status_old is None: # we didn't have a release before, but now we do. - logger.postprocess("SUCCESS: Movie {0} has now been added to CouchPotato with release status of [{1}]".format( - title, str(release_status_new).upper()), section) - return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] + url = "{0}movie.searcher.try_next".format(base_url) + logger.debug("Opening URL: {0}".format(url), section) - if release_status_new != release_status_old: - logger.postprocess("SUCCESS: Release for {0} has now been marked with a status of [{1}]".format( - title, str(release_status_new).upper()), section) - return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] - except Exception: - pass - elif scan_id: - url = "{0}/{1}".format(base_url, scan_id) - command_status = self.command_complete(url, params, headers, section) - if command_status: - logger.debug("The Scan command return status: {0}".format(command_status), section) - if command_status in ['completed']: - logger.debug("The Scan command has completed successfully. Renaming was successful.", section) - return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] - elif command_status in ['failed']: - logger.debug("The Scan command has failed. Renaming was not successful.", section) - # return [1, "%s: Failed to post-process %s" % (section, input_name) ] - - if not os.path.isdir(dir_name): - logger.postprocess("SUCCESS: Input Directory [{0}] has been processed and removed".format( - dir_name), section) - return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] - - elif not list_media_files(dir_name, media=True, audio=False, meta=False, archives=True): - logger.postprocess("SUCCESS: Input Directory [{0}] has no remaining media files. This has been fully processed.".format( - dir_name), section) - return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] - - # pause and let CouchPotatoServer/Radarr catch its breath - time.sleep(10 * wait_for) - - # The status hasn't changed. we have waited wait_for minutes which is more than enough. uTorrent can resume seeding now. - if section == "Radarr" and self.completed_download_handling(url2, headers, section=section): - logger.debug("The Scan command did not return status completed, but complete Download Handling is enabled. Passing back to {0}.".format(section), section) - return [status, "{0}: Complete DownLoad Handling is enabled. Passing back to {1}".format(section, section)] - logger.warning( - "{0} does not appear to have changed status after {1} minutes, Please check your logs.".format(input_name, wait_for), - section) - return [1, "{0}: Failed to post-process - No change in status".format(section)] - - def command_complete(self, url, params, headers, section): try: - r = requests.get(url, params=params, headers=headers, stream=True, verify=False, timeout=(30, 60)) + r = requests.get(url, params={'media_id': media_id}, verify=False, timeout=(30, 600)) except requests.ConnectionError: - logger.error("Unable to open URL: {0}".format(url), section) - return None + logger.error("Unable to open URL {0}".format(url), section) + return [1, "{0}: Failed to post-process - Unable to connect to {1}".format(section, section)] + + result = r.json() if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: logger.error("Server returned status {0}".format(r.status_code), section) - return None + return [1, "{0}: Failed to post-process - Server returned status {1}".format(section, r.status_code)] + elif result['success']: + logger.postprocess("SUCCESS: Snatched the next highest release ...", section) + return [0, "{0}: Successfully snatched next highest release".format(section)] else: - try: - return r.json()['state'] - except (ValueError, KeyError): - # ValueError catches simplejson's JSONDecodeError and json's ValueError - logger.error("{0} did not return expected json data.".format(section), section) - return None + logger.postprocess("SUCCESS: Unable to find a new release to snatch now. CP will keep searching!", section) + return [0, "{0}: No new release found now. {1} will keep searching".format(section, section)] - def completed_download_handling(self, url2, headers, section="MAIN"): - try: - r = requests.get(url2, params={}, headers=headers, stream=True, verify=False, timeout=(30, 60)) - except requests.ConnectionError: - logger.error("Unable to open URL: {0}".format(url2), section) - return False - if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: - logger.error("Server returned status {0}".format(r.status_code), section) - return False + # Added a release that was not in the wanted list so confirm rename successful by finding this movie media.list. + if not release: + download_id = None # we don't want to filter new releases based on this. + + # we will now check to see if CPS has finished renaming before returning to TorrentToMedia and unpausing. + timeout = time.time() + 60 * wait_for + while time.time() < timeout: # only wait 2 (default) minutes, then return. + logger.postprocess("Checking for status change, please stand by ...", section) + if section == "CouchPotato": + release = get_release(base_url, imdbid, download_id, release_id) + scan_id = None else: + release = None + if release: try: - return r.json().get("enableCompletedDownloadHandling", False) - except ValueError: - # ValueError catches simplejson's JSONDecodeError and json's ValueError - return False + release_id = list(release.keys())[0] + title = release[release_id]['title'] + release_status_new = release[release_id]['status'] + if release_status_old is None: # we didn't have a release before, but now we do. + logger.postprocess("SUCCESS: Movie {0} has now been added to CouchPotato with release status of [{1}]".format( + title, str(release_status_new).upper()), section) + return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] - def get_release(self, base_url, imdb_id=None, download_id=None, release_id=None): - results = {} - params = {} - - # determine cmd and params to send to CouchPotato to get our results - section = 'movies' - cmd = "media.list" - if release_id or imdb_id: - section = 'media' - cmd = "media.get" - params['id'] = release_id or imdb_id - - if not (release_id or imdb_id or download_id): - logger.debug("No information available to filter CP results") - return results - - url = "{0}{1}".format(base_url, cmd) - logger.debug("Opening URL: {0} with PARAMS: {1}".format(url, params)) - - try: - r = requests.get(url, params=params, verify=False, timeout=(30, 60)) - except requests.ConnectionError: - logger.error("Unable to open URL {0}".format(url)) - return results - - try: - result = r.json() - except ValueError: - # ValueError catches simplejson's JSONDecodeError and json's ValueError - logger.error("CouchPotato returned the following non-json data") - for line in r.iter_lines(): - logger.error("{0}".format(line)) - return results - - if not result['success']: - if 'error' in result: - logger.error('{0}'.format(result['error'])) - else: - logger.error("no media found for id {0}".format(params['id'])) - return results - - # Gather release info and return it back, no need to narrow results - if release_id: - try: - cur_id = result[section]['_id'] - results[cur_id] = result[section] - return results + if release_status_new != release_status_old: + logger.postprocess("SUCCESS: Release for {0} has now been marked with a status of [{1}]".format( + title, str(release_status_new).upper()), section) + return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] except Exception: pass + elif scan_id: + url = "{0}/{1}".format(base_url, scan_id) + command_status = command_complete(url, params, headers, section) + if command_status: + logger.debug("The Scan command return status: {0}".format(command_status), section) + if command_status in ['completed']: + logger.debug("The Scan command has completed successfully. Renaming was successful.", section) + return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] + elif command_status in ['failed']: + logger.debug("The Scan command has failed. Renaming was not successful.", section) + # return [1, "%s: Failed to post-process %s" % (section, input_name) ] - # Gather release info and proceed with trying to narrow results to one release choice + if not os.path.isdir(dir_name): + logger.postprocess("SUCCESS: Input Directory [{0}] has been processed and removed".format( + dir_name), section) + return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] - movies = result[section] - if not isinstance(movies, list): - movies = [movies] - for movie in movies: - if movie['status'] not in ['active', 'done']: - continue - releases = movie['releases'] - if not releases: - continue - for release in releases: - try: - if release['status'] not in ['snatched', 'downloaded', 'done']: - continue - if download_id: - if download_id.lower() != release['download_info']['id'].lower(): - continue + elif not list_media_files(dir_name, media=True, audio=False, meta=False, archives=True): + logger.postprocess("SUCCESS: Input Directory [{0}] has no remaining media files. This has been fully processed.".format( + dir_name), section) + return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] - cur_id = release['_id'] - results[cur_id] = release - results[cur_id]['title'] = movie['title'] - except Exception: - continue + # pause and let CouchPotatoServer/Radarr catch its breath + time.sleep(10 * wait_for) - # Narrow results by removing old releases by comparing their last_edit field - if len(results) > 1: - for id1, x1 in results.items(): - for id2, x2 in results.items(): - try: - if x2["last_edit"] > x1["last_edit"]: - results.pop(id1) - except Exception: - continue + # The status hasn't changed. we have waited wait_for minutes which is more than enough. uTorrent can resume seeding now. + if section == "Radarr" and completed_download_handling(url2, headers, section=section): + logger.debug("The Scan command did not return status completed, but complete Download Handling is enabled. Passing back to {0}.".format(section), section) + return [status, "{0}: Complete DownLoad Handling is enabled. Passing back to {1}".format(section, section)] + logger.warning( + "{0} does not appear to have changed status after {1} minutes, Please check your logs.".format(input_name, wait_for), + section) + return [1, "{0}: Failed to post-process - No change in status".format(section)] - # Search downloads on clients for a match to try and narrow our results down to 1 - if len(results) > 1: - for cur_id, x in results.items(): - try: - if not find_download(str(x['download_info']['downloader']).lower(), x['download_info']['id']): - results.pop(cur_id) - except Exception: - continue +def command_complete(url, params, headers, section): + try: + r = requests.get(url, params=params, headers=headers, stream=True, verify=False, timeout=(30, 60)) + except requests.ConnectionError: + logger.error("Unable to open URL: {0}".format(url), section) + return None + if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: + logger.error("Server returned status {0}".format(r.status_code), section) + return None + else: + try: + return r.json()['state'] + except (ValueError, KeyError): + # ValueError catches simplejson's JSONDecodeError and json's ValueError + logger.error("{0} did not return expected json data.".format(section), section) + return None + + +def completed_download_handling(url2, headers, section="MAIN"): + try: + r = requests.get(url2, params={}, headers=headers, stream=True, verify=False, timeout=(30, 60)) + except requests.ConnectionError: + logger.error("Unable to open URL: {0}".format(url2), section) + return False + if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: + logger.error("Server returned status {0}".format(r.status_code), section) + return False + else: + try: + return r.json().get("enableCompletedDownloadHandling", False) + except ValueError: + # ValueError catches simplejson's JSONDecodeError and json's ValueError + return False + + +def get_release(base_url, imdb_id=None, download_id=None, release_id=None): + results = {} + params = {} + + # determine cmd and params to send to CouchPotato to get our results + section = 'movies' + cmd = "media.list" + if release_id or imdb_id: + section = 'media' + cmd = "media.get" + params['id'] = release_id or imdb_id + + if not (release_id or imdb_id or download_id): + logger.debug("No information available to filter CP results") return results + + url = "{0}{1}".format(base_url, cmd) + logger.debug("Opening URL: {0} with PARAMS: {1}".format(url, params)) + + try: + r = requests.get(url, params=params, verify=False, timeout=(30, 60)) + except requests.ConnectionError: + logger.error("Unable to open URL {0}".format(url)) + return results + + try: + result = r.json() + except ValueError: + # ValueError catches simplejson's JSONDecodeError and json's ValueError + logger.error("CouchPotato returned the following non-json data") + for line in r.iter_lines(): + logger.error("{0}".format(line)) + return results + + if not result['success']: + if 'error' in result: + logger.error('{0}'.format(result['error'])) + else: + logger.error("no media found for id {0}".format(params['id'])) + return results + + # Gather release info and return it back, no need to narrow results + if release_id: + try: + cur_id = result[section]['_id'] + results[cur_id] = result[section] + return results + except Exception: + pass + + # Gather release info and proceed with trying to narrow results to one release choice + + movies = result[section] + if not isinstance(movies, list): + movies = [movies] + for movie in movies: + if movie['status'] not in ['active', 'done']: + continue + releases = movie['releases'] + if not releases: + continue + for release in releases: + try: + if release['status'] not in ['snatched', 'downloaded', 'done']: + continue + if download_id: + if download_id.lower() != release['download_info']['id'].lower(): + continue + + cur_id = release['_id'] + results[cur_id] = release + results[cur_id]['title'] = movie['title'] + except Exception: + continue + + # Narrow results by removing old releases by comparing their last_edit field + if len(results) > 1: + for id1, x1 in results.items(): + for id2, x2 in results.items(): + try: + if x2["last_edit"] > x1["last_edit"]: + results.pop(id1) + except Exception: + continue + + # Search downloads on clients for a match to try and narrow our results down to 1 + if len(results) > 1: + for cur_id, x in results.items(): + try: + if not find_download(str(x['download_info']['downloader']).lower(), x['download_info']['id']): + results.pop(cur_id) + except Exception: + continue + + return results diff --git a/core/auto_process/music.py b/core/auto_process/music.py index 1275087d..88521774 100644 --- a/core/auto_process/music.py +++ b/core/auto_process/music.py @@ -14,226 +14,228 @@ from core.utils import convert_to_ascii, list_media_files, remote_dir, remove_di requests.packages.urllib3.disable_warnings() -class Music(object): - def process(self, section, dir_name, input_name=None, status=0, client_agent="manual", input_category=None): - status = int(status) +def process(section, dir_name, input_name=None, status=0, client_agent="manual", input_category=None): + status = int(status) - cfg = dict(core.CFG[section][input_category]) + cfg = dict(core.CFG[section][input_category]) - host = cfg["host"] - port = cfg["port"] - apikey = cfg["apikey"] - wait_for = int(cfg["wait_for"]) - ssl = int(cfg.get("ssl", 0)) - delete_failed = int(cfg["delete_failed"]) - web_root = cfg.get("web_root", "") - remote_path = int(cfg.get("remote_path", 0)) - protocol = "https://" if ssl else "http://" - status = int(status) - if status > 0 and core.NOEXTRACTFAILED: - extract = 0 - else: - extract = int(cfg.get("extract", 0)) + host = cfg["host"] + port = cfg["port"] + apikey = cfg["apikey"] + wait_for = int(cfg["wait_for"]) + ssl = int(cfg.get("ssl", 0)) + delete_failed = int(cfg["delete_failed"]) + web_root = cfg.get("web_root", "") + remote_path = int(cfg.get("remote_path", 0)) + protocol = "https://" if ssl else "http://" + status = int(status) + if status > 0 and core.NOEXTRACTFAILED: + extract = 0 + else: + extract = int(cfg.get("extract", 0)) - if section == "Lidarr": - url = "{0}{1}:{2}{3}/api/v1".format(protocol, host, port, web_root) - else: - url = "{0}{1}:{2}{3}/api".format(protocol, host, port, web_root) - if not server_responding(url): - logger.error("Server did not respond. Exiting", section) - return [1, "{0}: Failed to post-process - {1} did not respond.".format(section, section)] + if section == "Lidarr": + url = "{0}{1}:{2}{3}/api/v1".format(protocol, host, port, web_root) + else: + url = "{0}{1}:{2}{3}/api".format(protocol, host, port, web_root) + if not server_responding(url): + logger.error("Server did not respond. Exiting", section) + return [1, "{0}: Failed to post-process - {1} did not respond.".format(section, section)] - if not os.path.isdir(dir_name) and os.path.isfile(dir_name): # If the input directory is a file, assume single file download and split dir/name. - dir_name = os.path.split(os.path.normpath(dir_name))[0] + if not os.path.isdir(dir_name) and os.path.isfile(dir_name): # If the input directory is a file, assume single file download and split dir/name. + dir_name = os.path.split(os.path.normpath(dir_name))[0] - specific_path = os.path.join(dir_name, str(input_name)) - clean_name = os.path.splitext(specific_path) - if clean_name[1] == ".nzb": - specific_path = clean_name[0] - if os.path.isdir(specific_path): - dir_name = specific_path + specific_path = os.path.join(dir_name, str(input_name)) + clean_name = os.path.splitext(specific_path) + if clean_name[1] == ".nzb": + specific_path = clean_name[0] + if os.path.isdir(specific_path): + dir_name = specific_path - process_all_exceptions(input_name, dir_name) + process_all_exceptions(input_name, dir_name) + input_name, dir_name = convert_to_ascii(input_name, dir_name) + + if not list_media_files(dir_name, media=False, audio=True, meta=False, archives=False) and list_media_files(dir_name, media=False, audio=False, meta=False, archives=True) and extract: + logger.debug('Checking for archives to extract in directory: {0}'.format(dir_name)) + core.extract_files(dir_name) input_name, dir_name = convert_to_ascii(input_name, dir_name) - if not list_media_files(dir_name, media=False, audio=True, meta=False, archives=False) and list_media_files(dir_name, media=False, audio=False, meta=False, archives=True) and extract: - logger.debug('Checking for archives to extract in directory: {0}'.format(dir_name)) - core.extract_files(dir_name) - input_name, dir_name = convert_to_ascii(input_name, dir_name) + # if listMediaFiles(dir_name, media=False, audio=True, meta=False, archives=False) and status: + # logger.info("Status shown as failed from Downloader, but valid video files found. Setting as successful.", section) + # status = 0 - # if listMediaFiles(dir_name, media=False, audio=True, meta=False, archives=False) and status: - # logger.info("Status shown as failed from Downloader, but valid video files found. Setting as successful.", section) - # status = 0 - - if status == 0 and section == "HeadPhones": - - params = { - 'apikey': apikey, - 'cmd': "forceProcess", - 'dir': remote_dir(dir_name) if remote_path else dir_name - } - - res = self.force_process(params, url, apikey, input_name, dir_name, section, wait_for) - if res[0] in [0, 1]: - return res - - params = { - 'apikey': apikey, - 'cmd': "forceProcess", - 'dir': os.path.split(remote_dir(dir_name))[0] if remote_path else os.path.split(dir_name)[0] - } - - res = self.force_process(params, url, apikey, input_name, dir_name, section, wait_for) - if res[0] in [0, 1]: - return res - - # The status hasn't changed. uTorrent can resume seeding now. - logger.warning("The music album does not appear to have changed status after {0} minutes. Please check your Logs".format(wait_for), section) - return [1, "{0}: Failed to post-process - No change in wanted status".format(section)] - - elif status == 0 and section == "Lidarr": - url = "{0}{1}:{2}{3}/api/v1/command".format(protocol, host, port, web_root) - headers = {"X-Api-Key": apikey} - if remote_path: - logger.debug("remote_path: {0}".format(remote_dir(dir_name)), section) - data = {"name": "Rename", "path": remote_dir(dir_name)} - else: - logger.debug("path: {0}".format(dir_name), section) - data = {"name": "Rename", "path": dir_name} - data = json.dumps(data) - try: - logger.debug("Opening URL: {0} with data: {1}".format(url, data), section) - r = requests.post(url, data=data, headers=headers, stream=True, verify=False, timeout=(30, 1800)) - except requests.ConnectionError: - logger.error("Unable to open URL: {0}".format(url), section) - return [1, "{0}: Failed to post-process - Unable to connect to {1}".format(section, section)] - - success = False - queued = False - started = False - try: - res = json.loads(r.content) - scan_id = int(res['id']) - logger.debug("Scan started with id: {0}".format(scan_id), section) - started = True - except Exception as e: - logger.warning("No scan id was returned due to: {0}".format(e), section) - scan_id = None - started = False - return [1, "{0}: Failed to post-process - Unable to start scan".format(section)] - - n = 0 - params = {} - url = "{0}/{1}".format(url, scan_id) - while n < 6: # set up wait_for minutes to see if command completes.. - time.sleep(10 * wait_for) - command_status = self.command_complete(url, params, headers, section) - if command_status and command_status in ['completed', 'failed']: - break - n += 1 - if command_status: - logger.debug("The Scan command return status: {0}".format(command_status), section) - if not os.path.exists(dir_name): - logger.debug("The directory {0} has been removed. Renaming was successful.".format(dir_name), section) - return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] - elif command_status and command_status in ['completed']: - logger.debug("The Scan command has completed successfully. Renaming was successful.", section) - return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] - elif command_status and command_status in ['failed']: - logger.debug("The Scan command has failed. Renaming was not successful.", section) - # return [1, "%s: Failed to post-process %s" % (section, input_name) ] - else: - logger.debug("The Scan command did not return status completed. Passing back to {0} to attempt complete download handling.".format(section), section) - return [status, "{0}: Passing back to {1} to attempt Complete Download Handling".format(section, section)] - - else: - if section == "Lidarr": - logger.postprocess("FAILED: The download failed. Sending failed download to {0} for CDH processing".format(section), section) - return [1, "{0}: Download Failed. Sending back to {1}".format(section, section)] # Return as failed to flag this in the downloader. - else: - logger.warning("FAILED DOWNLOAD DETECTED", section) - if delete_failed and os.path.isdir(dir_name) and not os.path.dirname(dir_name) == dir_name: - logger.postprocess("Deleting failed files and folder {0}".format(dir_name), section) - remove_dir(dir_name) - return [1, "{0}: Failed to post-process. {1} does not support failed downloads".format(section, section)] # Return as failed to flag this in the downloader. - - def command_complete(self, url, params, headers, section): - try: - r = requests.get(url, params=params, headers=headers, stream=True, verify=False, timeout=(30, 60)) - except requests.ConnectionError: - logger.error("Unable to open URL: {0}".format(url), section) - return None - if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: - logger.error("Server returned status {0}".format(r.status_code), section) - return None - else: - try: - return r.json()['state'] - except (ValueError, KeyError): - # ValueError catches simplejson's JSONDecodeError and json's ValueError - logger.error("{0} did not return expected json data.".format(section), section) - return None - - def get_status(self, url, apikey, dir_name): - logger.debug("Attempting to get current status for release:{0}".format(os.path.basename(dir_name))) + if status == 0 and section == "HeadPhones": params = { 'apikey': apikey, - 'cmd': "getHistory" + 'cmd': "forceProcess", + 'dir': remote_dir(dir_name) if remote_path else dir_name } - logger.debug("Opening URL: {0} with PARAMS: {1}".format(url, params)) + res = force_process(params, url, apikey, input_name, dir_name, section, wait_for) + if res[0] in [0, 1]: + return res + params = { + 'apikey': apikey, + 'cmd': "forceProcess", + 'dir': os.path.split(remote_dir(dir_name))[0] if remote_path else os.path.split(dir_name)[0] + } + + res = force_process(params, url, apikey, input_name, dir_name, section, wait_for) + if res[0] in [0, 1]: + return res + + # The status hasn't changed. uTorrent can resume seeding now. + logger.warning("The music album does not appear to have changed status after {0} minutes. Please check your Logs".format(wait_for), section) + return [1, "{0}: Failed to post-process - No change in wanted status".format(section)] + + elif status == 0 and section == "Lidarr": + url = "{0}{1}:{2}{3}/api/v1/command".format(protocol, host, port, web_root) + headers = {"X-Api-Key": apikey} + if remote_path: + logger.debug("remote_path: {0}".format(remote_dir(dir_name)), section) + data = {"name": "Rename", "path": remote_dir(dir_name)} + else: + logger.debug("path: {0}".format(dir_name), section) + data = {"name": "Rename", "path": dir_name} + data = json.dumps(data) try: - r = requests.get(url, params=params, verify=False, timeout=(30, 120)) - except requests.RequestException: - logger.error("Unable to open URL") - return None - - try: - result = r.json() - except ValueError: - # ValueError catches simplejson's JSONDecodeError and json's ValueError - return None - - for album in result: - if os.path.basename(dir_name) == album['FolderName']: - return album["Status"].lower() - - def force_process(self, params, url, apikey, input_name, dir_name, section, wait_for): - release_status = self.get_status(url, apikey, dir_name) - if not release_status: - logger.error("Could not find a status for {0}, is it in the wanted list ?".format(input_name), section) - - logger.debug("Opening URL: {0} with PARAMS: {1}".format(url, params), section) - - try: - r = requests.get(url, params=params, verify=False, timeout=(30, 300)) + logger.debug("Opening URL: {0} with data: {1}".format(url, data), section) + r = requests.post(url, data=data, headers=headers, stream=True, verify=False, timeout=(30, 1800)) except requests.ConnectionError: - logger.error("Unable to open URL {0}".format(url), section) + logger.error("Unable to open URL: {0}".format(url), section) return [1, "{0}: Failed to post-process - Unable to connect to {1}".format(section, section)] - logger.debug("Result: {0}".format(r.text), section) + success = False + queued = False + started = False + try: + res = json.loads(r.content) + scan_id = int(res['id']) + logger.debug("Scan started with id: {0}".format(scan_id), section) + started = True + except Exception as e: + logger.warning("No scan id was returned due to: {0}".format(e), section) + scan_id = None + started = False + return [1, "{0}: Failed to post-process - Unable to start scan".format(section)] - if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: - logger.error("Server returned status {0}".format(r.status_code), section) - return [1, "{0}: Failed to post-process - Server returned status {1}".format(section, r.status_code)] - elif r.text == "OK": - logger.postprocess("SUCCESS: Post-Processing started for {0} in folder {1} ...".format(input_name, dir_name), section) - else: - logger.error("FAILED: Post-Processing has NOT started for {0} in folder {1}. exiting!".format(input_name, dir_name), section) - return [1, "{0}: Failed to post-process - Returned log from {1} was not as expected.".format(section, section)] - - # we will now wait for this album to be processed before returning to TorrentToMedia and unpausing. - timeout = time.time() + 60 * wait_for - while time.time() < timeout: - current_status = self.get_status(url, apikey, dir_name) - if current_status is not None and current_status != release_status: # Something has changed. CPS must have processed this movie. - logger.postprocess("SUCCESS: This release is now marked as status [{0}]".format(current_status), section) - return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] - if not os.path.isdir(dir_name): - logger.postprocess("SUCCESS: The input directory {0} has been removed Processing must have finished.".format(dir_name), section) - return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] + n = 0 + params = {} + url = "{0}/{1}".format(url, scan_id) + while n < 6: # set up wait_for minutes to see if command completes.. time.sleep(10 * wait_for) - # The status hasn't changed. - return [2, "no change"] + command_status = command_complete(url, params, headers, section) + if command_status and command_status in ['completed', 'failed']: + break + n += 1 + if command_status: + logger.debug("The Scan command return status: {0}".format(command_status), section) + if not os.path.exists(dir_name): + logger.debug("The directory {0} has been removed. Renaming was successful.".format(dir_name), section) + return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] + elif command_status and command_status in ['completed']: + logger.debug("The Scan command has completed successfully. Renaming was successful.", section) + return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] + elif command_status and command_status in ['failed']: + logger.debug("The Scan command has failed. Renaming was not successful.", section) + # return [1, "%s: Failed to post-process %s" % (section, input_name) ] + else: + logger.debug("The Scan command did not return status completed. Passing back to {0} to attempt complete download handling.".format(section), section) + return [status, "{0}: Passing back to {1} to attempt Complete Download Handling".format(section, section)] + + else: + if section == "Lidarr": + logger.postprocess("FAILED: The download failed. Sending failed download to {0} for CDH processing".format(section), section) + return [1, "{0}: Download Failed. Sending back to {1}".format(section, section)] # Return as failed to flag this in the downloader. + else: + logger.warning("FAILED DOWNLOAD DETECTED", section) + if delete_failed and os.path.isdir(dir_name) and not os.path.dirname(dir_name) == dir_name: + logger.postprocess("Deleting failed files and folder {0}".format(dir_name), section) + remove_dir(dir_name) + return [1, "{0}: Failed to post-process. {1} does not support failed downloads".format(section, section)] # Return as failed to flag this in the downloader. + + +def command_complete(url, params, headers, section): + try: + r = requests.get(url, params=params, headers=headers, stream=True, verify=False, timeout=(30, 60)) + except requests.ConnectionError: + logger.error("Unable to open URL: {0}".format(url), section) + return None + if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: + logger.error("Server returned status {0}".format(r.status_code), section) + return None + else: + try: + return r.json()['state'] + except (ValueError, KeyError): + # ValueError catches simplejson's JSONDecodeError and json's ValueError + logger.error("{0} did not return expected json data.".format(section), section) + return None + + +def get_status(url, apikey, dir_name): + logger.debug("Attempting to get current status for release:{0}".format(os.path.basename(dir_name))) + + params = { + 'apikey': apikey, + 'cmd': "getHistory" + } + + logger.debug("Opening URL: {0} with PARAMS: {1}".format(url, params)) + + try: + r = requests.get(url, params=params, verify=False, timeout=(30, 120)) + except requests.RequestException: + logger.error("Unable to open URL") + return None + + try: + result = r.json() + except ValueError: + # ValueError catches simplejson's JSONDecodeError and json's ValueError + return None + + for album in result: + if os.path.basename(dir_name) == album['FolderName']: + return album["Status"].lower() + + +def force_process(params, url, apikey, input_name, dir_name, section, wait_for): + release_status = get_status(url, apikey, dir_name) + if not release_status: + logger.error("Could not find a status for {0}, is it in the wanted list ?".format(input_name), section) + + logger.debug("Opening URL: {0} with PARAMS: {1}".format(url, params), section) + + try: + r = requests.get(url, params=params, verify=False, timeout=(30, 300)) + except requests.ConnectionError: + logger.error("Unable to open URL {0}".format(url), section) + return [1, "{0}: Failed to post-process - Unable to connect to {1}".format(section, section)] + + logger.debug("Result: {0}".format(r.text), section) + + if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: + logger.error("Server returned status {0}".format(r.status_code), section) + return [1, "{0}: Failed to post-process - Server returned status {1}".format(section, r.status_code)] + elif r.text == "OK": + logger.postprocess("SUCCESS: Post-Processing started for {0} in folder {1} ...".format(input_name, dir_name), section) + else: + logger.error("FAILED: Post-Processing has NOT started for {0} in folder {1}. exiting!".format(input_name, dir_name), section) + return [1, "{0}: Failed to post-process - Returned log from {1} was not as expected.".format(section, section)] + + # we will now wait for this album to be processed before returning to TorrentToMedia and unpausing. + timeout = time.time() + 60 * wait_for + while time.time() < timeout: + current_status = get_status(url, apikey, dir_name) + if current_status is not None and current_status != release_status: # Something has changed. CPS must have processed this movie. + logger.postprocess("SUCCESS: This release is now marked as status [{0}]".format(current_status), section) + return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] + if not os.path.isdir(dir_name): + logger.postprocess("SUCCESS: The input directory {0} has been removed Processing must have finished.".format(dir_name), section) + return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] + time.sleep(10 * wait_for) + # The status hasn't changed. + return [2, "no change"] diff --git a/core/auto_process/tv.py b/core/auto_process/tv.py index 33cae750..a7b17edb 100644 --- a/core/auto_process/tv.py +++ b/core/auto_process/tv.py @@ -17,358 +17,359 @@ from core.utils import convert_to_ascii, flatten, import_subs, list_media_files, requests.packages.urllib3.disable_warnings() -class TV(object): - def process(self, section, dir_name, input_name=None, failed=False, client_agent="manual", download_id=None, input_category=None, failure_link=None): +def process(section, dir_name, input_name=None, failed=False, client_agent="manual", download_id=None, input_category=None, failure_link=None): - cfg = dict(core.CFG[section][input_category]) + cfg = dict(core.CFG[section][input_category]) - host = cfg["host"] - port = cfg["port"] - ssl = int(cfg.get("ssl", 0)) - web_root = cfg.get("web_root", "") - protocol = "https://" if ssl else "http://" - username = cfg.get("username", "") - password = cfg.get("password", "") - apikey = cfg.get("apikey", "") + host = cfg["host"] + port = cfg["port"] + ssl = int(cfg.get("ssl", 0)) + web_root = cfg.get("web_root", "") + protocol = "https://" if ssl else "http://" + username = cfg.get("username", "") + password = cfg.get("password", "") + apikey = cfg.get("apikey", "") - if server_responding("{0}{1}:{2}{3}".format(protocol, host, port, web_root)): - # auto-detect correct fork - fork, fork_params = auto_fork(section, input_category) - elif not username and not apikey: - logger.info('No SickBeard username or Sonarr apikey entered. Performing transcoder functions only') - fork, fork_params = "None", {} - else: - logger.error("Server did not respond. Exiting", section) - return [1, "{0}: Failed to post-process - {1} did not respond.".format(section, section)] + if server_responding("{0}{1}:{2}{3}".format(protocol, host, port, web_root)): + # auto-detect correct fork + fork, fork_params = auto_fork(section, input_category) + elif not username and not apikey: + logger.info('No SickBeard username or Sonarr apikey entered. Performing transcoder functions only') + fork, fork_params = "None", {} + else: + logger.error("Server did not respond. Exiting", section) + return [1, "{0}: Failed to post-process - {1} did not respond.".format(section, section)] - delete_failed = int(cfg.get("delete_failed", 0)) - nzb_extraction_by = cfg.get("nzbExtractionBy", "Downloader") - process_method = cfg.get("process_method") - if client_agent == core.TORRENT_CLIENTAGENT and core.USELINK == "move-sym": - process_method = "symlink" - remote_path = int(cfg.get("remote_path", 0)) - wait_for = int(cfg.get("wait_for", 2)) - force = int(cfg.get("force", 0)) - delete_on = int(cfg.get("delete_on", 0)) - ignore_subs = int(cfg.get("ignore_subs", 0)) - status = int(failed) - if status > 0 and core.NOEXTRACTFAILED: - extract = 0 - else: - extract = int(cfg.get("extract", 0)) - # get importmode, default to "Move" for consistency with legacy - import_mode = cfg.get("importMode", "Move") + delete_failed = int(cfg.get("delete_failed", 0)) + nzb_extraction_by = cfg.get("nzbExtractionBy", "Downloader") + process_method = cfg.get("process_method") + if client_agent == core.TORRENT_CLIENTAGENT and core.USELINK == "move-sym": + process_method = "symlink" + remote_path = int(cfg.get("remote_path", 0)) + wait_for = int(cfg.get("wait_for", 2)) + force = int(cfg.get("force", 0)) + delete_on = int(cfg.get("delete_on", 0)) + ignore_subs = int(cfg.get("ignore_subs", 0)) + status = int(failed) + if status > 0 and core.NOEXTRACTFAILED: + extract = 0 + else: + extract = int(cfg.get("extract", 0)) + # get importmode, default to "Move" for consistency with legacy + import_mode = cfg.get("importMode", "Move") - if not os.path.isdir(dir_name) and os.path.isfile(dir_name): # If the input directory is a file, assume single file download and split dir/name. - dir_name = os.path.split(os.path.normpath(dir_name))[0] + if not os.path.isdir(dir_name) and os.path.isfile(dir_name): # If the input directory is a file, assume single file download and split dir/name. + dir_name = os.path.split(os.path.normpath(dir_name))[0] - specific_path = os.path.join(dir_name, str(input_name)) - clean_name = os.path.splitext(specific_path) - if clean_name[1] == ".nzb": - specific_path = clean_name[0] - if os.path.isdir(specific_path): - dir_name = specific_path + specific_path = os.path.join(dir_name, str(input_name)) + clean_name = os.path.splitext(specific_path) + if clean_name[1] == ".nzb": + specific_path = clean_name[0] + if os.path.isdir(specific_path): + dir_name = specific_path - # Attempt to create the directory if it doesn't exist and ignore any - # error stating that it already exists. This fixes a bug where SickRage - # won't process the directory because it doesn't exist. - try: - os.makedirs(dir_name) # Attempt to create the directory - except OSError as e: - # Re-raise the error if it wasn't about the directory not existing - if e.errno != errno.EEXIST: - raise + # Attempt to create the directory if it doesn't exist and ignore any + # error stating that it already exists. This fixes a bug where SickRage + # won't process the directory because it doesn't exist. + try: + os.makedirs(dir_name) # Attempt to create the directory + except OSError as e: + # Re-raise the error if it wasn't about the directory not existing + if e.errno != errno.EEXIST: + raise - if 'process_method' not in fork_params or (client_agent in ['nzbget', 'sabnzbd'] and nzb_extraction_by != "Destination"): - if input_name: - process_all_exceptions(input_name, dir_name) + if 'process_method' not in fork_params or (client_agent in ['nzbget', 'sabnzbd'] and nzb_extraction_by != "Destination"): + if input_name: + process_all_exceptions(input_name, dir_name) + input_name, dir_name = convert_to_ascii(input_name, dir_name) + + # Now check if tv files exist in destination. + if not list_media_files(dir_name, media=True, audio=False, meta=False, archives=False): + if list_media_files(dir_name, media=False, audio=False, meta=False, archives=True) and extract: + logger.debug('Checking for archives to extract in directory: {0}'.format(dir_name)) + core.extract_files(dir_name) input_name, dir_name = convert_to_ascii(input_name, dir_name) - # Now check if tv files exist in destination. - if not list_media_files(dir_name, media=True, audio=False, meta=False, archives=False): - if list_media_files(dir_name, media=False, audio=False, meta=False, archives=True) and extract: - logger.debug('Checking for archives to extract in directory: {0}'.format(dir_name)) - core.extract_files(dir_name) - input_name, dir_name = convert_to_ascii(input_name, dir_name) + if list_media_files(dir_name, media=True, audio=False, meta=False, archives=False): # Check that a video exists. if not, assume failed. + flatten(dir_name) - if list_media_files(dir_name, media=True, audio=False, meta=False, archives=False): # Check that a video exists. if not, assume failed. - flatten(dir_name) - - # Check video files for corruption - good_files = 0 - num_files = 0 - for video in list_media_files(dir_name, media=True, audio=False, meta=False, archives=False): - num_files += 1 - if transcoder.is_video_good(video, status): - good_files += 1 - import_subs(video) - if num_files > 0: - if good_files == num_files and not status == 0: - logger.info('Found Valid Videos. Setting status Success') - status = 0 - failed = 0 - if good_files < num_files and status == 0: - logger.info('Found corrupt videos. Setting status Failed') - status = 1 - failed = 1 - if 'NZBOP_VERSION' in os.environ and os.environ['NZBOP_VERSION'][0:5] >= '14.0': - print('[NZB] MARK=BAD') - if failure_link: - failure_link += '&corrupt=true' - elif client_agent == "manual": - logger.warning("No media files found in directory {0} to manually process.".format(dir_name), section) - return [0, ""] # Success (as far as this script is concerned) - elif nzb_extraction_by == "Destination": - logger.info("Check for media files ignored because nzbExtractionBy is set to Destination.") - if int(failed) == 0: - logger.info("Setting Status Success.") - status = 0 - failed = 0 - else: - logger.info("Downloader reported an error during download or verification. Processing this as a failed download.") - status = 1 - failed = 1 - else: - logger.warning("No media files found in directory {0}. Processing this as a failed download".format(dir_name), section) + # Check video files for corruption + good_files = 0 + num_files = 0 + for video in list_media_files(dir_name, media=True, audio=False, meta=False, archives=False): + num_files += 1 + if transcoder.is_video_good(video, status): + good_files += 1 + import_subs(video) + if num_files > 0: + if good_files == num_files and not status == 0: + logger.info('Found Valid Videos. Setting status Success') + status = 0 + failed = 0 + if good_files < num_files and status == 0: + logger.info('Found corrupt videos. Setting status Failed') status = 1 failed = 1 if 'NZBOP_VERSION' in os.environ and os.environ['NZBOP_VERSION'][0:5] >= '14.0': print('[NZB] MARK=BAD') - - if status == 0 and core.TRANSCODE == 1: # only transcode successful downloads - result, new_dir_name = transcoder.transcode_directory(dir_name) - if result == 0: - logger.debug("SUCCESS: Transcoding succeeded for files in {0}".format(dir_name), section) - dir_name = new_dir_name - - chmod_directory = int(str(cfg.get("chmodDirectory", "0")), 8) - logger.debug("Config setting 'chmodDirectory' currently set to {0}".format(oct(chmod_directory)), section) - if chmod_directory: - logger.info("Attempting to set the octal permission of '{0}' on directory '{1}'".format(oct(chmod_directory), dir_name), section) - core.rchmod(dir_name, chmod_directory) - else: - logger.error("FAILED: Transcoding failed for files in {0}".format(dir_name), section) - return [1, "{0}: Failed to post-process - Transcoding failed".format(section)] - - # configure SB params to pass - fork_params['quiet'] = 1 - fork_params['proc_type'] = 'manual' - if input_name is not None: - fork_params['nzbName'] = input_name - - for param in copy.copy(fork_params): - if param == "failed": - fork_params[param] = failed - del fork_params['proc_type'] - if "type" in fork_params: - del fork_params['type'] - - if param == "return_data": - fork_params[param] = 0 - del fork_params['quiet'] - - if param == "type": - fork_params[param] = 'manual' - if "proc_type" in fork_params: - del fork_params['proc_type'] - - if param in ["dir_name", "dir", "proc_dir", "process_directory", "path"]: - fork_params[param] = dir_name - if remote_path: - fork_params[param] = remote_dir(dir_name) - - if param == "process_method": - if process_method: - fork_params[param] = process_method - else: - del fork_params[param] - - if param in ["force", "force_replace"]: - if force: - fork_params[param] = force - else: - del fork_params[param] - - if param in ["delete_on", "delete"]: - if delete_on: - fork_params[param] = delete_on - else: - del fork_params[param] - - if param == "ignore_subs": - if ignore_subs: - fork_params[param] = ignore_subs - else: - del fork_params[param] - - if param == "force_next": - fork_params[param] = 1 - - # delete any unused params so we don't pass them to SB by mistake - [fork_params.pop(k) for k, v in fork_params.items() if v is None] - - if status == 0: - if section == "NzbDrone" and not apikey: - logger.info('No Sonarr apikey entered. Processing completed.') - return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] - logger.postprocess("SUCCESS: The download succeeded, sending a post-process request", section) - else: - core.FAILED = True if failure_link: - report_nzb(failure_link, client_agent) - if 'failed' in fork_params: - logger.postprocess("FAILED: The download failed. Sending 'failed' process request to {0} branch".format(fork), section) - elif section == "NzbDrone": - logger.postprocess("FAILED: The download failed. Sending failed download to {0} for CDH processing".format(fork), section) - return [1, "{0}: Download Failed. Sending back to {1}".format(section, section)] # Return as failed to flag this in the downloader. - else: - logger.postprocess("FAILED: The download failed. {0} branch does not handle failed downloads. Nothing to process".format(fork), section) - if delete_failed and os.path.isdir(dir_name) and not os.path.dirname(dir_name) == dir_name: - logger.postprocess("Deleting failed files and folder {0}".format(dir_name), section) - remove_dir(dir_name) - return [1, "{0}: Failed to post-process. {1} does not support failed downloads".format(section, section)] # Return as failed to flag this in the downloader. + failure_link += '&corrupt=true' + elif client_agent == "manual": + logger.warning("No media files found in directory {0} to manually process.".format(dir_name), section) + return [0, ""] # Success (as far as this script is concerned) + elif nzb_extraction_by == "Destination": + logger.info("Check for media files ignored because nzbExtractionBy is set to Destination.") + if int(failed) == 0: + logger.info("Setting Status Success.") + status = 0 + failed = 0 + else: + logger.info("Downloader reported an error during download or verification. Processing this as a failed download.") + status = 1 + failed = 1 + else: + logger.warning("No media files found in directory {0}. Processing this as a failed download".format(dir_name), section) + status = 1 + failed = 1 + if 'NZBOP_VERSION' in os.environ and os.environ['NZBOP_VERSION'][0:5] >= '14.0': + print('[NZB] MARK=BAD') - url = None - if section == "SickBeard": - if apikey: - url = "{0}{1}:{2}{3}/api/{4}/?cmd=postprocess".format(protocol, host, port, web_root, apikey) - else: - url = "{0}{1}:{2}{3}/home/postprocess/processEpisode".format(protocol, host, port, web_root) - elif section == "NzbDrone": - url = "{0}{1}:{2}{3}/api/command".format(protocol, host, port, web_root) - url2 = "{0}{1}:{2}{3}/api/config/downloadClient".format(protocol, host, port, web_root) - headers = {"X-Api-Key": apikey} - # params = {'sortKey': 'series.title', 'page': 1, 'pageSize': 1, 'sortDir': 'asc'} + if status == 0 and core.TRANSCODE == 1: # only transcode successful downloads + result, new_dir_name = transcoder.transcode_directory(dir_name) + if result == 0: + logger.debug("SUCCESS: Transcoding succeeded for files in {0}".format(dir_name), section) + dir_name = new_dir_name + + chmod_directory = int(str(cfg.get("chmodDirectory", "0")), 8) + logger.debug("Config setting 'chmodDirectory' currently set to {0}".format(oct(chmod_directory)), section) + if chmod_directory: + logger.info("Attempting to set the octal permission of '{0}' on directory '{1}'".format(oct(chmod_directory), dir_name), section) + core.rchmod(dir_name, chmod_directory) + else: + logger.error("FAILED: Transcoding failed for files in {0}".format(dir_name), section) + return [1, "{0}: Failed to post-process - Transcoding failed".format(section)] + + # configure SB params to pass + fork_params['quiet'] = 1 + fork_params['proc_type'] = 'manual' + if input_name is not None: + fork_params['nzbName'] = input_name + + for param in copy.copy(fork_params): + if param == "failed": + fork_params[param] = failed + del fork_params['proc_type'] + if "type" in fork_params: + del fork_params['type'] + + if param == "return_data": + fork_params[param] = 0 + del fork_params['quiet'] + + if param == "type": + fork_params[param] = 'manual' + if "proc_type" in fork_params: + del fork_params['proc_type'] + + if param in ["dir_name", "dir", "proc_dir", "process_directory", "path"]: + fork_params[param] = dir_name if remote_path: - logger.debug("remote_path: {0}".format(remote_dir(dir_name)), section) - data = {"name": "DownloadedEpisodesScan", "path": remote_dir(dir_name), "downloadClientId": download_id, "importMode": import_mode} + fork_params[param] = remote_dir(dir_name) + + if param == "process_method": + if process_method: + fork_params[param] = process_method else: - logger.debug("path: {0}".format(dir_name), section) - data = {"name": "DownloadedEpisodesScan", "path": dir_name, "downloadClientId": download_id, "importMode": import_mode} - if not download_id: - data.pop("downloadClientId") - data = json.dumps(data) + del fork_params[param] - try: - if section == "SickBeard": - logger.debug("Opening URL: {0} with params: {1}".format(url, fork_params), section) - s = requests.Session() - if not apikey and username and password: - login = "{0}{1}:{2}{3}/login".format(protocol, host, port, web_root) - login_params = {'username': username, 'password': password} - r = s.get(login, verify=False, timeout=(30, 60)) - if r.status_code == 401 and r.cookies.get('_xsrf'): - login_params['_xsrf'] = r.cookies.get('_xsrf') - s.post(login, data=login_params, stream=True, verify=False, timeout=(30, 60)) - r = s.get(url, auth=(username, password), params=fork_params, stream=True, verify=False, timeout=(30, 1800)) - elif section == "NzbDrone": - logger.debug("Opening URL: {0} with data: {1}".format(url, data), section) - r = requests.post(url, data=data, headers=headers, stream=True, verify=False, timeout=(30, 1800)) - except requests.ConnectionError: - logger.error("Unable to open URL: {0}".format(url), section) - return [1, "{0}: Failed to post-process - Unable to connect to {1}".format(section, section)] - - if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: - logger.error("Server returned status {0}".format(r.status_code), section) - return [1, "{0}: Failed to post-process - Server returned status {1}".format(section, r.status_code)] - - success = False - queued = False - started = False - if section == "SickBeard": - if apikey: - if r.json()['result'] == 'success': - success = True + if param in ["force", "force_replace"]: + if force: + fork_params[param] = force else: - for line in r.iter_lines(): - if line: - line = line.decode('utf-8') - logger.postprocess("{0}".format(line), section) - if "Moving file from" in line: - input_name = os.path.split(line)[1] - if "added to the queue" in line: - queued = True - if "Processing succeeded" in line or "Successfully processed" in line: - success = True + del fork_params[param] - if queued: - time.sleep(60) - elif section == "NzbDrone": - try: - res = json.loads(r.content) - scan_id = int(res['id']) - logger.debug("Scan started with id: {0}".format(scan_id), section) - started = True - except Exception as e: - logger.warning("No scan id was returned due to: {0}".format(e), section) - scan_id = None - started = False + if param in ["delete_on", "delete"]: + if delete_on: + fork_params[param] = delete_on + else: + del fork_params[param] - if status != 0 and delete_failed and not os.path.dirname(dir_name) == dir_name: - logger.postprocess("Deleting failed files and folder {0}".format(dir_name), section) - remove_dir(dir_name) + if param == "ignore_subs": + if ignore_subs: + fork_params[param] = ignore_subs + else: + del fork_params[param] - if success: + if param == "force_next": + fork_params[param] = 1 + + # delete any unused params so we don't pass them to SB by mistake + [fork_params.pop(k) for k, v in fork_params.items() if v is None] + + if status == 0: + if section == "NzbDrone" and not apikey: + logger.info('No Sonarr apikey entered. Processing completed.') return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] - elif section == "NzbDrone" and started: - n = 0 - params = {} - url = "{0}/{1}".format(url, scan_id) - while n < 6: # set up wait_for minutes to see if command completes.. - time.sleep(10 * wait_for) - command_status = self.command_complete(url, params, headers, section) - if command_status and command_status in ['completed', 'failed']: - break - n += 1 - if command_status: - logger.debug("The Scan command return status: {0}".format(command_status), section) - if not os.path.exists(dir_name): - logger.debug("The directory {0} has been removed. Renaming was successful.".format(dir_name), section) - return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] - elif command_status and command_status in ['completed']: - logger.debug("The Scan command has completed successfully. Renaming was successful.", section) - return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] - elif command_status and command_status in ['failed']: - logger.debug("The Scan command has failed. Renaming was not successful.", section) - # return [1, "%s: Failed to post-process %s" % (section, input_name) ] - if self.completed_download_handling(url2, headers, section=section): - logger.debug("The Scan command did not return status completed, but complete Download Handling is enabled. Passing back to {0}.".format(section), section) - return [status, "{0}: Complete DownLoad Handling is enabled. Passing back to {1}".format(section, section)] - else: - logger.warning("The Scan command did not return a valid status. Renaming was not successful.", section) - return [1, "{0}: Failed to post-process {1}".format(section, input_name)] + logger.postprocess("SUCCESS: The download succeeded, sending a post-process request", section) + else: + core.FAILED = True + if failure_link: + report_nzb(failure_link, client_agent) + if 'failed' in fork_params: + logger.postprocess("FAILED: The download failed. Sending 'failed' process request to {0} branch".format(fork), section) + elif section == "NzbDrone": + logger.postprocess("FAILED: The download failed. Sending failed download to {0} for CDH processing".format(fork), section) + return [1, "{0}: Download Failed. Sending back to {1}".format(section, section)] # Return as failed to flag this in the downloader. else: - return [1, "{0}: Failed to post-process - Returned log from {1} was not as expected.".format(section, section)] # We did not receive Success confirmation. + logger.postprocess("FAILED: The download failed. {0} branch does not handle failed downloads. Nothing to process".format(fork), section) + if delete_failed and os.path.isdir(dir_name) and not os.path.dirname(dir_name) == dir_name: + logger.postprocess("Deleting failed files and folder {0}".format(dir_name), section) + remove_dir(dir_name) + return [1, "{0}: Failed to post-process. {1} does not support failed downloads".format(section, section)] # Return as failed to flag this in the downloader. - def command_complete(self, url, params, headers, section): - try: - r = requests.get(url, params=params, headers=headers, stream=True, verify=False, timeout=(30, 60)) - except requests.ConnectionError: - logger.error("Unable to open URL: {0}".format(url), section) - return None - if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: - logger.error("Server returned status {0}".format(r.status_code), section) - return None + url = None + if section == "SickBeard": + if apikey: + url = "{0}{1}:{2}{3}/api/{4}/?cmd=postprocess".format(protocol, host, port, web_root, apikey) else: - try: - return r.json()['state'] - except (ValueError, KeyError): - # ValueError catches simplejson's JSONDecodeError and json's ValueError - logger.error("{0} did not return expected json data.".format(section), section) - return None + url = "{0}{1}:{2}{3}/home/postprocess/processEpisode".format(protocol, host, port, web_root) + elif section == "NzbDrone": + url = "{0}{1}:{2}{3}/api/command".format(protocol, host, port, web_root) + url2 = "{0}{1}:{2}{3}/api/config/downloadClient".format(protocol, host, port, web_root) + headers = {"X-Api-Key": apikey} + # params = {'sortKey': 'series.title', 'page': 1, 'pageSize': 1, 'sortDir': 'asc'} + if remote_path: + logger.debug("remote_path: {0}".format(remote_dir(dir_name)), section) + data = {"name": "DownloadedEpisodesScan", "path": remote_dir(dir_name), "downloadClientId": download_id, "importMode": import_mode} + else: + logger.debug("path: {0}".format(dir_name), section) + data = {"name": "DownloadedEpisodesScan", "path": dir_name, "downloadClientId": download_id, "importMode": import_mode} + if not download_id: + data.pop("downloadClientId") + data = json.dumps(data) - def completed_download_handling(self, url2, headers, section="MAIN"): - try: - r = requests.get(url2, params={}, headers=headers, stream=True, verify=False, timeout=(30, 60)) - except requests.ConnectionError: - logger.error("Unable to open URL: {0}".format(url2), section) - return False - if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: - logger.error("Server returned status {0}".format(r.status_code), section) - return False + try: + if section == "SickBeard": + logger.debug("Opening URL: {0} with params: {1}".format(url, fork_params), section) + s = requests.Session() + if not apikey and username and password: + login = "{0}{1}:{2}{3}/login".format(protocol, host, port, web_root) + login_params = {'username': username, 'password': password} + r = s.get(login, verify=False, timeout=(30, 60)) + if r.status_code == 401 and r.cookies.get('_xsrf'): + login_params['_xsrf'] = r.cookies.get('_xsrf') + s.post(login, data=login_params, stream=True, verify=False, timeout=(30, 60)) + r = s.get(url, auth=(username, password), params=fork_params, stream=True, verify=False, timeout=(30, 1800)) + elif section == "NzbDrone": + logger.debug("Opening URL: {0} with data: {1}".format(url, data), section) + r = requests.post(url, data=data, headers=headers, stream=True, verify=False, timeout=(30, 1800)) + except requests.ConnectionError: + logger.error("Unable to open URL: {0}".format(url), section) + return [1, "{0}: Failed to post-process - Unable to connect to {1}".format(section, section)] + + if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: + logger.error("Server returned status {0}".format(r.status_code), section) + return [1, "{0}: Failed to post-process - Server returned status {1}".format(section, r.status_code)] + + success = False + queued = False + started = False + if section == "SickBeard": + if apikey: + if r.json()['result'] == 'success': + success = True else: - try: - return r.json().get("enableCompletedDownloadHandling", False) - except ValueError: - # ValueError catches simplejson's JSONDecodeError and json's ValueError - return False + for line in r.iter_lines(): + if line: + line = line.decode('utf-8') + logger.postprocess("{0}".format(line), section) + if "Moving file from" in line: + input_name = os.path.split(line)[1] + if "added to the queue" in line: + queued = True + if "Processing succeeded" in line or "Successfully processed" in line: + success = True + + if queued: + time.sleep(60) + elif section == "NzbDrone": + try: + res = json.loads(r.content) + scan_id = int(res['id']) + logger.debug("Scan started with id: {0}".format(scan_id), section) + started = True + except Exception as e: + logger.warning("No scan id was returned due to: {0}".format(e), section) + scan_id = None + started = False + + if status != 0 and delete_failed and not os.path.dirname(dir_name) == dir_name: + logger.postprocess("Deleting failed files and folder {0}".format(dir_name), section) + remove_dir(dir_name) + + if success: + return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] + elif section == "NzbDrone" and started: + n = 0 + params = {} + url = "{0}/{1}".format(url, scan_id) + while n < 6: # set up wait_for minutes to see if command completes.. + time.sleep(10 * wait_for) + command_status = command_complete(url, params, headers, section) + if command_status and command_status in ['completed', 'failed']: + break + n += 1 + if command_status: + logger.debug("The Scan command return status: {0}".format(command_status), section) + if not os.path.exists(dir_name): + logger.debug("The directory {0} has been removed. Renaming was successful.".format(dir_name), section) + return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] + elif command_status and command_status in ['completed']: + logger.debug("The Scan command has completed successfully. Renaming was successful.", section) + return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] + elif command_status and command_status in ['failed']: + logger.debug("The Scan command has failed. Renaming was not successful.", section) + # return [1, "%s: Failed to post-process %s" % (section, input_name) ] + if completed_download_handling(url2, headers, section=section): + logger.debug("The Scan command did not return status completed, but complete Download Handling is enabled. Passing back to {0}.".format(section), section) + return [status, "{0}: Complete DownLoad Handling is enabled. Passing back to {1}".format(section, section)] + else: + logger.warning("The Scan command did not return a valid status. Renaming was not successful.", section) + return [1, "{0}: Failed to post-process {1}".format(section, input_name)] + else: + return [1, "{0}: Failed to post-process - Returned log from {1} was not as expected.".format(section, section)] # We did not receive Success confirmation. + + +def command_complete(url, params, headers, section): + try: + r = requests.get(url, params=params, headers=headers, stream=True, verify=False, timeout=(30, 60)) + except requests.ConnectionError: + logger.error("Unable to open URL: {0}".format(url), section) + return None + if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: + logger.error("Server returned status {0}".format(r.status_code), section) + return None + else: + try: + return r.json()['state'] + except (ValueError, KeyError): + # ValueError catches simplejson's JSONDecodeError and json's ValueError + logger.error("{0} did not return expected json data.".format(section), section) + return None + + +def completed_download_handling(url2, headers, section="MAIN"): + try: + r = requests.get(url2, params={}, headers=headers, stream=True, verify=False, timeout=(30, 60)) + except requests.ConnectionError: + logger.error("Unable to open URL: {0}".format(url2), section) + return False + if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: + logger.error("Server returned status {0}".format(r.status_code), section) + return False + else: + try: + return r.json().get("enableCompletedDownloadHandling", False) + except ValueError: + # ValueError catches simplejson's JSONDecodeError and json's ValueError + return False diff --git a/nzbToMedia.py b/nzbToMedia.py index cfda2f0b..ebcc9e5f 100755 --- a/nzbToMedia.py +++ b/nzbToMedia.py @@ -12,7 +12,7 @@ import sys import core from core import logger, main_db -from core.auto_process import Comic, Game, Movie, Music, TV +from core.auto_process import comics, games, movies, music, tv from core.user_scripts import external_script from core.utils import char_replace, clean_dir, convert_to_ascii, extract_files, get_dirs, get_download_info, get_nzoid, plex_update, update_download_info_status @@ -109,18 +109,18 @@ def process(input_directory, input_name=None, status=0, client_agent='manual', d logger.info("Calling {0}:{1} to post-process:{2}".format(section_name, input_category, input_name)) if section_name in ["CouchPotato", "Radarr"]: - result = Movie().process(section_name, input_directory, input_name, status, client_agent, download_id, + result = movies.process(section_name, input_directory, input_name, status, client_agent, download_id, input_category, failure_link) elif section_name in ["SickBeard", "NzbDrone", "Sonarr"]: - result = TV().process(section_name, input_directory, input_name, status, client_agent, + result = tv.process(section_name, input_directory, input_name, status, client_agent, download_id, input_category, failure_link) elif section_name in ["HeadPhones", "Lidarr"]: - result = Music().process(section_name, input_directory, input_name, status, client_agent, input_category) + result = music.process(section_name, input_directory, input_name, status, client_agent, input_category) elif section_name == "Mylar": - result = Comic().process(section_name, input_directory, input_name, status, client_agent, + result = comics.process(section_name, input_directory, input_name, status, client_agent, input_category) elif section_name == "Gamez": - result = Game().process(section_name, input_directory, input_name, status, client_agent, input_category) + result = games.process(section_name, input_directory, input_name, status, client_agent, input_category) elif section_name == 'UserScript': result = external_script(input_directory, input_name, input_category, section[usercat]) else: From 1d46f716e11d4f12f1775b9b26bb7c19f9fc9f48 Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Thu, 27 Dec 2018 08:27:47 -0500 Subject: [PATCH 2/3] Refactor common functions from auto_process to auto_process.common --- core/auto_process/common.py | 38 +++++++++++++++++++++++++++++++++++++ core/auto_process/movies.py | 36 +---------------------------------- core/auto_process/music.py | 19 +------------------ core/auto_process/tv.py | 36 +---------------------------------- 4 files changed, 41 insertions(+), 88 deletions(-) create mode 100644 core/auto_process/common.py diff --git a/core/auto_process/common.py b/core/auto_process/common.py new file mode 100644 index 00000000..4d4e6ae8 --- /dev/null +++ b/core/auto_process/common.py @@ -0,0 +1,38 @@ +import requests + +from core import logger + + +def command_complete(url, params, headers, section): + try: + r = requests.get(url, params=params, headers=headers, stream=True, verify=False, timeout=(30, 60)) + except requests.ConnectionError: + logger.error("Unable to open URL: {0}".format(url), section) + return None + if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: + logger.error("Server returned status {0}".format(r.status_code), section) + return None + else: + try: + return r.json()['state'] + except (ValueError, KeyError): + # ValueError catches simplejson's JSONDecodeError and json's ValueError + logger.error("{0} did not return expected json data.".format(section), section) + return None + + +def completed_download_handling(url2, headers, section="MAIN"): + try: + r = requests.get(url2, params={}, headers=headers, stream=True, verify=False, timeout=(30, 60)) + except requests.ConnectionError: + logger.error("Unable to open URL: {0}".format(url2), section) + return False + if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: + logger.error("Server returned status {0}".format(r.status_code), section) + return False + else: + try: + return r.json().get("enableCompletedDownloadHandling", False) + except ValueError: + # ValueError catches simplejson's JSONDecodeError and json's ValueError + return False diff --git a/core/auto_process/movies.py b/core/auto_process/movies.py index a82529fd..6a5cf304 100644 --- a/core/auto_process/movies.py +++ b/core/auto_process/movies.py @@ -8,6 +8,7 @@ import requests import core from core import logger, transcoder +from core.auto_process.common import command_complete, completed_download_handling from core.scene_exceptions import process_all_exceptions from core.utils import convert_to_ascii, find_download, find_imdbid, import_subs, list_media_files, remote_dir, remove_dir, report_nzb, server_responding @@ -336,41 +337,6 @@ def process(section, dir_name, input_name=None, status=0, client_agent="manual", return [1, "{0}: Failed to post-process - No change in status".format(section)] -def command_complete(url, params, headers, section): - try: - r = requests.get(url, params=params, headers=headers, stream=True, verify=False, timeout=(30, 60)) - except requests.ConnectionError: - logger.error("Unable to open URL: {0}".format(url), section) - return None - if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: - logger.error("Server returned status {0}".format(r.status_code), section) - return None - else: - try: - return r.json()['state'] - except (ValueError, KeyError): - # ValueError catches simplejson's JSONDecodeError and json's ValueError - logger.error("{0} did not return expected json data.".format(section), section) - return None - - -def completed_download_handling(url2, headers, section="MAIN"): - try: - r = requests.get(url2, params={}, headers=headers, stream=True, verify=False, timeout=(30, 60)) - except requests.ConnectionError: - logger.error("Unable to open URL: {0}".format(url2), section) - return False - if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: - logger.error("Server returned status {0}".format(r.status_code), section) - return False - else: - try: - return r.json().get("enableCompletedDownloadHandling", False) - except ValueError: - # ValueError catches simplejson's JSONDecodeError and json's ValueError - return False - - def get_release(base_url, imdb_id=None, download_id=None, release_id=None): results = {} params = {} diff --git a/core/auto_process/music.py b/core/auto_process/music.py index 88521774..01300fb9 100644 --- a/core/auto_process/music.py +++ b/core/auto_process/music.py @@ -8,6 +8,7 @@ import requests import core from core import logger +from core.auto_process.common import command_complete from core.scene_exceptions import process_all_exceptions from core.utils import convert_to_ascii, list_media_files, remote_dir, remove_dir, server_responding @@ -157,24 +158,6 @@ def process(section, dir_name, input_name=None, status=0, client_agent="manual", return [1, "{0}: Failed to post-process. {1} does not support failed downloads".format(section, section)] # Return as failed to flag this in the downloader. -def command_complete(url, params, headers, section): - try: - r = requests.get(url, params=params, headers=headers, stream=True, verify=False, timeout=(30, 60)) - except requests.ConnectionError: - logger.error("Unable to open URL: {0}".format(url), section) - return None - if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: - logger.error("Server returned status {0}".format(r.status_code), section) - return None - else: - try: - return r.json()['state'] - except (ValueError, KeyError): - # ValueError catches simplejson's JSONDecodeError and json's ValueError - logger.error("{0} did not return expected json data.".format(section), section) - return None - - def get_status(url, apikey, dir_name): logger.debug("Attempting to get current status for release:{0}".format(os.path.basename(dir_name))) diff --git a/core/auto_process/tv.py b/core/auto_process/tv.py index a7b17edb..6e266639 100644 --- a/core/auto_process/tv.py +++ b/core/auto_process/tv.py @@ -10,6 +10,7 @@ import requests import core from core import logger, transcoder +from core.auto_process.common import command_complete, completed_download_handling from core.forks import auto_fork from core.scene_exceptions import process_all_exceptions from core.utils import convert_to_ascii, flatten, import_subs, list_media_files, remote_dir, remove_dir, report_nzb, server_responding @@ -338,38 +339,3 @@ def process(section, dir_name, input_name=None, failed=False, client_agent="manu return [1, "{0}: Failed to post-process {1}".format(section, input_name)] else: return [1, "{0}: Failed to post-process - Returned log from {1} was not as expected.".format(section, section)] # We did not receive Success confirmation. - - -def command_complete(url, params, headers, section): - try: - r = requests.get(url, params=params, headers=headers, stream=True, verify=False, timeout=(30, 60)) - except requests.ConnectionError: - logger.error("Unable to open URL: {0}".format(url), section) - return None - if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: - logger.error("Server returned status {0}".format(r.status_code), section) - return None - else: - try: - return r.json()['state'] - except (ValueError, KeyError): - # ValueError catches simplejson's JSONDecodeError and json's ValueError - logger.error("{0} did not return expected json data.".format(section), section) - return None - - -def completed_download_handling(url2, headers, section="MAIN"): - try: - r = requests.get(url2, params={}, headers=headers, stream=True, verify=False, timeout=(30, 60)) - except requests.ConnectionError: - logger.error("Unable to open URL: {0}".format(url2), section) - return False - if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: - logger.error("Server returned status {0}".format(r.status_code), section) - return False - else: - try: - return r.json().get("enableCompletedDownloadHandling", False) - except ValueError: - # ValueError catches simplejson's JSONDecodeError and json's ValueError - return False From 8c4353bc903b2e783ca4061b6cec8e63294c4f8a Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Thu, 27 Dec 2018 09:08:28 -0500 Subject: [PATCH 3/3] Add ProcessResult to auto_process.common --- TorrentToMedia.py | 33 +++++++++++++++++---------------- core/auto_process/comics.py | 26 +++++++++++++++++++++----- core/auto_process/common.py | 24 ++++++++++++++++++++++++ core/auto_process/games.py | 36 +++++++++++++++++++++++++++++------- nzbToMedia.py | 36 ++++++++++++++++++++---------------- 5 files changed, 111 insertions(+), 44 deletions(-) diff --git a/TorrentToMedia.py b/TorrentToMedia.py index 0a6ba62a..11160988 100755 --- a/TorrentToMedia.py +++ b/TorrentToMedia.py @@ -11,6 +11,7 @@ import sys import core from core import logger, main_db from core.auto_process import comics, games, movies, music, tv +from core.auto_process.common import ProcessResult from core.user_scripts import external_script from core.utils import char_replace, convert_to_ascii, plex_update, replace_links from six import text_type @@ -233,31 +234,28 @@ def process_torrent(input_directory, input_name, input_category, input_hash, inp if core.TORRENT_CHMOD_DIRECTORY: core.rchmod(output_destination, core.TORRENT_CHMOD_DIRECTORY) - result = [0, ""] + result = ProcessResult( + message="", + status_code=0, + ) if section_name == 'UserScript': result = external_script(output_destination, input_name, input_category, section) - elif section_name in ['CouchPotato', 'Radarr']: - result = movies.process(section_name, output_destination, input_name, - status, client_agent, input_hash, input_category) + result = movies.process(section_name, output_destination, input_name, status, client_agent, input_hash, input_category) elif section_name in ['SickBeard', 'NzbDrone', 'Sonarr']: if input_hash: input_hash = input_hash.upper() - result = tv.process(section_name, output_destination, input_name, - status, client_agent, input_hash, input_category) + result = tv.process(section_name, output_destination, input_name, status, client_agent, input_hash, input_category) elif section_name in ['HeadPhones', 'Lidarr']: - result = music.process(section_name, output_destination, input_name, - status, client_agent, input_category) + result = music.process(section_name, output_destination, input_name, status, client_agent, input_category) elif section_name == 'Mylar': - result = comics.process(section_name, output_destination, input_name, - status, client_agent, input_category) + result = comics.process(section_name, output_destination, input_name, status, client_agent, input_category) elif section_name == 'Gamez': - result = games.process(section_name, output_destination, input_name, - status, client_agent, input_category) + result = games.process(section_name, output_destination, input_name, status, client_agent, input_category) plex_update(input_category) - if result[0] != 0: + if result.status_code != 0: if not core.TORRENT_RESUME_ON_FAILURE: logger.error("A problem was reported in the autoProcess* script. " "Torrent won't resume seeding (settings)") @@ -303,7 +301,10 @@ def main(args): logger.debug("Options passed into TorrentToMedia: {0}".format(args)) # Post-Processing Result - result = [0, ""] + result = ProcessResult( + message="", + status_code=0, + ) try: input_directory, input_name, input_category, input_hash, input_id = core.parse_args(client_agent, args) @@ -362,12 +363,12 @@ def main(args): (section, subsection)) result = results - if result[0] == 0: + if result.status_code == 0: logger.info("The {0} script completed successfully.".format(args[0])) else: logger.error("A problem was reported in the {0} script.".format(args[0])) del core.MYAPP - return result[0] + return result.status_code if __name__ == "__main__": diff --git a/core/auto_process/comics.py b/core/auto_process/comics.py index 3eb8a247..0dfda8d0 100644 --- a/core/auto_process/comics.py +++ b/core/auto_process/comics.py @@ -6,6 +6,7 @@ import requests import core from core import logger +from core.auto_process.common import ProcessResult from core.utils import convert_to_ascii, remote_dir, server_responding requests.packages.urllib3.disable_warnings() @@ -28,7 +29,10 @@ def process(section, dir_name, input_name=None, status=0, client_agent='manual', url = "{0}{1}:{2}{3}/api".format(protocol, host, port, web_root) if not server_responding(url): logger.error("Server did not respond. Exiting", section) - return [1, "{0}: Failed to post-process - {1} did not respond.".format(section, section)] + return ProcessResult( + message="{0}: Failed to post-process - {0} did not respond.".format(section), + status_code=1, + ) input_name, dir_name = convert_to_ascii(input_name, dir_name) clean_name, ext = os.path.splitext(input_name) @@ -54,10 +58,16 @@ def process(section, dir_name, input_name=None, status=0, client_agent='manual', r = requests.post(url, params=params, stream=True, verify=False, timeout=(30, 300)) except requests.ConnectionError: logger.error("Unable to open URL", section) - return [1, "{0}: Failed to post-process - Unable to connect to {1}".format(section, section)] + return ProcessResult( + message="{0}: Failed to post-process - Unable to connect to {0}".format(section), + status_code=1 + ) if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: logger.error("Server returned status {0}".format(r.status_code), section) - return [1, "{0}: Failed to post-process - Server returned status {1}".format(section, r.status_code)] + return ProcessResult( + message="{0}: Failed to post-process - Server returned status {1}".format(section, r.status_code), + status_code=1, + ) result = r.content if not type(result) == list: @@ -70,7 +80,13 @@ def process(section, dir_name, input_name=None, status=0, client_agent='manual', if success: logger.postprocess("SUCCESS: This issue has been processed successfully", section) - return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] + return ProcessResult( + message="{0}: Successfully post-processed {1}".format(section, input_name), + status_code=0, + ) else: logger.warning("The issue does not appear to have successfully processed. Please check your Logs", section) - return [1, "{0}: Failed to post-process - Returned log from {1} was not as expected.".format(section, section)] + return ProcessResult( + message="{0}: Failed to post-process - Returned log from {0} was not as expected.".format(section), + status_code=1, + ) diff --git a/core/auto_process/common.py b/core/auto_process/common.py index 4d4e6ae8..53e231e0 100644 --- a/core/auto_process/common.py +++ b/core/auto_process/common.py @@ -3,6 +3,30 @@ import requests from core import logger +class ProcessResult(object): + def __init__(self, message, status_code): + self.message = message + self.status_code = status_code + + def __iter__(self): + return self.status_code, self.message + + def __bool__(self): + return not bool(self.status_code) + + def __str__(self): + return 'Processing {0}: {1}'.format( + 'succeeded' if bool(self) else 'failed', + self.message + ) + + def __repr__(self): + return ''.format( + self.status_code, + self.message, + ) + + def command_complete(url, params, headers, section): try: r = requests.get(url, params=params, headers=headers, stream=True, verify=False, timeout=(30, 60)) diff --git a/core/auto_process/games.py b/core/auto_process/games.py index 38d88782..f0cd63a3 100644 --- a/core/auto_process/games.py +++ b/core/auto_process/games.py @@ -7,6 +7,7 @@ import requests import core from core import logger +from core.auto_process.common import ProcessResult from core.utils import convert_to_ascii, server_responding requests.packages.urllib3.disable_warnings() @@ -28,7 +29,10 @@ def process(section, dir_name, input_name=None, status=0, client_agent='manual', url = "{0}{1}:{2}{3}/api".format(protocol, host, port, web_root) if not server_responding(url): logger.error("Server did not respond. Exiting", section) - return [1, "{0}: Failed to post-process - {1} did not respond.".format(section, section)] + return ProcessResult( + message="{0}: Failed to post-process - {0} did not respond.".format(section), + status_code=1, + ) input_name, dir_name = convert_to_ascii(input_name, dir_name) @@ -51,7 +55,10 @@ def process(section, dir_name, input_name=None, status=0, client_agent='manual', r = requests.get(url, params=params, verify=False, timeout=(30, 300)) except requests.ConnectionError: logger.error("Unable to open URL") - return [1, "{0}: Failed to post-process - Unable to connect to {1}".format(section, section)] + return ProcessResult( + message="{0}: Failed to post-process - Unable to connect to {1}".format(section, section), + status_code=1, + ) result = r.json() logger.postprocess("{0}".format(result), section) @@ -61,17 +68,32 @@ def process(section, dir_name, input_name=None, status=0, client_agent='manual', shutil.move(dir_name, os.path.join(library, input_name)) except Exception: logger.error("Unable to move {0} to {1}".format(dir_name, os.path.join(library, input_name)), section) - return [1, "{0}: Failed to post-process - Unable to move files".format(section)] + return ProcessResult( + message="{0}: Failed to post-process - Unable to move files".format(section), + status_code=1, + ) else: logger.error("No library specified to move files to. Please edit your configuration.", section) - return [1, "{0}: Failed to post-process - No library defined in {1}".format(section, section)] + return ProcessResult( + message="{0}: Failed to post-process - No library defined in {0}".format(section), + status_code=1, + ) if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: logger.error("Server returned status {0}".format(r.status_code), section) - return [1, "{0}: Failed to post-process - Server returned status {1}".format(section, r.status_code)] + return ProcessResult( + message="{0}: Failed to post-process - Server returned status {1}".format(section, r.status_code), + status_code=1, + ) elif result['success']: logger.postprocess("SUCCESS: Status for {0} has been set to {1} in Gamez".format(gamez_id, download_status), section) - return [0, "{0}: Successfully post-processed {1}".format(section, input_name)] + return ProcessResult( + message="{0}: Successfully post-processed {1}".format(section, input_name), + status_code=0, + ) else: logger.error("FAILED: Status for {0} has NOT been updated in Gamez".format(gamez_id), section) - return [1, "{0}: Failed to post-process - Returned log from {1} was not as expected.".format(section, section)] + return ProcessResult( + message="{0}: Failed to post-process - Returned log from {0} was not as expected.".format(section), + status_code=1, + ) diff --git a/nzbToMedia.py b/nzbToMedia.py index ebcc9e5f..5e904c91 100755 --- a/nzbToMedia.py +++ b/nzbToMedia.py @@ -13,6 +13,7 @@ import sys import core from core import logger, main_db from core.auto_process import comics, games, movies, music, tv +from core.auto_process.common import ProcessResult from core.user_scripts import external_script from core.utils import char_replace, clean_dir, convert_to_ascii, extract_files, get_dirs, get_download_info, get_nzoid, plex_update, update_download_info_status @@ -109,26 +110,26 @@ def process(input_directory, input_name=None, status=0, client_agent='manual', d logger.info("Calling {0}:{1} to post-process:{2}".format(section_name, input_category, input_name)) if section_name in ["CouchPotato", "Radarr"]: - result = movies.process(section_name, input_directory, input_name, status, client_agent, download_id, - input_category, failure_link) + result = movies.process(section_name, input_directory, input_name, status, client_agent, download_id, input_category, failure_link) elif section_name in ["SickBeard", "NzbDrone", "Sonarr"]: - result = tv.process(section_name, input_directory, input_name, status, client_agent, - download_id, input_category, failure_link) + result = tv.process(section_name, input_directory, input_name, status, client_agent, download_id, input_category, failure_link) elif section_name in ["HeadPhones", "Lidarr"]: result = music.process(section_name, input_directory, input_name, status, client_agent, input_category) elif section_name == "Mylar": - result = comics.process(section_name, input_directory, input_name, status, client_agent, - input_category) + result = comics.process(section_name, input_directory, input_name, status, client_agent, input_category) elif section_name == "Gamez": result = games.process(section_name, input_directory, input_name, status, client_agent, input_category) elif section_name == 'UserScript': result = external_script(input_directory, input_name, input_category, section[usercat]) else: - result = [-1, ""] + result = ProcessResult( + message="", + status_code=-1, + ) plex_update(input_category) - if result[0] == 0: + if result.staus_code == 0: if client_agent != 'manual': # update download status in our DB update_download_info_status(input_name, 1) @@ -151,7 +152,10 @@ def main(args, section=None): logger.debug("Options passed into nzbToMedia: {0}".format(args)) # Post-Processing Result - result = [0, ""] + result = ProcessResult( + message="", + status_code=0, + ) status = 0 # NZBGet @@ -289,27 +293,27 @@ def main(args, section=None): results = process(dir_name, input_name, 0, client_agent=client_agent, download_id=download_id or None, input_category=subsection) - if results[0] != 0: + if results.status_code != 0: logger.error("A problem was reported when trying to perform a manual run for {0}:{1}.".format (section, subsection)) result = results - if result[0] == 0: + if result.status_code == 0: logger.info("The {0} script completed successfully.".format(args[0])) - if result[1]: - print(result[1] + "!") + if result.message: + print(result.message + "!") if 'NZBOP_SCRIPTDIR' in os.environ: # return code for nzbget v11 del core.MYAPP return core.NZBGET_POSTPROCESS_SUCCESS else: logger.error("A problem was reported in the {0} script.".format(args[0])) - if result[1]: - print(result[1] + "!") + if result.message: + print(result.message + "!") if 'NZBOP_SCRIPTDIR' in os.environ: # return code for nzbget v11 del core.MYAPP return core.NZBGET_POSTPROCESS_ERROR del core.MYAPP - return result[0] + return result.status_code if __name__ == '__main__':