mirror of
https://github.com/SickGear/SickGear.git
synced 2024-11-30 16:33:37 +00:00
667 lines
25 KiB
Python
Executable file
667 lines
25 KiB
Python
Executable file
#!/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 v16 or earlier is installed, then as an 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 or enter `python -m pip install requests` at admin commandline
|
|
#
|
|
# 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.6.
|
|
# <!--
|
|
# For more info and updates please visit forum topic at
|
|
# -->
|
|
# <span style="display:block;position:absolute;right:20px;top:105px;width:138px;height:74px;background:url(https://raw.githubusercontent.com/SickGear/SickGear/master/gui/slick/images/sickgear.png)"></span>
|
|
# <span style="display:inline-block;margin-top:10px" class="label label-important">
|
|
# Setup steps</span> <span class="label label-important" style="display:inline-block;cursor:pointer" data-toggle="modal" href="#InfoDialog">NZBGet Version</span>
|
|
# <span style="display:block;color:#666">
|
|
# <span style="display:block;padding:4px;margin-top:3px;background-color:#efefef;border:1px solid #ccc;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px">
|
|
# <span style="width:1em;float:left;padding:3px 0 0 3px">
|
|
# <span class="label label-important">1</span>
|
|
# </span>
|
|
# <span style="display:block;margin-left:1.75em;padding:3px 3px 3px 0">
|
|
# If <span style="font-weight:bold">NZBGet v17 or newer</span> is installed on the same system as SickGear, then add the
|
|
# location of this script file to NZBGet Settings/PATHS/ScriptDir
|
|
# <br /><br />
|
|
# Or, if <span style="font-weight:bold">NZBGet v16 or earlier</span> is installed on the same system as SickGear and
|
|
# if python <a href="https://pypi.python.org/pypi/requests" title="requests library page" target="_blank">requests library</a>
|
|
# is not installed, then <strong style="font-weight:bold;color:#128D12 !important">sg_base_path</strong> must be set
|
|
# </span>
|
|
# </span>
|
|
# <span style="display:block;padding:4px;margin-top:3px;background-color:#efefef;border:1px solid #ccc;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px">
|
|
# <span style="width:1em;float:left;padding:3px 0 0 3px">
|
|
# <span class="label label-important">2</span>
|
|
# </span>
|
|
# <span style="display:block;margin-left:1.75em;padding:3px 3px 3px 0">
|
|
# Then, for <span style="font-weight:bold">any install</span> type, click <span class="btn" style="padding:1px 5px 0;line-height:16px">Choose</span>
|
|
# then apply "<span style="color:#222">SickGear-NG</span>" in a TV Category at NZBGet Settings/CATEGORIES,
|
|
# save all changes and reload NZBGet
|
|
#
|
|
# </span>
|
|
# </span>
|
|
# </span>
|
|
#
|
|
# <span class="label label-warning">Note</span> This script requires Python 2.7+ and may not work with Python 3.x+
|
|
#
|
|
##############################################################################
|
|
### OPTIONS ###
|
|
#
|
|
#test connection@Test SickGear connection
|
|
#
|
|
# <span class="label label-info">
|
|
# Optional</span>
|
|
# SickGear <span style="font-weight:bold;color:#128D12 !important">base installation path</span>.
|
|
# 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=
|
|
|
|
# <span class="label label-info">
|
|
# Optional</span>
|
|
# 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
|
|
|
|
# <span class="label label-info">
|
|
# Optional</span>
|
|
# SickGear HTTP Port [default:8081] (1025-65535).
|
|
#sg_port=8081
|
|
|
|
# <span class="label label-info">
|
|
# Optional</span>
|
|
# SickGear Username.
|
|
#sg_username=
|
|
|
|
# <span class="label label-info">
|
|
# Optional</span>
|
|
# SickGear Password.
|
|
#sg_password=
|
|
|
|
# <span class="label label-info">
|
|
# Optional</span>
|
|
# SickGear has SSL enabled [default:No] (yes, no).
|
|
#sg_ssl=no
|
|
|
|
# <span class="label label-warning">
|
|
# Advanced use</span>
|
|
# SickGear Web Root.
|
|
# change if using a custom SickGear web_root setting (e.g. for a reverse proxy)
|
|
#sg_web_root=
|
|
|
|
# <span class="label label-info">
|
|
# Optional</span>
|
|
# 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
|
|
import warnings
|
|
|
|
__version__ = '1.6'
|
|
|
|
verbose = 0 or 'yes' == os.environ.get('NZBPO_SG_VERBOSE', 'no')
|
|
|
|
warnings.filterwarnings('ignore', module=r'.*connectionpool.*', message='.*certificate verification.*')
|
|
warnings.filterwarnings('ignore', module=r'.*ssl_.*', message='.*SSLContext object.*')
|
|
|
|
# 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 win_encode_unicode(x):
|
|
if isinstance(x, str):
|
|
try:
|
|
return x.decode('UTF-8')
|
|
except UnicodeDecodeError:
|
|
return x
|
|
return x
|
|
|
|
@staticmethod
|
|
def ek(func, *args, **kwargs):
|
|
if 'nt' == os.name:
|
|
# convert all str parameter values to unicode
|
|
args = tuple([x if not isinstance(x, str) else Ek.win_encode_unicode(x) for x in args])
|
|
kwargs = {k: x if not isinstance(x, str) else Ek.win_encode_unicode(x) for k, x in
|
|
kwargs.iteritems()}
|
|
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)
|
|
r = s.get(login, verify=False)
|
|
login_params = {'username': username, 'password': password}
|
|
if 401 == r.status_code and r.cookies.get('_xsrf'):
|
|
login_params['_xsrf'] = r.cookies.get('_xsrf')
|
|
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)
|