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