|
@ -33,7 +33,8 @@ except: |
|
|
# Work-around for Python-ports with bad "locale" support |
|
|
# Work-around for Python-ports with bad "locale" support |
|
|
pass |
|
|
pass |
|
|
try: |
|
|
try: |
|
|
import win32api, win32file |
|
|
import win32api |
|
|
|
|
|
import win32file |
|
|
except ImportError: |
|
|
except ImportError: |
|
|
pass |
|
|
pass |
|
|
|
|
|
|
|
@ -65,8 +66,9 @@ import sabnzbd.rss |
|
|
import sabnzbd.emailer |
|
|
import sabnzbd.emailer |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
############################################################################## |
|
|
# API error messages |
|
|
# API error messages |
|
|
|
|
|
############################################################################## |
|
|
_MSG_NO_VALUE = 'expect one parameter' |
|
|
_MSG_NO_VALUE = 'expect one parameter' |
|
|
_MSG_NO_VALUE2 = 'expect two parameters' |
|
|
_MSG_NO_VALUE2 = 'expect two parameters' |
|
|
_MSG_INT_VALUE = 'expect integer value' |
|
|
_MSG_INT_VALUE = 'expect integer value' |
|
@ -79,36 +81,36 @@ _MSG_NO_SUCH_CONFIG = 'Config item does not exist' |
|
|
_MSG_BAD_SERVER_PARMS = 'Incorrect server settings' |
|
|
_MSG_BAD_SERVER_PARMS = 'Incorrect server settings' |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
# For Windows: determine executable extensions |
|
|
# For Windows: determine executable extensions |
|
|
if os.name == 'nt': |
|
|
if os.name == 'nt': |
|
|
PATHEXT = os.environ.get('PATHEXT', '').lower().split(';') |
|
|
PATHEXT = os.environ.get('PATHEXT', '').lower().split(';') |
|
|
else: |
|
|
else: |
|
|
PATHEXT = [] |
|
|
PATHEXT = [] |
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
def api_handler(kwargs): |
|
|
def api_handler(kwargs): |
|
|
""" API Dispatcher |
|
|
""" API Dispatcher """ |
|
|
""" |
|
|
|
|
|
mode = kwargs.get('mode', '') |
|
|
mode = kwargs.get('mode', '') |
|
|
output = kwargs.get('output') |
|
|
output = kwargs.get('output') |
|
|
name = kwargs.get('name', '') |
|
|
name = kwargs.get('name', '') |
|
|
callback = kwargs.get('callback', '') |
|
|
callback = kwargs.get('callback', '') |
|
|
|
|
|
|
|
|
if isinstance(mode, list): mode = mode[0] |
|
|
if isinstance(mode, list): |
|
|
if isinstance(output, list): output = output[0] |
|
|
mode = mode[0] |
|
|
|
|
|
if isinstance(output, list): |
|
|
|
|
|
output = output[0] |
|
|
response = _api_table.get(mode, (_api_undefined, 2))[0](name, output, kwargs) |
|
|
response = _api_table.get(mode, (_api_undefined, 2))[0](name, output, kwargs) |
|
|
if output == 'json' and callback: |
|
|
if output == 'json' and callback: |
|
|
response = '%s(%s)' % (callback, response) |
|
|
response = '%s(%s)' % (callback, response) |
|
|
return response |
|
|
return response |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
def _api_get_config(name, output, kwargs): |
|
|
def _api_get_config(name, output, kwargs): |
|
|
""" API: accepts output, keyword, section """ |
|
|
""" API: accepts output, keyword, section """ |
|
|
res, data = config.get_dconfig(kwargs.get('section'), kwargs.get('keyword')) |
|
|
res, data = config.get_dconfig(kwargs.get('section'), kwargs.get('keyword')) |
|
|
return report(output, keyword='config', data=data) |
|
|
return report(output, keyword='config', data=data) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _api_set_config(name, output, kwargs): |
|
|
def _api_set_config(name, output, kwargs): |
|
|
""" API: accepts output, keyword, section """ |
|
|
""" API: accepts output, keyword, section """ |
|
|
if kwargs.get('section') == 'servers': |
|
|
if kwargs.get('section') == 'servers': |
|
@ -145,7 +147,6 @@ def _api_qstatus(name, output, kwargs): |
|
|
return report(output, keyword=keyword, data=qstatus_data()) |
|
|
return report(output, keyword=keyword, data=qstatus_data()) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
def _api_queue(name, output, kwargs): |
|
|
def _api_queue(name, output, kwargs): |
|
|
""" API: Dispatcher for mode=queue """ |
|
|
""" API: Dispatcher for mode=queue """ |
|
|
value = kwargs.get('value', '') |
|
|
value = kwargs.get('value', '') |
|
@ -271,6 +272,7 @@ def _api_queue_default(output, value, kwargs): |
|
|
else: |
|
|
else: |
|
|
return report(output, _MSG_NOT_IMPLEMENTED) |
|
|
return report(output, _MSG_NOT_IMPLEMENTED) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _api_queue_rating(output, value, kwargs): |
|
|
def _api_queue_rating(output, value, kwargs): |
|
|
""" API: accepts output, value(=nzo_id), type, setting, detail """ |
|
|
""" API: accepts output, value(=nzo_id), type, setting, detail """ |
|
|
vote_map = {'up': Rating.VOTE_UP, 'down': Rating.VOTE_DOWN} |
|
|
vote_map = {'up': Rating.VOTE_UP, 'down': Rating.VOTE_DOWN} |
|
@ -291,7 +293,7 @@ def _api_queue_rating(output, value, kwargs): |
|
|
else: |
|
|
else: |
|
|
return report(output, _MSG_NO_VALUE) |
|
|
return report(output, _MSG_NO_VALUE) |
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
def _api_options(name, output, kwargs): |
|
|
def _api_options(name, output, kwargs): |
|
|
""" API: accepts output """ |
|
|
""" API: accepts output """ |
|
|
return options_list(output) |
|
|
return options_list(output) |
|
@ -378,10 +380,10 @@ def _api_addlocalfile(name, output, kwargs): |
|
|
nzbname = kwargs.get('nzbname') |
|
|
nzbname = kwargs.get('nzbname') |
|
|
|
|
|
|
|
|
if get_ext(name) in VALID_ARCHIVES: |
|
|
if get_ext(name) in VALID_ARCHIVES: |
|
|
res = sabnzbd.dirscanner.ProcessArchiveFile(\ |
|
|
res = sabnzbd.dirscanner.ProcessArchiveFile( |
|
|
fn, name, pp=pp, script=script, cat=cat, priority=priority, keep=True, nzbname=nzbname) |
|
|
fn, name, pp=pp, script=script, cat=cat, priority=priority, keep=True, nzbname=nzbname) |
|
|
elif get_ext(name) in ('.nzb', '.gz', '.bz2'): |
|
|
elif get_ext(name) in ('.nzb', '.gz', '.bz2'): |
|
|
res = sabnzbd.dirscanner.ProcessSingleFile(\ |
|
|
res = sabnzbd.dirscanner.ProcessSingleFile( |
|
|
fn, name, pp=pp, script=script, cat=cat, priority=priority, keep=True, nzbname=nzbname) |
|
|
fn, name, pp=pp, script=script, cat=cat, priority=priority, keep=True, nzbname=nzbname) |
|
|
else: |
|
|
else: |
|
|
logging.info('API-call addlocalfile: "%s" not a proper file name', name) |
|
|
logging.info('API-call addlocalfile: "%s" not a proper file name', name) |
|
@ -439,6 +441,7 @@ def _api_change_script(name, output, kwargs): |
|
|
else: |
|
|
else: |
|
|
return report(output, _MSG_NO_VALUE) |
|
|
return report(output, _MSG_NO_VALUE) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _api_change_opts(name, output, kwargs): |
|
|
def _api_change_opts(name, output, kwargs): |
|
|
""" API: accepts output, value(=nzo_id), value2(=pp) """ |
|
|
""" API: accepts output, value(=nzo_id), value2(=pp) """ |
|
|
value = kwargs.get('value') |
|
|
value = kwargs.get('value') |
|
@ -681,14 +684,17 @@ def _api_rss_now(name, output, kwargs): |
|
|
scheduler.force_rss() |
|
|
scheduler.force_rss() |
|
|
return report(output) |
|
|
return report(output) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _api_retry_all(name, output, kwargs): |
|
|
def _api_retry_all(name, output, kwargs): |
|
|
""" API: Retry all failed items in History """ |
|
|
""" API: Retry all failed items in History """ |
|
|
return report(output, keyword='status', data=retry_all_jobs) |
|
|
return report(output, keyword='status', data=retry_all_jobs) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _api_reset_quota(name, output, kwargs): |
|
|
def _api_reset_quota(name, output, kwargs): |
|
|
""" Reset quota left """ |
|
|
""" Reset quota left """ |
|
|
BPSMeter.do.reset_quota(force=True) |
|
|
BPSMeter.do.reset_quota(force=True) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _api_test_email(name, output, kwargs): |
|
|
def _api_test_email(name, output, kwargs): |
|
|
""" API: send a test email, return result """ |
|
|
""" API: send a test email, return result """ |
|
|
logging.info("Sending testmail") |
|
|
logging.info("Sending testmail") |
|
@ -703,48 +709,54 @@ def _api_test_email(name, output, kwargs): |
|
|
res = None |
|
|
res = None |
|
|
return report(output, error=res) |
|
|
return report(output, error=res) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _api_test_notif(name, output, kwargs): |
|
|
def _api_test_notif(name, output, kwargs): |
|
|
""" API: send a test to Notification Center, return result """ |
|
|
""" API: send a test to Notification Center, return result """ |
|
|
logging.info("Sending test notification") |
|
|
logging.info("Sending test notification") |
|
|
res = sabnzbd.growler.send_notification_center('SABnzbd', T('Test Notification'), 'other') |
|
|
res = sabnzbd.growler.send_notification_center('SABnzbd', T('Test Notification'), 'other') |
|
|
return report(output, error=res) |
|
|
return report(output, error=res) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _api_test_growl(name, output, kwargs): |
|
|
def _api_test_growl(name, output, kwargs): |
|
|
""" API: send a test Growl notification, return result """ |
|
|
""" API: send a test Growl notification, return result """ |
|
|
logging.info("Sending Growl notification") |
|
|
logging.info("Sending Growl notification") |
|
|
res = sabnzbd.growler.send_growl('SABnzbd', T('Test Notification'), 'other', test=kwargs) |
|
|
res = sabnzbd.growler.send_growl('SABnzbd', T('Test Notification'), 'other', test=kwargs) |
|
|
return report(output, error=res) |
|
|
return report(output, error=res) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _api_test_osd(name, output, kwargs): |
|
|
def _api_test_osd(name, output, kwargs): |
|
|
""" API: send a test OSD notification, return result """ |
|
|
""" API: send a test OSD notification, return result """ |
|
|
logging.info("Sending OSD notification") |
|
|
logging.info("Sending OSD notification") |
|
|
res = sabnzbd.growler.send_notify_osd('SABnzbd', T('Test Notification')) |
|
|
res = sabnzbd.growler.send_notify_osd('SABnzbd', T('Test Notification')) |
|
|
return report(output, error=res) |
|
|
return report(output, error=res) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _api_test_prowl(name, output, kwargs): |
|
|
def _api_test_prowl(name, output, kwargs): |
|
|
""" API: send a test Prowl notification, return result """ |
|
|
""" API: send a test Prowl notification, return result """ |
|
|
logging.info("Sending Prowl notification") |
|
|
logging.info("Sending Prowl notification") |
|
|
res = sabnzbd.growler.send_prowl('SABnzbd', T('Test Notification'), 'other', force=True, test=kwargs) |
|
|
res = sabnzbd.growler.send_prowl('SABnzbd', T('Test Notification'), 'other', force=True, test=kwargs) |
|
|
return report(output, error=res) |
|
|
return report(output, error=res) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _api_test_pushover(name, output, kwargs): |
|
|
def _api_test_pushover(name, output, kwargs): |
|
|
""" API: send a test Pushover notification, return result """ |
|
|
""" API: send a test Pushover notification, return result """ |
|
|
logging.info("Sending Pushover notification") |
|
|
logging.info("Sending Pushover notification") |
|
|
res = sabnzbd.growler.send_pushover('SABnzbd', T('Test Notification'), 'other', force=True, test=kwargs) |
|
|
res = sabnzbd.growler.send_pushover('SABnzbd', T('Test Notification'), 'other', force=True, test=kwargs) |
|
|
return report(output, error=res) |
|
|
return report(output, error=res) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _api_test_pushbullet(name, output, kwargs): |
|
|
def _api_test_pushbullet(name, output, kwargs): |
|
|
""" API: send a test Pushbullet notification, return result """ |
|
|
""" API: send a test Pushbullet notification, return result """ |
|
|
logging.info("Sending Pushbullet notification") |
|
|
logging.info("Sending Pushbullet notification") |
|
|
res = sabnzbd.growler.send_pushbullet('SABnzbd', T('Test Notification'), 'other', force=True, test=kwargs) |
|
|
res = sabnzbd.growler.send_pushbullet('SABnzbd', T('Test Notification'), 'other', force=True, test=kwargs) |
|
|
return report(output, error=res) |
|
|
return report(output, error=res) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _api_undefined(name, output, kwargs): |
|
|
def _api_undefined(name, output, kwargs): |
|
|
""" API: accepts output """ |
|
|
""" API: accepts output """ |
|
|
return report(output, _MSG_NOT_IMPLEMENTED) |
|
|
return report(output, _MSG_NOT_IMPLEMENTED) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
def _api_browse(name, output, kwargs): |
|
|
def _api_browse(name, output, kwargs): |
|
|
""" Return tree of local path """ |
|
|
""" Return tree of local path """ |
|
|
compact = kwargs.get('compact') |
|
|
compact = kwargs.get('compact') |
|
@ -759,7 +771,6 @@ def _api_browse(name, output, kwargs): |
|
|
return report(output, keyword='paths', data=paths) |
|
|
return report(output, keyword='paths', data=paths) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
def _api_config(name, output, kwargs): |
|
|
def _api_config(name, output, kwargs): |
|
|
""" API: Dispatcher for "config" """ |
|
|
""" API: Dispatcher for "config" """ |
|
|
return _api_config_table.get(name, (_api_config_undefined, 2))[0](output, kwargs) |
|
|
return _api_config_table.get(name, (_api_config_undefined, 2))[0](output, kwargs) |
|
@ -813,6 +824,7 @@ def _api_config_set_apikey(output, kwargs): |
|
|
config.save_config() |
|
|
config.save_config() |
|
|
return report(output, keyword='apikey', data=cfg.api_key()) |
|
|
return report(output, keyword='apikey', data=cfg.api_key()) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _api_config_set_nzbkey(output, kwargs): |
|
|
def _api_config_set_nzbkey(output, kwargs): |
|
|
""" API: accepts output """ |
|
|
""" API: accepts output """ |
|
|
cfg.nzb_key.set(config.create_api_key()) |
|
|
cfg.nzb_key.set(config.create_api_key()) |
|
@ -848,7 +860,7 @@ def _api_server_stats(name, output, kwargs): |
|
|
return report(output, keyword='', data=stats) |
|
|
return report(output, keyword='', data=stats) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
############################################################################## |
|
|
_api_table = { |
|
|
_api_table = { |
|
|
'server_stats': (_api_server_stats, 2), |
|
|
'server_stats': (_api_server_stats, 2), |
|
|
'get_config': (_api_get_config, 3), |
|
|
'get_config': (_api_get_config, 3), |
|
@ -926,7 +938,6 @@ _api_config_table = { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
def api_level(cmd, name): |
|
|
def api_level(cmd, name): |
|
|
""" Return access level required for this API call """ |
|
|
""" Return access level required for this API call """ |
|
|
if cmd in _api_table: |
|
|
if cmd in _api_table: |
|
@ -937,7 +948,7 @@ def api_level(cmd, name): |
|
|
return _api_config_table[cmd][1] |
|
|
return _api_config_table[cmd][1] |
|
|
return 4 |
|
|
return 4 |
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
def report(output, error=None, keyword='value', data=None, callback=None, compat=False): |
|
|
def report(output, error=None, keyword='value', data=None, callback=None, compat=False): |
|
|
""" Report message in json, xml or plain text |
|
|
""" Report message in json, xml or plain text |
|
|
If error is set, only an status/error report is made. |
|
|
If error is set, only an status/error report is made. |
|
@ -960,7 +971,6 @@ def report(output, error=None, keyword='value', data=None, callback=None, compat |
|
|
if callback: |
|
|
if callback: |
|
|
response = '%s(%s)' % (callback, response) |
|
|
response = '%s(%s)' % (callback, response) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
elif output == 'xml': |
|
|
elif output == 'xml': |
|
|
if not keyword: |
|
|
if not keyword: |
|
|
# xml always needs an outer keyword, even when json doesn't |
|
|
# xml always needs an outer keyword, even when json doesn't |
|
@ -996,14 +1006,13 @@ def report(output, error=None, keyword='value', data=None, callback=None, compat |
|
|
return response |
|
|
return response |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
class xml_factory(object): |
|
|
class xml_factory(object): |
|
|
""" |
|
|
""" Recursive xml string maker. Feed it a mixed tuple/dict/item object and will output into an xml string |
|
|
Recursive xml string maker. Feed it a mixed tuple/dict/item object and will output into an xml string |
|
|
|
|
|
Current limitations: |
|
|
Current limitations: |
|
|
In Two tiered lists hardcoded name of "item": <cat_list><item> </item></cat_list> |
|
|
In Two tiered lists hardcoded name of "item": <cat_list><item> </item></cat_list> |
|
|
In Three tiered lists hardcoded name of "slot": <tier1><slot><tier2> </tier2></slot></tier1> |
|
|
In Three tiered lists hardcoded name of "slot": <tier1><slot><tier2> </tier2></slot></tier1> |
|
|
""" |
|
|
""" |
|
|
|
|
|
|
|
|
def __init__(self): |
|
|
def __init__(self): |
|
|
self.__text = '' |
|
|
self.__text = '' |
|
|
|
|
|
|
|
@ -1055,10 +1064,8 @@ class xml_factory(object): |
|
|
return text |
|
|
return text |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
def handle_server_api(output, kwargs): |
|
|
def handle_server_api(output, kwargs): |
|
|
""" Special handler for API-call 'set_config' [servers] |
|
|
""" Special handler for API-call 'set_config' [servers] """ |
|
|
""" |
|
|
|
|
|
name = kwargs.get('keyword') |
|
|
name = kwargs.get('keyword') |
|
|
if not name: |
|
|
if not name: |
|
|
name = kwargs.get('name') |
|
|
name = kwargs.get('name') |
|
@ -1075,10 +1082,8 @@ def handle_server_api(output, kwargs): |
|
|
return name |
|
|
return name |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
def handle_rss_api(output, kwargs): |
|
|
def handle_rss_api(output, kwargs): |
|
|
""" Special handler for API-call 'set_config' [rss] |
|
|
""" Special handler for API-call 'set_config' [rss] """ |
|
|
""" |
|
|
|
|
|
name = kwargs.get('keyword') |
|
|
name = kwargs.get('keyword') |
|
|
if not name: |
|
|
if not name: |
|
|
name = kwargs.get('name') |
|
|
name = kwargs.get('name') |
|
@ -1093,10 +1098,8 @@ def handle_rss_api(output, kwargs): |
|
|
return name |
|
|
return name |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
def handle_cat_api(output, kwargs): |
|
|
def handle_cat_api(output, kwargs): |
|
|
""" Special handler for API-call 'set_config' [categories] |
|
|
""" Special handler for API-call 'set_config' [categories] """ |
|
|
""" |
|
|
|
|
|
name = kwargs.get('keyword') |
|
|
name = kwargs.get('keyword') |
|
|
if not name: |
|
|
if not name: |
|
|
name = kwargs.get('name') |
|
|
name = kwargs.get('name') |
|
@ -1111,7 +1114,6 @@ def handle_cat_api(output, kwargs): |
|
|
return name |
|
|
return name |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
def build_queue(web_dir=None, root=None, verbose=False, prim=True, webdir='', verbose_list=None, |
|
|
def build_queue(web_dir=None, root=None, verbose=False, prim=True, webdir='', verbose_list=None, |
|
|
dictionary=None, history=False, start=None, limit=None, dummy2=None, trans=False, output=None, |
|
|
dictionary=None, history=False, start=None, limit=None, dummy2=None, trans=False, output=None, |
|
|
search=None): |
|
|
search=None): |
|
@ -1270,7 +1272,8 @@ def build_queue(web_dir=None, root=None, verbose=False, prim=True, webdir='', ve |
|
|
finished = [] |
|
|
finished = [] |
|
|
active = [] |
|
|
active = [] |
|
|
queued = [] |
|
|
queued = [] |
|
|
if verbose or nzo_id in verbose_list:#this will list files in the xml output, wanted yes/no? |
|
|
# this will list files in the xml output, wanted yes/no? |
|
|
|
|
|
if verbose or nzo_id in verbose_list: |
|
|
slot['verbosity'] = "True" |
|
|
slot['verbosity'] = "True" |
|
|
for tup in finished_files: |
|
|
for tup in finished_files: |
|
|
bytes_left, bytes, fn, date = tup |
|
|
bytes_left, bytes, fn, date = tup |
|
@ -1350,7 +1353,6 @@ def build_queue(web_dir=None, root=None, verbose=False, prim=True, webdir='', ve |
|
|
return info, pnfo_list, bytespersec, verbose_list, dictn |
|
|
return info, pnfo_list, bytespersec, verbose_list, dictn |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
def fast_queue(): |
|
|
def fast_queue(): |
|
|
""" Return paused, bytes_left, bpsnow, time_left """ |
|
|
""" Return paused, bytes_left, bpsnow, time_left """ |
|
|
bytes_left = NzbQueue.do.remaining() |
|
|
bytes_left = NzbQueue.do.remaining() |
|
@ -1360,10 +1362,8 @@ def fast_queue(): |
|
|
return paused, bytes_left, bpsnow, time_left |
|
|
return paused, bytes_left, bpsnow, time_left |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
def qstatus_data(): |
|
|
def qstatus_data(): |
|
|
"""Build up the queue status as a nested object and output as a JSON object |
|
|
"""Build up the queue status as a nested object and output as a JSON object """ |
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
qnfo = NzbQueue.do.queue_info() |
|
|
qnfo = NzbQueue.do.queue_info() |
|
|
pnfo_list = qnfo[QNFO_PNFO_LIST_FIELD] |
|
|
pnfo_list = qnfo[QNFO_PNFO_LIST_FIELD] |
|
@ -1409,7 +1409,6 @@ def qstatus_data(): |
|
|
return status |
|
|
return status |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
def build_file_list(id): |
|
|
def build_file_list(id): |
|
|
qnfo = NzbQueue.do.queue_info() |
|
|
qnfo = NzbQueue.do.queue_info() |
|
|
pnfo_list = qnfo[QNFO_PNFO_LIST_FIELD] |
|
|
pnfo_list = qnfo[QNFO_PNFO_LIST_FIELD] |
|
@ -1422,7 +1421,6 @@ def build_file_list(id): |
|
|
active_files = pnfo[PNFO_ACTIVE_FILES_FIELD] |
|
|
active_files = pnfo[PNFO_ACTIVE_FILES_FIELD] |
|
|
queued_files = pnfo[PNFO_QUEUED_FILES_FIELD] |
|
|
queued_files = pnfo[PNFO_QUEUED_FILES_FIELD] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
n = 0 |
|
|
n = 0 |
|
|
for tup in finished_files: |
|
|
for tup in finished_files: |
|
|
bytes_left, bytes, fn, date = tup |
|
|
bytes_left, bytes, fn, date = tup |
|
@ -1471,17 +1469,15 @@ def build_file_list(id): |
|
|
return jobs |
|
|
return jobs |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
def rss_qstatus(): |
|
|
def rss_qstatus(): |
|
|
""" Return a RSS feed with the queue status |
|
|
""" Return a RSS feed with the queue status """ |
|
|
""" |
|
|
|
|
|
qnfo = NzbQueue.do.queue_info() |
|
|
qnfo = NzbQueue.do.queue_info() |
|
|
pnfo_list = qnfo[QNFO_PNFO_LIST_FIELD] |
|
|
pnfo_list = qnfo[QNFO_PNFO_LIST_FIELD] |
|
|
|
|
|
|
|
|
rss = RSS() |
|
|
rss = RSS() |
|
|
rss.channel.title = "SABnzbd Queue" |
|
|
rss.channel.title = "SABnzbd Queue" |
|
|
rss.channel.description = "Overview of current downloads" |
|
|
rss.channel.description = "Overview of current downloads" |
|
|
rss.channel.link = "http://%s:%s/sabnzbd/queue" % ( \ |
|
|
rss.channel.link = "http://%s:%s/sabnzbd/queue" % ( |
|
|
cfg.cherryhost(), cfg.cherryport()) |
|
|
cfg.cherryhost(), cfg.cherryport()) |
|
|
rss.channel.language = "en" |
|
|
rss.channel.language = "en" |
|
|
|
|
|
|
|
@ -1503,7 +1499,6 @@ def rss_qstatus(): |
|
|
mb = (bytes / MEBI) |
|
|
mb = (bytes / MEBI) |
|
|
nzo_id = pnfo[PNFO_NZO_ID_FIELD] |
|
|
nzo_id = pnfo[PNFO_NZO_ID_FIELD] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if mb == mbleft: |
|
|
if mb == mbleft: |
|
|
percentage = "0%" |
|
|
percentage = "0%" |
|
|
else: |
|
|
else: |
|
@ -1534,10 +1529,8 @@ def rss_qstatus(): |
|
|
return rss.write() |
|
|
return rss.write() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
def options_list(output): |
|
|
def options_list(output): |
|
|
return report(output, keyword='options', data= |
|
|
return report(output, keyword='options', data={ |
|
|
{ |
|
|
|
|
|
'yenc': sabnzbd.decoder.HAVE_YENC, |
|
|
'yenc': sabnzbd.decoder.HAVE_YENC, |
|
|
'par2': sabnzbd.newsunpack.PAR2_COMMAND, |
|
|
'par2': sabnzbd.newsunpack.PAR2_COMMAND, |
|
|
'par2c': sabnzbd.newsunpack.PAR2C_COMMAND, |
|
|
'par2c': sabnzbd.newsunpack.PAR2C_COMMAND, |
|
@ -1550,8 +1543,6 @@ def options_list(output): |
|
|
}) |
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
def retry_job(job, new_nzb, password): |
|
|
def retry_job(job, new_nzb, password): |
|
|
""" Re enter failed job in the download queue """ |
|
|
""" Re enter failed job in the download queue """ |
|
|
if job: |
|
|
if job: |
|
@ -1569,14 +1560,12 @@ def retry_job(job, new_nzb, password): |
|
|
return None |
|
|
return None |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
def retry_all_jobs(): |
|
|
def retry_all_jobs(): |
|
|
""" Re enter all failed jobs in the download queue """ |
|
|
""" Re enter all failed jobs in the download queue """ |
|
|
history_db = cherrypy.thread_data.history_db |
|
|
history_db = cherrypy.thread_data.history_db |
|
|
return NzbQueue.do.retry_all_jobs(history_db) |
|
|
return NzbQueue.do.retry_all_jobs(history_db) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
def del_job_files(job_paths): |
|
|
def del_job_files(job_paths): |
|
|
""" Remove files of each path in the list """ |
|
|
""" Remove files of each path in the list """ |
|
|
for path in job_paths: |
|
|
for path in job_paths: |
|
@ -1584,7 +1573,6 @@ def del_job_files(job_paths): |
|
|
remove_all(path, recursive=True) |
|
|
remove_all(path, recursive=True) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
def del_hist_job(job, del_files): |
|
|
def del_hist_job(job, del_files): |
|
|
""" Remove history element """ |
|
|
""" Remove history element """ |
|
|
if job: |
|
|
if job: |
|
@ -1601,7 +1589,7 @@ def del_hist_job(job, del_files): |
|
|
remove_all(path, recursive=True) |
|
|
remove_all(path, recursive=True) |
|
|
return True |
|
|
return True |
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
def Tspec(txt): |
|
|
def Tspec(txt): |
|
|
""" Translate special terms """ |
|
|
""" Translate special terms """ |
|
|
if txt == 'None': |
|
|
if txt == 'None': |
|
@ -1612,12 +1600,10 @@ def Tspec(txt): |
|
|
return txt |
|
|
return txt |
|
|
|
|
|
|
|
|
_SKIN_CACHE = {} # Stores pre-translated acronyms |
|
|
_SKIN_CACHE = {} # Stores pre-translated acronyms |
|
|
|
|
|
|
|
|
# This special is to be used in interface.py for template processing |
|
|
# This special is to be used in interface.py for template processing |
|
|
# to be passed for the $T function: so { ..., 'T' : Ttemplate, ...} |
|
|
# to be passed for the $T function: so { ..., 'T' : Ttemplate, ...} |
|
|
def Ttemplate(txt): |
|
|
def Ttemplate(txt): |
|
|
""" Translation function for Skin texts |
|
|
""" Translation function for Skin texts """ |
|
|
""" |
|
|
|
|
|
global _SKIN_CACHE |
|
|
global _SKIN_CACHE |
|
|
if txt in _SKIN_CACHE: |
|
|
if txt in _SKIN_CACHE: |
|
|
return _SKIN_CACHE[txt] |
|
|
return _SKIN_CACHE[txt] |
|
@ -1628,8 +1614,7 @@ def Ttemplate(txt): |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def clear_trans_cache(): |
|
|
def clear_trans_cache(): |
|
|
""" Clean cache for skin translations |
|
|
""" Clean cache for skin translations """ |
|
|
""" |
|
|
|
|
|
global _SKIN_CACHE |
|
|
global _SKIN_CACHE |
|
|
dummy = _SKIN_CACHE |
|
|
dummy = _SKIN_CACHE |
|
|
_SKIN_CACHE = {} |
|
|
_SKIN_CACHE = {} |
|
@ -1677,7 +1662,6 @@ def build_header(prim, webdir='', search=None): |
|
|
header['my_lcldata'] = sabnzbd.DIR_LCLDATA |
|
|
header['my_lcldata'] = sabnzbd.DIR_LCLDATA |
|
|
header['my_home'] = sabnzbd.DIR_HOME |
|
|
header['my_home'] = sabnzbd.DIR_HOME |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
header['webdir'] = webdir |
|
|
header['webdir'] = webdir |
|
|
header['pid'] = os.getpid() |
|
|
header['pid'] = os.getpid() |
|
|
|
|
|
|
|
@ -1741,8 +1725,6 @@ def build_header(prim, webdir='', search=None): |
|
|
return (header, qnfo[QNFO_PNFO_LIST_FIELD], bytespersec) |
|
|
return (header, qnfo[QNFO_PNFO_LIST_FIELD], bytespersec) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
|
|
|
|
|
|
def build_history(start=None, limit=None, verbose=False, verbose_list=None, search=None, failed_only=0, |
|
|
def build_history(start=None, limit=None, verbose=False, verbose_list=None, search=None, failed_only=0, |
|
|
categories=None, output=None): |
|
|
categories=None, output=None): |
|
|
|
|
|
|
|
@ -1755,7 +1737,8 @@ def build_history(start=None, limit=None, verbose=False, verbose_list=None, sear |
|
|
verbose_list = [] |
|
|
verbose_list = [] |
|
|
|
|
|
|
|
|
limit = int_conv(limit) |
|
|
limit = int_conv(limit) |
|
|
if not limit: limit = 1000000 |
|
|
if not limit: |
|
|
|
|
|
limit = 1000000 |
|
|
start = int_conv(start) |
|
|
start = int_conv(start) |
|
|
failed_only = int_conv(failed_only) |
|
|
failed_only = int_conv(failed_only) |
|
|
|
|
|
|
|
@ -1864,12 +1847,12 @@ def build_history(start=None, limit=None, verbose=False, verbose_list=None, sear |
|
|
if item.get('status') != 'Failed': |
|
|
if item.get('status') != 'Failed': |
|
|
item['path'] = '' |
|
|
item['path'] = '' |
|
|
|
|
|
|
|
|
item['retry'] = int(bool(item.get('status') == 'Failed' and \ |
|
|
item['retry'] = int(bool(item.get('status') == 'Failed' and |
|
|
path and \ |
|
|
path and |
|
|
path not in retry_folders and \ |
|
|
path not in retry_folders and |
|
|
starts_with_path(path, cfg.download_dir.get_path()) and \ |
|
|
starts_with_path(path, cfg.download_dir.get_path()) and |
|
|
os.path.exists(path)) and \ |
|
|
os.path.exists(path)) and |
|
|
not bool(globber(os.path.join(path, JOB_ADMIN), 'SABnzbd_n*')) \ |
|
|
not bool(globber(os.path.join(path, JOB_ADMIN), 'SABnzbd_n*')) |
|
|
) |
|
|
) |
|
|
if item['report'] == 'future': |
|
|
if item['report'] == 'future': |
|
|
item['retry'] = True |
|
|
item['retry'] = True |
|
@ -1900,10 +1883,8 @@ def build_history(start=None, limit=None, verbose=False, verbose_list=None, sear |
|
|
return (items, fetched_items, total_items) |
|
|
return (items, fetched_items, total_items) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
def format_history_for_queue(): |
|
|
def format_history_for_queue(): |
|
|
''' Retrieves the information on currently active history items, and formats them for displaying in the queue ''' |
|
|
""" Retrieves the information on currently active history items, and formats them for displaying in the queue """ |
|
|
slotinfo = [] |
|
|
slotinfo = [] |
|
|
history_items = get_active_history() |
|
|
history_items = get_active_history() |
|
|
|
|
|
|
|
@ -1918,7 +1899,7 @@ def format_history_for_queue(): |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_active_history(queue=None, items=None): |
|
|
def get_active_history(queue=None, items=None): |
|
|
# Get the currently in progress and active history queue. |
|
|
""" Get the currently in progress and active history queue. """ |
|
|
if items is None: |
|
|
if items is None: |
|
|
items = [] |
|
|
items = [] |
|
|
if queue is None: |
|
|
if queue is None: |
|
@ -1951,7 +1932,6 @@ def get_active_history(queue=None, items=None): |
|
|
return items |
|
|
return items |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
def format_bytes(bytes): |
|
|
def format_bytes(bytes): |
|
|
b = to_units(bytes) |
|
|
b = to_units(bytes) |
|
|
if b == '': |
|
|
if b == '': |
|
@ -1961,9 +1941,7 @@ def format_bytes(bytes): |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def calc_timeleft(bytesleft, bps): |
|
|
def calc_timeleft(bytesleft, bps): |
|
|
""" |
|
|
""" Calculate the time left in the format HH:MM:SS """ |
|
|
Calculate the time left in the format HH:MM:SS |
|
|
|
|
|
""" |
|
|
|
|
|
try: |
|
|
try: |
|
|
totalseconds = int(bytesleft / bps) |
|
|
totalseconds = int(bytesleft / bps) |
|
|
minutes, seconds = divmod(totalseconds, 60) |
|
|
minutes, seconds = divmod(totalseconds, 60) |
|
@ -1978,8 +1956,7 @@ def calc_timeleft(bytesleft, bps): |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def calc_age(date, trans=False): |
|
|
def calc_age(date, trans=False): |
|
|
""" |
|
|
""" Calculate the age difference between now and date. |
|
|
Calculate the age difference between now and date. |
|
|
|
|
|
Value is returned as either days, hours, or minutes. |
|
|
Value is returned as either days, hours, or minutes. |
|
|
When 'trans' is True, time symbols will be translated. |
|
|
When 'trans' is True, time symbols will be translated. |
|
|
""" |
|
|
""" |
|
@ -2026,7 +2003,7 @@ def list_scripts(default=False): |
|
|
if path and os.access(path, os.R_OK): |
|
|
if path and os.access(path, os.R_OK): |
|
|
for script in globber_full(path): |
|
|
for script in globber_full(path): |
|
|
if os.path.isfile(script): |
|
|
if os.path.isfile(script): |
|
|
if (sabnzbd.WIN32 and os.path.splitext(script)[1].lower() in PATHEXT and \ |
|
|
if (sabnzbd.WIN32 and os.path.splitext(script)[1].lower() in PATHEXT and |
|
|
not (win32api.GetFileAttributes(script) & win32file.FILE_ATTRIBUTE_HIDDEN)) or \ |
|
|
not (win32api.GetFileAttributes(script) & win32file.FILE_ATTRIBUTE_HIDDEN)) or \ |
|
|
script.endswith('.py') or \ |
|
|
script.endswith('.py') or \ |
|
|
(not sabnzbd.WIN32 and os.access(script, os.X_OK) and not os.path.basename(script).startswith('.')): |
|
|
(not sabnzbd.WIN32 and os.access(script, os.X_OK) and not os.path.basename(script).startswith('.')): |
|
@ -2054,7 +2031,6 @@ def remove_callable(dic): |
|
|
return dic |
|
|
return dic |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
_PLURAL_TO_SINGLE = { |
|
|
_PLURAL_TO_SINGLE = { |
|
|
'categories': 'category', |
|
|
'categories': 'category', |
|
|
'servers': 'server', |
|
|
'servers': 'server', |
|
@ -2064,6 +2040,8 @@ _PLURAL_TO_SINGLE = { |
|
|
'files': 'file', |
|
|
'files': 'file', |
|
|
'jobs': 'job' |
|
|
'jobs': 'job' |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def plural_to_single(kw, def_kw=''): |
|
|
def plural_to_single(kw, def_kw=''): |
|
|
try: |
|
|
try: |
|
|
return _PLURAL_TO_SINGLE[kw] |
|
|
return _PLURAL_TO_SINGLE[kw] |
|
@ -2071,7 +2049,6 @@ def plural_to_single(kw, def_kw=''): |
|
|
return def_kw |
|
|
return def_kw |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
def del_from_section(kwargs): |
|
|
def del_from_section(kwargs): |
|
|
""" Remove keyword in section """ |
|
|
""" Remove keyword in section """ |
|
|
section = kwargs.get('section', '') |
|
|
section = kwargs.get('section', '') |
|
@ -2090,7 +2067,6 @@ def del_from_section(kwargs): |
|
|
return False |
|
|
return False |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
|
|
def history_remove_failed(): |
|
|
def history_remove_failed(): |
|
|
""" Remove all failed jobs from history, including files """ |
|
|
""" Remove all failed jobs from history, including files """ |
|
|
logging.info('Scheduled removal of all failed jobs') |
|
|
logging.info('Scheduled removal of all failed jobs') |
|
|