diff --git a/CHANGES.md b/CHANGES.md index 1217f0d6..86feeab4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -90,6 +90,7 @@ * Add X-Filename response header to getbanner, getposter * Add X-Fanartname response header for sg.show.getfanart * Change remove some non-release group stuff from newnab results +* Add SickGear-NZBGet dedicated post processing script, see.. \autoProcessTV\SickGear-NG\INSTALL.txt [develop changelog] diff --git a/autoProcessTV/SickGear-NG/INSTALL.txt b/autoProcessTV/SickGear-NG/INSTALL.txt new file mode 100644 index 00000000..eb8ae033 --- /dev/null +++ b/autoProcessTV/SickGear-NG/INSTALL.txt @@ -0,0 +1,34 @@ +SickGear PostProcessing script for NZBGet +========================================= + +If NZBGet v17+ is installed on the same system as SickGear then as a local install, + +1) Add the location of this script file to NZBGet Settings/PATHS/ScriptDir + +2) Navigate to any named TV category at Settings/Categories, click "Choose" Category.Extensions then Apply SickGear-NG + +This is the best set up to automatically get script updates from SickGear + +############# + +If NZBGet is installed on a different system to SickGear or NZBGet is v16 or earlier, then as a remote/older install, + +1) Copy the directory with/or this single script file to path set in NZBGet Settings/PATHS/ScriptDir + +2) Refresh the NZBGet page and navigate to Settings/SickGear-NG + +3) Click View -> Compact to remove any tick and un hide tips and suggestions + +4) The bare minimum change is the sg_base_path setting + +5) Navigate to any named TV category at Settings/Categories, click "Choose" Category.Extensions then Apply SickGear-NG + +You will need to manually update your script with this set up + +############# + +Notes: +Debian doesn't have pip, _if_ requests is needed, try "apt install python-requests" + +----- +Enjoy diff --git a/autoProcessTV/SickGear-NG/SickGear-NG.py b/autoProcessTV/SickGear-NG/SickGear-NG.py new file mode 100755 index 00000000..493f3c67 --- /dev/null +++ b/autoProcessTV/SickGear-NG/SickGear-NG.py @@ -0,0 +1,651 @@ +#!/usr/bin/env python +# +# ############################################################################## +# ############################################################################## +# +# SickGear PostProcessing script for NZBGet +# ========================================= +# +# If NZBGet v17+ is installed on the same system as SickGear then as a local install, +# +# 1) Add the location of this script file to NZBGet Settings/PATHS/ScriptDir +# +# 2) Navigate to any named TV category at Settings/Categories, click "Choose" Category.Extensions then Apply SickGear-NG +# +# This is the best set up to automatically get script updates from SickGear +# +# ############# +# +# If NZBGet is installed on a different system to SickGear or NZBGet is v16 or earlier, then as a remote/older install, +# +# 1) Copy the directory with/or this single script file to path set in NZBGet Settings/PATHS/ScriptDir +# +# 2) Refresh the NZBGet page and navigate to Settings/SickGear-NG +# +# 3) Click View -> Compact to remove any tick and un hide tips and suggestions +# +# 4) The bare minimum change is the sg_base_path setting +# +# 5) Navigate to any named TV category at Settings/Categories, click "Choose" Category.Extensions then Apply SickGear-NG +# +# You will need to manually update your script with this set up +# +# ############ +# +# Notes: +# Debian doesn't have pip, _if_ requests is needed, try "apt install python-requests" +# ----- +# Enjoy +# +# ############################################################################## +# ############################################################################## +# +# Copyright (C) 2016 SickGear Developers +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# + +############################################################################## +### NZBGET QUEUE/POST-PROCESSING SCRIPT ### +### QUEUE EVENTS: NZB_ADDED, NZB_DELETED, URL_COMPLETED, NZB_MARKED ### + +# Send PostProcessing requests to SickGear +# +# PostProcessing-Script version: 1.3. +# +# +# +# Setup steps NZBGet Version +# +# +# +# 1 +# +# +# If NZBGet v17 or newer is installed on the same system as SickGear, then add the +# location of this script file to NZBGet Settings/PATHS/ScriptDir +#

+# Or, if NZBGet v16 or earlier is installed on the same system as SickGear and +# if python requests library +# is not installed, then sg_base_path must be set +#

+# Or, if NZBGet is installed on a different system to SickGear, then python +# requests library +# must be installed onto the NZBGet system +#
+#
+# +# +# 2 +# +# +# Then, for any install type, click Choose +# then apply "SickGear-NG" in a TV Category at NZBGet Settings/CATEGORIES, +# save all changes and reload NZBGet +# +# +# +#
+# +# Note This script requires Python 2.7+ and may not work with Python 3.x+ +# +############################################################################## +### OPTIONS ### +# +#test connection@Test SickGear connection +# +# +# Optional +# SickGear base installation path. +# use where NZBGet v16 or older is installed on the same system as SickGear, and no python requests library is installed +# (use "pip list" to check installed modules) +#sg_base_path= + +# +# Optional +# SickGear server ipaddress [default:127.0.0.1 aka localhost]. +# change if SickGear is not installed on the same localhost as NZBGet +#sg_host=localhost + +# +# Optional +# SickGear HTTP Port [default:8081] (1025-65535). +#sg_port=8081 + +# +# Optional +# SickGear Username. +#sg_username= + +# +# Optional +# SickGear Password. +#sg_password= + +# +# Optional +# SickGear has SSL enabled [default:No] (yes, no). +#sg_ssl=no + +# +# Advanced use +# SickGear Web Root. +# change if using a custom SickGear web_root setting (e.g. for a reverse proxy) +#sg_web_root= + +# +# Optional +# Print more logging messages [default:No] (yes, no). +# For debugging or if you need to report a bug. +#sg_verbose=no + +### NZBGET QUEUE/POST-PROCESSING SCRIPT ### +############################################################################## +import locale +import os +import re +import sys + +__version__ = '1.3' + +verbose = 0 or 'yes' == os.environ.get('NZBPO_SG_VERBOSE', 'no') + +# NZBGet exit codes for post-processing scripts (Queue-scripts don't have any special exit codes). +POSTPROCESS_SUCCESS, POSTPROCESS_ERROR, POSTPROCESS_NONE = 93, 94, 95 + +failed = False + +# define minimum dir size, downloads under this size will be handled as failure +min_dir_size = 20 * 1024 * 1024 + + +class Logger: + INFO, DETAIL, ERROR, WARNING = 'INFO', 'DETAIL', 'ERROR', 'WARNING' + # '[NZB]' send a command message to NZBGet (no log) + NZB = 'NZB' + + def __init__(self): + pass + + @staticmethod + def safe_print(msg_type, message): + try: + print '[%s] %s' % (msg_type, message.encode(SYS_ENCODING)) + except (StandardError, Exception): + try: + print '[%s] %s' % (msg_type, message) + except (StandardError, Exception): + try: + print '[%s] %s' % (msg_type, repr(message)) + except (StandardError, Exception): + pass + + @staticmethod + def log(message, msg_type=INFO): + size = 900 + if size > len(message): + Logger.safe_print(msg_type, message) + else: + for group in (message[pos:pos + size] for pos in xrange(0, len(message), size)): + Logger.safe_print(msg_type, group) + + +if 'nt' == os.name: + import ctypes + + class WinEnv: + def __init__(self): + pass + + @staticmethod + def get_environment_variable(name): + name = unicode(name) # ensures string argument is unicode + n = ctypes.windll.kernel32.GetEnvironmentVariableW(name, None, 0) + env_value = None + if n: + buf = ctypes.create_unicode_buffer(u'\0'*n) + ctypes.windll.kernel32.GetEnvironmentVariableW(name, buf, n) + env_value = buf.value + verbose and Logger.log('Get var(%s) = %s' % (name, env_value or n)) + return env_value + + def __getitem__(self, key): + return self.get_environment_variable(key) + + def get(self, key, default=None): + r = self.get_environment_variable(key) + return r if r is not None else default + + env_var = WinEnv() +else: + class LinuxEnv(object): + def __init__(self, environ): + self.environ = environ + + def __getitem__(self, key): + v = self.environ.get(key) + try: + return v.decode(SYS_ENCODING) if isinstance(v, str) else v + except (UnicodeDecodeError, UnicodeEncodeError): + return v + + def get(self, key, default=None): + v = self[key] + return v if v is not None else default + + env_var = LinuxEnv(os.environ) + + +SYS_ENCODING = None +try: + locale.setlocale(locale.LC_ALL, '') +except (locale.Error, IOError): + pass +try: + SYS_ENCODING = locale.getpreferredencoding() +except (locale.Error, IOError): + pass +if not SYS_ENCODING or SYS_ENCODING in ('ANSI_X3.4-1968', 'US-ASCII', 'ASCII'): + SYS_ENCODING = 'UTF-8' + + +verbose and Logger.log('%s(%s) env dump = %s' % (('posix', 'nt')['nt' == os.name], SYS_ENCODING, os.environ)) + + +class Ek: + def __init__(self): + pass + + @staticmethod + def fix_string_encoding(x): + if str == type(x): + try: + return x.decode(SYS_ENCODING) + except UnicodeDecodeError: + return None + elif unicode == type(x): + return x + return None + + @staticmethod + def fix_list_encoding(x): + if type(x) not in (list, tuple): + return x + return filter(lambda i: None is not i, map(Ek.fix_string_encoding, i)) + + @staticmethod + def encode_item(x): + try: + return x.encode(SYS_ENCODING) + except UnicodeEncodeError: + return x.encode(SYS_ENCODING, 'ignore') + + @staticmethod + def ek(func, *args, **kwargs): + if 'nt' == os.name: + func_result = func(*args, **kwargs) + else: + func_result = func(*[Ek.encode_item(x) if type(x) == str else x for x in args], **kwargs) + + if type(func_result) in (list, tuple): + return Ek.fix_list_encoding(func_result) + elif str == type(func_result): + return Ek.fix_string_encoding(func_result) + return func_result + + +# Depending on the mode in which the script was called (queue-script NZBNA_DELETESTATUS +# or post-processing-script) a different set of parameters (env. vars) +# is passed. They also have different prefixes: +# - NZBNA in queue-script mode; +# - NZBPP in pp-script mode. +env_run_mode = ('PP', 'NA')['NZBNA_EVENT' in os.environ] + + +def nzbget_var(name, default='', namespace=env_run_mode): + return env_var.get('NZB%s_%s' % (namespace, name), default) + + +def nzbget_opt(name, default=''): + return nzbget_var(name, default, 'OP') + + +def nzbget_plugin_opt(name, default=''): + return nzbget_var('SG_%s' % name, default, 'PO') + + +sg_path = nzbget_plugin_opt('BASE_PATH') +if not sg_path or not Ek.ek(os.path.isdir, sg_path): + try: + script_path = Ek.ek(os.path.dirname, __file__) + sg_path = Ek.ek(os.path.dirname, Ek.ek(os.path.dirname, script_path)) + except (StandardError, Exception): + pass +if sg_path and Ek.ek(os.path.isdir, Ek.ek(os.path.join, sg_path, 'lib')): + sys.path.insert(1, Ek.ek(os.path.join, sg_path, 'lib')) + + +try: + import requests +except ImportError: + Logger.log('You must set SickGear sg_base_path in script config or install python requests library', Logger.ERROR) + sys.exit(1) + + +def get_size(start_path='.'): + if Ek.ek(os.path.isfile, start_path): + return Ek.ek(os.path.getsize, start_path) + total_size = 0 + for dirpath, dirnames, filenames in Ek.ek(os.walk, start_path): + for f in filenames: + if not f.lower().endswith(('.nzb', '.jpg', '.jpeg', '.gif', '.png', '.tif', '.nfo', '.txt', '.srt', '.sub', + '.sbv', '.idx', '.bat', '.sh', '.exe', '.pdf')): + fp = Ek.ek(os.path.join, dirpath, f) + total_size += Ek.ek(os.path.getsize, fp) + return total_size + + +def try_int(s, s_default=0): + try: + return int(s) + except (StandardError, Exception): + return s_default + + +def try_float(s, s_default=0): + try: + return float(s) + except (StandardError, Exception): + return s_default + + +class ExitReason: + def __init__(self): + pass + PP_SUCCESS = 0 + FAIL_SUCCESS = 1 + MARKED_BAD_SUCCESS = 2 + DELETED = 5 + SAME_DUPEKEY = 10 + UNFINISHED_DOWNLOAD = 11 + NONE = 20 + NONE_SG = 21 + PP_ERROR = 25 + FAIL_ERROR = 26 + MARKED_BAD_ERROR = 27 + + +def script_exit(status, reason, runmode=None): + Logger.log('NZBPR_SICKGEAR_PROCESSED=%s_%s_%s' % (status, runmode or env_run_mode, reason), Logger.NZB) + sys.exit(status) + + +def get_old_status(): + old_status = env_var.get('NZBPR_SICKGEAR_PROCESSED', '') + status_regex = re.compile(r'(\d+)_(\w\w)_(\d+)') + if old_status and status_regex.search(old_status) is not None: + s = status_regex.match(old_status) + return try_int(s.group(1)), s.group(2), try_int(s.group(3)) + return POSTPROCESS_NONE, env_run_mode, ExitReason.NONE + + +markbad = 'NZB_MARKED' == env_var.get('NZBNA_EVENT') and 'BAD' == env_var.get('NZBNA_MARKSTATUS') + +good_statuses = [(POSTPROCESS_SUCCESS, 'PP', ExitReason.FAIL_SUCCESS), # successfully failed pp'ed + (POSTPROCESS_SUCCESS, 'NA', ExitReason.FAIL_SUCCESS), # queue, successfully failed sent + (POSTPROCESS_SUCCESS, 'NA', ExitReason.MARKED_BAD_SUCCESS)] # queue, mark bad+successfully failed sent + +if not markbad: + good_statuses.append((POSTPROCESS_SUCCESS, 'PP', ExitReason.PP_SUCCESS)) # successfully pp'ed + + +# Start up checks +def start_check(): + + # Check if the script is called from a compatible NZBGet version (as queue-script or as pp-script) + nzbget_version = re.search(r'^(\d+\.\d+)', nzbget_opt('VERSION', '0.1')) + nzbget_version = nzbget_version.group(1) if nzbget_version and len(nzbget_version.groups()) >= 1 else '0.1' + nzbget_version = try_float(nzbget_version) + if 17 > nzbget_version: + Logger.log('This script is designed to be called from NZBGet 17.0 or later.') + sys.exit(0) + + if 'NZB_ADDED' == env_var.get('NZBNA_EVENT'): + Logger.log('NZBPR_SICKGEAR_PROCESSED=', Logger.NZB) # reset var in case of Download Again + sys.exit(0) + + # This script processes only certain queue events. + # For compatibility with newer NZBGet versions it ignores event types it doesn't know + if env_var.get('NZBNA_EVENT') not in ['NZB_DELETED', 'URL_COMPLETED', 'NZB_MARKED', None]: + sys.exit(0) + + if 'NZB_MARKED' == env_var.get('NZBNA_EVENT') and 'BAD' != env_var.get('NZBNA_MARKSTATUS'): + Logger.log('Marked as [%s], nothing to do, existing' % env_var.get('NZBNA_MARKSTATUS', '')) + sys.exit(0) + + old_exit_status = get_old_status() + if old_exit_status in good_statuses and not ( + ExitReason.FAIL_SUCCESS == old_exit_status[2] and 'SUCCESS' == nzbget_var('TOTALSTATUS')): + Logger.log('Found result from a previous completed run, exiting') + script_exit(old_exit_status[0], old_exit_status[2], old_exit_status[1]) + + # If called via "Post-process again" from history details dialog the download may not exist anymore + if 'NZBNA_EVENT' not in os.environ and 'NZBPP_DIRECTORY' in os.environ: + directory = nzbget_var('DIRECTORY') + if not directory or not Ek.ek(os.path.exists, directory): + Logger.log('No files for postprocessor, look back in your NZBGet logs if required, exiting') + script_exit(POSTPROCESS_NONE, ExitReason.NONE) + + +def call_nzbget_direct(url_command): + # Connect to NZBGet and call an RPC-API method without using python's XML-RPC which is slow for large amount of data + # First we need connection info: host, port and password of NZBGet server, NZBGet passes configuration options to + # scripts using environment variables + host, port, username, password = [nzbget_opt('CONTROL%s' % name) for name in 'IP', 'PORT', 'USERNAME', 'PASSWORD'] + url = 'http://%s:%s/jsonrpc/%s' % ((host, '127.0.0.1')['0.0.0.0' == host], port, url_command) + + try: + response = requests.get(url, auth=(username, password)) + except requests.RequestException: + return '' + + return response.content if response.ok else '' + + +def call_sickgear(nzb_name, dir_name, test=False): + + global failed + ssl, host, port, username, password, webroot = [nzbget_plugin_opt(name, default) for name, default in + ('SSL', 'no'), ('HOST', 'localhost'), ('PORT', '8081'), + ('USERNAME', ''), ('PASSWORD', ''), ('WEB_ROOT', '')] + protocol = 'http%s://' % ('', 's')['yes' == ssl] + webroot = any(webroot) and '/%s' % webroot.strip('/') or '' + url = '%s%s:%s%s/home/postprocess/processEpisode' % (protocol, host, port, webroot) + + dupescore = nzbget_var('DUPESCORE') + dupekey = nzbget_var('DUPEKEY') + nzbid = nzbget_var('NZBID') + params = {'nzbName': '%s.nzb' % (nzb_name and re.sub('(?i)\.nzb$', '', nzb_name) or None), 'dir': dir_name, + 'failed': int(failed), 'quiet': 1, 'stream': 1, 'force': 1, 'dupekey': dupekey, 'dupescore': dupescore, + 'nzbid': nzbid, 'ppVersion': __version__, 'is_basedir': 0, 'client': 'nzbget'} + if test: + params['test'] = '1' + Logger.log('Opening URL: %s with params: %s' % (url, params)) + try: + s = requests.Session() + if username or password: + login = '%s%s:%s%s/login' % (protocol, host, port, webroot) + login_params = {'username': username, 'password': password} + s.post(login, data=login_params, stream=True, verify=False) + r = s.get(url, auth=(username, password), params=params, stream=True, verify=False, timeout=900) + except (StandardError, Exception): + Logger.log('Unable to open URL: %s' % url, Logger.ERROR) + return False + + success = False + try: + if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: + Logger.log('Server returned status %s' % str(r.status_code), Logger.ERROR) + return False + + for line in r.iter_lines(): + if line: + Logger.log(line, Logger.DETAIL) + if test: + if 'Connection success!' in line: + return True + elif not failed and 'Failed download detected:' in line: + failed = True + global markbad + markbad = True + Logger.log('MARK=BAD', Logger.NZB) + success = ('Processing succeeded' in line or 'Successfully processed' in line or + (1 == failed and 'Successful failed download processing' in line)) + except Exception as e: + Logger.log(str(e), Logger.ERROR) + + return success + + +def find_dupekey_history(dupekey, nzb_id): + + if not dupekey: + return False + data = call_nzbget_direct('history?hidden=true') + cur_status = cur_dupekey = cur_id = '' + cur_dupescore = 0 + for line in data.splitlines(): + if line.startswith('"NZBID" : '): + cur_id = line[10:-1] + elif line.startswith('"Status" : '): + cur_status = line[12:-2] + elif line.startswith('"DupeKey" : '): + cur_dupekey = line[13:-2] + elif line.startswith('"DupeScore" : '): + cur_dupescore = try_int(line[14:-1]) + elif cur_id and line.startswith('}'): + if (cur_status.startswith('SUCCESS') and dupekey == cur_dupekey and + cur_dupescore >= try_int(nzbget_var('DUPESCORE')) and cur_id != nzb_id): + return True + cur_status = cur_dupekey = cur_id = '' + cur_dupescore = 0 + return False + + +def find_dupekey_queue(dupekey, nzb_id): + + if not dupekey: + return False + data = call_nzbget_direct('listgroups') + cur_status = cur_dupekey = cur_id = '' + for line in data.splitlines(): + if line.startswith('"NZBID" : '): + cur_id = line[10:-1] + elif line.startswith('"Status" : '): + cur_status = line[12:-2] + elif line.startswith('"DupeKey" : '): + cur_dupekey = line[13:-2] + elif cur_id and line.startswith('}'): + if 'PAUSED' != cur_status and dupekey == cur_dupekey and cur_id != nzb_id: + return True + cur_status = cur_dupekey = cur_id = '' + return False + + +def check_for_failure(directory): + + failure = True + dupekey = nzbget_var('DUPEKEY') + if 'PP' == env_run_mode: + total_status = nzbget_var('TOTALSTATUS') + status = nzbget_var('STATUS') + if 'WARNING' == total_status and status in ['WARNING/REPAIRABLE', 'WARNING/SPACE', 'WARNING/DAMAGED']: + Logger.log('WARNING/REPAIRABLE' == status and 'Download is damaged but probably can be repaired' or + 'WARNING/SPACE' == status and 'Out of Diskspace' or + 'Par-check is required but is disabled in settings', Logger.WARNING) + script_exit(POSTPROCESS_ERROR, ExitReason.UNFINISHED_DOWNLOAD) + elif 'DELETED' == total_status: + Logger.log('Download was deleted and manually processed, nothing to do, exiting') + script_exit(POSTPROCESS_NONE, ExitReason.DELETED) + elif 'SUCCESS' == total_status: + # check for min dir size + if get_size(directory) > min_dir_size: + failure = False + else: + nzb_id = nzbget_var('NZBID') + if (not markbad and find_dupekey_queue(dupekey, nzb_id)) or find_dupekey_history(dupekey, nzb_id): + Logger.log('Download with same Dupekey in download queue or history, exiting') + script_exit(POSTPROCESS_NONE, ExitReason.SAME_DUPEKEY) + nzb_delete_status = nzbget_var('DELETESTATUS') + if nzb_delete_status == 'MANUAL': + Logger.log('Download was manually deleted, exiting') + script_exit(POSTPROCESS_NONE, ExitReason.DELETED) + + # Check if it's a Failed Download not added by SickGear + if failure and (not dupekey or not dupekey.startswith('SickGear-')): + Logger.log('Failed download was not added by SickGear, exiting') + script_exit(POSTPROCESS_NONE, ExitReason.NONE_SG) + + return failure + + +# Check if the script is executed from settings page with a custom command +command = os.environ.get('NZBCP_COMMAND') +if None is not command: + if 'test connection' == command: + Logger.log('Test connection...') + result = call_sickgear('', '', test=True) + if True is result: + Logger.log('Connection Test successful!') + sys.exit(POSTPROCESS_SUCCESS) + Logger.log('Connection Test failed!', Logger.ERROR) + sys.exit(POSTPROCESS_ERROR) + + Logger.log('Invalid command passed to SickGear-NG: ' + command, Logger.ERROR) + sys.exit(POSTPROCESS_ERROR) + + +# Script body +def main(): + + global failed + # Do start up check + start_check() + + # Read context (what nzb is currently being processed) + directory = nzbget_var('DIRECTORY') + nzbname = nzbget_var('NZBNAME') + failed = check_for_failure(directory) + + if call_sickgear(nzbname, directory): + Logger.log('Successfully post-processed %s' % nzbname) + sys.stdout.flush() + script_exit(POSTPROCESS_SUCCESS, + failed and (markbad and ExitReason.MARKED_BAD_SUCCESS or ExitReason.FAIL_SUCCESS) or + ExitReason.PP_SUCCESS) + + Logger.log('Failed to post-process %s' % nzbname, Logger.ERROR) + sys.stdout.flush() + script_exit(POSTPROCESS_ERROR, + failed and (markbad and ExitReason.MARKED_BAD_ERROR or ExitReason.FAIL_ERROR) or + ExitReason.PP_ERROR) + + +# Execute main script function +main() + +script_exit(POSTPROCESS_NONE, ExitReason.NONE) diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py index c876944b..122d136a 100755 --- a/sickbeard/__init__.py +++ b/sickbeard/__init__.py @@ -257,6 +257,7 @@ NZBGET_CATEGORY = None NZBGET_HOST = None NZBGET_USE_HTTPS = False NZBGET_PRIORITY = 100 +NZBGET_SCRIPT_VERSION = None SAB_USERNAME = None SAB_PASSWORD = None @@ -600,7 +601,8 @@ def initialize(console_logging=True): ALLOW_HIGH_PRIORITY, SEARCH_UNAIRED, UNAIRED_RECENT_SEARCH_ONLY # Search Settings/NZB search global USE_NZBS, NZB_METHOD, NZB_DIR, SAB_HOST, SAB_USERNAME, SAB_PASSWORD, SAB_APIKEY, SAB_CATEGORY, \ - NZBGET_USE_HTTPS, NZBGET_HOST, NZBGET_USERNAME, NZBGET_PASSWORD, NZBGET_CATEGORY, NZBGET_PRIORITY + NZBGET_USE_HTTPS, NZBGET_HOST, NZBGET_USERNAME, NZBGET_PASSWORD, NZBGET_CATEGORY, NZBGET_PRIORITY, \ + NZBGET_SCRIPT_VERSION # Search Settings/Torrent search global USE_TORRENTS, TORRENT_METHOD, TORRENT_DIR, TORRENT_HOST, TORRENT_USERNAME, TORRENT_PASSWORD, \ TORRENT_LABEL, TORRENT_PATH, TORRENT_SEED_TIME, TORRENT_PAUSED, TORRENT_HIGH_BANDWIDTH, TORRENT_VERIFY_CERT @@ -908,6 +910,15 @@ def initialize(console_logging=True): NZBGET_USE_HTTPS = bool(check_setting_int(CFG, 'NZBget', 'nzbget_use_https', 0)) NZBGET_PRIORITY = check_setting_int(CFG, 'NZBget', 'nzbget_priority', 100) + try: + ng_script_file = ek.ek(os.path.join, ek.ek(os.path.dirname, ek.ek(os.path.dirname, __file__)), + 'autoProcessTV', 'SickGear-NG', 'SickGear-NG.py') + with open(ng_script_file, 'r') as ng: + text = ng.read() + NZBGET_SCRIPT_VERSION = re.search(r'__version__ =.*\'([0-9.]+)\'.*$', text, flags=re.M).group(1) + except (StandardError, Exception): + NZBGET_SCRIPT_VERSION = None + TORRENT_USERNAME = check_setting_str(CFG, 'TORRENT', 'torrent_username', '') TORRENT_PASSWORD = check_setting_str(CFG, 'TORRENT', 'torrent_password', '') TORRENT_HOST = check_setting_str(CFG, 'TORRENT', 'torrent_host', '') diff --git a/sickbeard/processTV.py b/sickbeard/processTV.py index b6b23f9d..4208e1c3 100644 --- a/sickbeard/processTV.py +++ b/sickbeard/processTV.py @@ -58,10 +58,11 @@ except ImportError: class ProcessTVShow(object): """ Process a TV Show """ - def __init__(self, webhandler=None, is_basedir=True): + def __init__(self, webhandler=None, is_basedir=True, skip_failure_processing=False): self.files_passed = 0 self.files_failed = 0 self.fail_detected = False + self.skip_failure_processing = skip_failure_processing self._output = [] self.webhandler = webhandler self.is_basedir = is_basedir @@ -919,6 +920,10 @@ class ProcessTVShow(object): def _process_failed(self, dir_name, nzb_name, showObj=None): """ Process a download that did not complete correctly """ + if self.skip_failure_processing: + self._log_helper('Download was not added by SickGear, ignoring failure', logger.WARNING) + return + if sickbeard.USE_FAILED_DOWNLOADS: processor = None @@ -947,8 +952,9 @@ class ProcessTVShow(object): # backward compatibility prevents the case of this function name from being updated to PEP8 def processDir(dir_name, nzb_name=None, process_method=None, force=False, force_replace=None, - failed=False, type='auto', cleanup=False, webhandler=None, showObj=None, is_basedir=True): + failed=False, type='auto', cleanup=False, webhandler=None, showObj=None, is_basedir=True, + skip_failure_processing=False): # backward compatibility prevents the case of this function name from being updated to PEP8 - return ProcessTVShow(webhandler, is_basedir).process_dir( + return ProcessTVShow(webhandler, is_basedir, skip_failure_processing=skip_failure_processing).process_dir( dir_name, nzb_name, process_method, force, force_replace, failed, type, cleanup, showObj) diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index 7da4499c..68a08763 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -2519,7 +2519,11 @@ class HomePostProcess(Home): return t.respond() def processEpisode(self, dir=None, nzbName=None, jobName=None, quiet=None, process_method=None, force=None, - force_replace=None, failed='0', type='auto', stream='0', dupekey=None, is_basedir='1', **kwargs): + force_replace=None, failed='0', type='auto', stream='0', dupekey=None, is_basedir='1', + client=None, **kwargs): + + if 'test' in kwargs and kwargs['test'] in ['True', True, 1, '1']: + return 'Connection success!' if not dir and ('0' == failed or not nzbName): self.redirect('/home/postprocess/') @@ -2536,6 +2540,15 @@ class HomePostProcess(Home): break showObj = helpers.find_show_by_id(sickbeard.showList, {indexer: int(m.group(2))}, no_mapped_ids=True) + + skip_failure_processing = isinstance(client, basestring) and 'nzbget' == client and \ + (not isinstance(dupekey, basestring) or None is re.search(r'^SickGear-([A-Za-z]*)(\d+)-', dupekey)) + + if isinstance(client, basestring) and 'nzbget' == client and \ + sickbeard.NZBGET_SCRIPT_VERSION != kwargs.get('ppVersion', '0'): + logger.log('Calling SickGear-NG.py script %s is not current version %s, please update.' % + (kwargs.get('ppVersion', '0'), sickbeard.NZBGET_SCRIPT_VERSION), logger.ERROR) + result = processTV.processDir(dir.decode('utf-8') if dir else None, nzbName.decode('utf-8') if nzbName else None, process_method=process_method, type=type, cleanup='cleanup' in kwargs and kwargs['cleanup'] in ['on', '1'], @@ -2543,7 +2556,8 @@ class HomePostProcess(Home): force_replace=force_replace in ['on', '1'], failed='0' != failed, webhandler=self.send_message if stream != '0' else None, - showObj=showObj, is_basedir=is_basedir in ['on', '1']) + showObj=showObj, is_basedir=is_basedir in ['on', '1'], + skip_failure_processing=skip_failure_processing) if '0' != stream: return