You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2540 lines
83 KiB
2540 lines
83 KiB
#!/usr/bin/python -OO
|
|
# Copyright 2008-2011 The SABnzbd-Team <team@sabnzbd.org>
|
|
#
|
|
# 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.
|
|
|
|
"""
|
|
sabnzbd.interface - webinterface
|
|
"""
|
|
|
|
import os
|
|
import time
|
|
import cherrypy
|
|
import logging
|
|
import re
|
|
import urllib
|
|
from xml.sax.saxutils import escape
|
|
|
|
from sabnzbd.utils.rsslib import RSS, Item
|
|
import sabnzbd
|
|
import sabnzbd.rss
|
|
import sabnzbd.scheduler as scheduler
|
|
|
|
from Cheetah.Template import Template
|
|
import sabnzbd.emailer as emailer
|
|
from sabnzbd.misc import real_path, to_units, \
|
|
diskfree, sanitize_foldername, time_format, HAVE_AMPM, \
|
|
cat_to_opts, int_conv, panic_old_queue, globber, clean_folder
|
|
from sabnzbd.newswrapper import GetServerParms
|
|
from sabnzbd.newzbin import Bookmarks
|
|
from sabnzbd.bpsmeter import BPSMeter
|
|
from sabnzbd.encoding import TRANS, xml_name, LatinFilter, unicoder, special_fixer, platform_encode, latin1
|
|
import sabnzbd.config as config
|
|
import sabnzbd.cfg as cfg
|
|
import sabnzbd.newsunpack
|
|
from sabnzbd.postproc import PostProcessor
|
|
from sabnzbd.downloader import Downloader
|
|
from sabnzbd.nzbqueue import NzbQueue
|
|
import sabnzbd.wizard
|
|
from sabnzbd.utils.servertests import test_nntp_server_dict
|
|
|
|
from sabnzbd.constants import *
|
|
from sabnzbd.lang import list_languages, set_language
|
|
|
|
from sabnzbd.api import list_scripts, list_cats, del_from_section, \
|
|
api_handler, build_queue, rss_qstatus, \
|
|
retry_job, build_header, get_history_size, build_history, \
|
|
format_bytes, calc_age, std_time, report, del_hist_job, Ttemplate
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Global constants
|
|
|
|
DIRECTIVES = {
|
|
'directiveStartToken': '<!--#',
|
|
'directiveEndToken': '#-->',
|
|
'prioritizeSearchListOverSelf' : True
|
|
}
|
|
FILTER = LatinFilter
|
|
|
|
#------------------------------------------------------------------------------
|
|
#
|
|
def check_server(host, port):
|
|
""" Check if server address resolves properly """
|
|
|
|
if host.lower() == 'localhost' and sabnzbd.AMBI_LOCALHOST:
|
|
return badParameterResponse(T('Warning: LOCALHOST is ambiguous, use numerical IP-address.'))
|
|
|
|
if GetServerParms(host, int_conv(port)):
|
|
return ""
|
|
else:
|
|
return badParameterResponse(T('Server address "%s:%s" is not valid.') % (host, port))
|
|
|
|
|
|
def ConvertSpecials(p):
|
|
""" Convert None to 'None' and 'Default' to ''
|
|
"""
|
|
if p is None:
|
|
p = 'None'
|
|
elif p.lower() == T('Default').lower():
|
|
p = ''
|
|
return p
|
|
|
|
|
|
def Raiser(root, **kwargs):
|
|
args = {}
|
|
for key in kwargs:
|
|
val = kwargs.get(key)
|
|
if val:
|
|
args[key] = val
|
|
root = '%s?%s' % (root, urllib.urlencode(args))
|
|
return cherrypy.HTTPRedirect(root)
|
|
|
|
|
|
def queueRaiser(root, kwargs):
|
|
return Raiser(root, start=kwargs.get('start'),
|
|
limit=kwargs.get('limit'),
|
|
search=kwargs.get('search'),
|
|
_dc=kwargs.get('_dc'))
|
|
|
|
def dcRaiser(root, kwargs):
|
|
return Raiser(root, _dc=kwargs.get('_dc'))
|
|
|
|
def rssRaiser(root, kwargs):
|
|
return Raiser(root, feed=kwargs.get('feed'))
|
|
|
|
#------------------------------------------------------------------------------
|
|
def IsNone(value):
|
|
""" Return True if either None, 'None' or '' """
|
|
return value==None or value=="" or value.lower()=='none'
|
|
|
|
|
|
def Strip(txt):
|
|
""" Return stripped string, can handle None """
|
|
try:
|
|
return txt.strip()
|
|
except:
|
|
return None
|
|
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Web login support
|
|
def get_users():
|
|
users = {}
|
|
users[cfg.username()] = cfg.password()
|
|
return users
|
|
|
|
def encrypt_pwd(pwd):
|
|
return pwd
|
|
|
|
|
|
def set_auth(conf):
|
|
""" Set the authentication for CherryPy
|
|
"""
|
|
if cfg.username() and cfg.password():
|
|
conf.update({'tools.basic_auth.on' : True, 'tools.basic_auth.realm' : cfg.login_realm(),
|
|
'tools.basic_auth.users' : get_users, 'tools.basic_auth.encrypt' : encrypt_pwd})
|
|
conf.update({'/api':{'tools.basic_auth.on' : False},
|
|
'/m/api':{'tools.basic_auth.on' : False},
|
|
'/sabnzbd/api':{'tools.basic_auth.on' : False},
|
|
'/sabnzbd/m/api':{'tools.basic_auth.on' : False},
|
|
})
|
|
else:
|
|
conf.update({'tools.basic_auth.on':False})
|
|
|
|
|
|
def check_session(kwargs):
|
|
""" Check session key """
|
|
key = kwargs.get('session')
|
|
if not key:
|
|
key = kwargs.get('apikey')
|
|
msg = None
|
|
if not key:
|
|
logging.warning(Ta('Missing Session key'))
|
|
msg = T('Error: Session Key Required')
|
|
elif key != cfg.api_key():
|
|
logging.warning(Ta('Error: Session Key Incorrect'))
|
|
msg = T('Error: Session Key Incorrect')
|
|
return msg
|
|
|
|
|
|
#------------------------------------------------------------------------------
|
|
def check_apikey(kwargs, nokey=False):
|
|
""" Check api key """
|
|
output = kwargs.get('output')
|
|
mode = kwargs.get('mode', '')
|
|
|
|
# Don't give a visible warning: these commands are used by some
|
|
# external utilities to detect if username/password is required
|
|
# The cfg item can suppress all visible warnings
|
|
special = mode in ('get_scripts', 'qstatus') or not cfg.api_warnings.get()
|
|
|
|
# First check APIKEY, if OK that's sufficient
|
|
if not (cfg.disable_key() or nokey):
|
|
key = kwargs.get('apikey')
|
|
if not key:
|
|
if not special:
|
|
logging.warning(Ta('API Key missing, please enter the api key from Config->General into your 3rd party program:'))
|
|
return report(output, 'API Key Required')
|
|
elif key != cfg.api_key():
|
|
logging.warning(Ta('API Key incorrect, Use the api key from Config->General in your 3rd party program:'))
|
|
return report(output, 'API Key Incorrect')
|
|
else:
|
|
return None
|
|
|
|
# No active APIKEY, check web credentials instead
|
|
if cfg.username() and cfg.password():
|
|
if kwargs.get('ma_username') == cfg.username() and kwargs.get('ma_password') == cfg.password():
|
|
pass
|
|
else:
|
|
if not special:
|
|
logging.warning(Ta('Authentication missing, please enter username/password from Config->General into your 3rd party program:'))
|
|
return report(output, 'Missing authentication')
|
|
return None
|
|
|
|
|
|
#------------------------------------------------------------------------------
|
|
class NoPage(object):
|
|
def __init__(self):
|
|
pass
|
|
|
|
@cherrypy.expose
|
|
def index(self, **kwargs):
|
|
return badParameterResponse(T('Error: No secondary interface defined.'))
|
|
|
|
|
|
|
|
class MainPage(object):
|
|
def __init__(self, web_dir, root, web_dir2=None, root2=None, prim=True, first=0):
|
|
self.__root = root
|
|
self.__web_dir = web_dir
|
|
self.__prim = prim
|
|
if first >= 1:
|
|
self.m = MainPage(web_dir2, root2, prim=False)
|
|
if first == 2:
|
|
self.sabnzbd = MainPage(web_dir, '/sabnzbd/', web_dir2, '/sabnzbd/m/', prim=True, first=1)
|
|
self.queue = QueuePage(web_dir, root+'queue/', prim)
|
|
self.history = HistoryPage(web_dir, root+'history/', prim)
|
|
self.connections = ConnectionInfo(web_dir, root+'connections/', prim)
|
|
self.config = ConfigPage(web_dir, root+'config/', prim)
|
|
self.nzb = NzoPage(web_dir, root+'nzb/', prim)
|
|
self.wizard = sabnzbd.wizard.Wizard(web_dir, root+'wizard/', prim)
|
|
|
|
|
|
@cherrypy.expose
|
|
def index(self, **kwargs):
|
|
if sabnzbd.OLD_QUEUE and not cfg.warned_old_queue():
|
|
cfg.warned_old_queue.set(True)
|
|
config.save_config()
|
|
return panic_old_queue()
|
|
|
|
if kwargs.get('skip_wizard') or config.get_servers():
|
|
info, pnfo_list, bytespersec = build_header(self.__prim)
|
|
|
|
if cfg.newzbin_username() and cfg.newzbin_password.get_stars():
|
|
info['newzbinDetails'] = True
|
|
|
|
info['script_list'] = list_scripts(default=True)
|
|
info['script'] = 'Default'
|
|
|
|
info['cat'] = 'Default'
|
|
info['cat_list'] = list_cats(True)
|
|
|
|
info['warning'] = ''
|
|
if cfg.enable_unrar():
|
|
if sabnzbd.newsunpack.RAR_PROBLEM and not cfg.ignore_wrong_unrar():
|
|
info['warning'] = T('Your UNRAR version is not recommended, get it from http://www.rarlab.com/rar_add.htm<br />')
|
|
if not sabnzbd.newsunpack.RAR_COMMAND:
|
|
info['warning'] = T('No UNRAR program found, unpacking RAR files is not possible<br />')
|
|
if not sabnzbd.newsunpack.PAR2_COMMAND:
|
|
info['warning'] = T('No PAR2 program found, repairs not possible<br />')
|
|
|
|
template = Template(file=os.path.join(self.__web_dir, 'main.tmpl'),
|
|
filter=FILTER, searchList=[info], compilerSettings=DIRECTIVES)
|
|
return template.respond()
|
|
else:
|
|
# Redirect to the setup wizard
|
|
raise cherrypy.HTTPRedirect('/wizard/')
|
|
|
|
#@cherrypy.expose
|
|
#def reset_lang(self, **kwargs):
|
|
# msg = check_session(kwargs)
|
|
# if msg: return msg
|
|
# set_language(cfg.language())
|
|
# raise dcRaiser(self.__root, kwargs)
|
|
|
|
|
|
def add_handler(self, kwargs):
|
|
id = kwargs.get('id', '')
|
|
if not id:
|
|
id = kwargs.get('url', '')
|
|
pp = kwargs.get('pp')
|
|
script = kwargs.get('script')
|
|
cat = kwargs.get('cat')
|
|
priority = kwargs.get('priority')
|
|
redirect = kwargs.get('redirect')
|
|
nzbname = kwargs.get('nzbname')
|
|
|
|
RE_NEWZBIN_URL = re.compile(r'/browse/post/(\d+)')
|
|
newzbin_url = RE_NEWZBIN_URL.search(id.lower())
|
|
|
|
id = Strip(id)
|
|
if id and (id.isdigit() or len(id)==5):
|
|
sabnzbd.add_msgid(id, pp, script, cat, priority, nzbname)
|
|
elif newzbin_url:
|
|
sabnzbd.add_msgid(Strip(newzbin_url.group(1)), pp, script, cat, priority, nzbname)
|
|
elif id:
|
|
sabnzbd.add_url(id, pp, script, cat, priority, nzbname)
|
|
if not redirect:
|
|
redirect = self.__root
|
|
raise cherrypy.HTTPRedirect(redirect)
|
|
|
|
|
|
@cherrypy.expose
|
|
def addID(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
raise self.add_handler(kwargs)
|
|
|
|
|
|
@cherrypy.expose
|
|
def addURL(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
raise self.add_handler(kwargs)
|
|
|
|
|
|
@cherrypy.expose
|
|
def addFile(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
|
|
nzbfile = kwargs.get('nzbfile')
|
|
if nzbfile is not None and nzbfile.filename and nzbfile.value:
|
|
sabnzbd.add_nzbfile(nzbfile, kwargs.get('pp'), kwargs.get('script'),
|
|
kwargs.get('cat'), kwargs.get('priority', NORMAL_PRIORITY))
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def shutdown(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg:
|
|
yield msg
|
|
else:
|
|
yield "Initiating shutdown..."
|
|
sabnzbd.halt()
|
|
yield "<br>SABnzbd-%s shutdown finished" % sabnzbd.__version__
|
|
cherrypy.engine.exit()
|
|
sabnzbd.SABSTOP = True
|
|
|
|
@cherrypy.expose
|
|
def pause(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
|
|
scheduler.plan_resume(0)
|
|
Downloader.do.pause()
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def resume(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
|
|
scheduler.plan_resume(0)
|
|
sabnzbd.unpause_all()
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def rss(self, **kwargs):
|
|
msg = check_apikey(kwargs, nokey=True)
|
|
if msg: return msg
|
|
|
|
if kwargs.get('mode') == 'history':
|
|
return rss_history(cherrypy.url(), limit=kwargs.get('limit',50), search=kwargs.get('search'))
|
|
elif kwargs.get('mode') == 'queue':
|
|
return rss_qstatus()
|
|
elif kwargs.get('mode') == 'warnings':
|
|
return rss_warnings()
|
|
|
|
@cherrypy.expose
|
|
def tapi(self, **kwargs):
|
|
"""Handler for API over http, for template use
|
|
"""
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
return api_handler(kwargs)
|
|
|
|
@cherrypy.expose
|
|
def api(self, **kwargs):
|
|
"""Handler for API over http, with explicit authentication parameters
|
|
"""
|
|
if kwargs.get('mode', '') not in ('version', 'auth'):
|
|
msg = check_apikey(kwargs)
|
|
if msg: return msg
|
|
return api_handler(kwargs)
|
|
|
|
@cherrypy.expose
|
|
def scriptlog(self, **kwargs):
|
|
""" Duplicate of scriptlog of History, needed for some skins """
|
|
# No session key check, due to fixed URLs
|
|
|
|
name = kwargs.get('name')
|
|
if name:
|
|
history_db = cherrypy.thread_data.history_db
|
|
return ShowString(history_db.get_name(name), history_db.get_script_log(name))
|
|
else:
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def retry(self, **kwargs):
|
|
""" Duplicate of retry of History, needed for some skins """
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
|
|
url = kwargs.get('url', '')
|
|
pp = kwargs.get('pp')
|
|
cat = kwargs.get('cat')
|
|
script = kwargs.get('script')
|
|
|
|
url = url.strip()
|
|
if url and (url.isdigit() or len(url)==5):
|
|
sabnzbd.add_msgid(url, pp, script, cat)
|
|
elif url:
|
|
sabnzbd.add_url(url, pp, script, cat)
|
|
if url:
|
|
return ShowOK(url)
|
|
else:
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def retry_pp(self, **kwargs):
|
|
# Duplicate of History/retry_pp to please the SMPL skin :(
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
retry_job(kwargs.get('job'), kwargs.get('nzbfile'))
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
|
|
#------------------------------------------------------------------------------
|
|
class NzoPage(object):
|
|
def __init__(self, web_dir, root, prim):
|
|
self.__root = root
|
|
self.__web_dir = web_dir
|
|
self.__verbose = False
|
|
self.__prim = prim
|
|
self.__cached_selection = {} #None
|
|
|
|
@cherrypy.expose
|
|
def default(self, *args, **kwargs):
|
|
# Allowed URL's
|
|
# /nzb/SABnzbd_nzo_xxxxx/
|
|
# /nzb/SABnzbd_nzo_xxxxx/details
|
|
# /nzb/SABnzbd_nzo_xxxxx/files
|
|
# /nzb/SABnzbd_nzo_xxxxx/bulk_operation
|
|
# /nzb/SABnzbd_nzo_xxxxx/save
|
|
|
|
info, pnfo_list, bytespersec = build_header(self.__prim)
|
|
nzo_id = None
|
|
|
|
for a in args:
|
|
if a.startswith('SABnzbd_nzo'):
|
|
nzo_id = a
|
|
break
|
|
|
|
if nzo_id:
|
|
# /SABnzbd_nzo_xxxxx/bulk_operation
|
|
if 'bulk_operation' in args:
|
|
return self.bulk_operation(nzo_id, kwargs)
|
|
|
|
# /SABnzbd_nzo_xxxxx/details
|
|
elif 'details' in args:
|
|
info = self.nzo_details(info, pnfo_list, nzo_id)
|
|
|
|
# /SABnzbd_nzo_xxxxx/files
|
|
elif 'files' in args:
|
|
info = self.nzo_files(info, pnfo_list, nzo_id)
|
|
|
|
# /SABnzbd_nzo_xxxxx/save
|
|
elif 'save' in args:
|
|
self.save_details(nzo_id, args, kwargs)
|
|
return
|
|
|
|
# /SABnzbd_nzo_xxxxx/
|
|
else:
|
|
info = self.nzo_details(info, pnfo_list, nzo_id)
|
|
info = self.nzo_files(info, pnfo_list, nzo_id)
|
|
|
|
template = Template(file=os.path.join(self.__web_dir, 'nzo.tmpl'),
|
|
filter=FILTER, searchList=[info], compilerSettings=DIRECTIVES)
|
|
return template.respond()
|
|
|
|
|
|
def nzo_details(self, info, pnfo_list, nzo_id):
|
|
slot = {}
|
|
n = 0
|
|
for pnfo in pnfo_list:
|
|
if pnfo[PNFO_NZO_ID_FIELD] == nzo_id:
|
|
repair = pnfo[PNFO_REPAIR_FIELD]
|
|
unpack = pnfo[PNFO_UNPACK_FIELD]
|
|
delete = pnfo[PNFO_DELETE_FIELD]
|
|
unpackopts = sabnzbd.opts_to_pp(repair, unpack, delete)
|
|
script = pnfo[PNFO_SCRIPT_FIELD]
|
|
if script is None:
|
|
script = 'None'
|
|
cat = pnfo[PNFO_EXTRA_FIELD1]
|
|
if not cat:
|
|
cat = 'None'
|
|
filename = xml_name(pnfo[PNFO_FILENAME_FIELD])
|
|
priority = pnfo[PNFO_PRIORITY_FIELD]
|
|
|
|
slot['nzo_id'] = str(nzo_id)
|
|
slot['cat'] = cat
|
|
slot['filename'] = filename
|
|
slot['script'] = script
|
|
slot['priority'] = str(priority)
|
|
slot['unpackopts'] = str(unpackopts)
|
|
info['index'] = n
|
|
break
|
|
n += 1
|
|
|
|
info['slot'] = slot
|
|
info['script_list'] = list_scripts()
|
|
info['cat_list'] = list_cats()
|
|
info['noofslots'] = len(pnfo_list)
|
|
|
|
return info
|
|
|
|
def nzo_files(self, info, pnfo_list, nzo_id):
|
|
|
|
active = []
|
|
for pnfo in pnfo_list:
|
|
if pnfo[PNFO_NZO_ID_FIELD] == nzo_id:
|
|
info['nzo_id'] = nzo_id
|
|
info['filename'] = xml_name(pnfo[PNFO_FILENAME_FIELD])
|
|
|
|
for tup in pnfo[PNFO_ACTIVE_FILES_FIELD]:
|
|
bytes_left, bytes, fn, date, nzf_id = tup
|
|
checked = False
|
|
if nzf_id in self.__cached_selection and \
|
|
self.__cached_selection[nzf_id] == 'on':
|
|
checked = True
|
|
|
|
line = {'filename':xml_name(fn),
|
|
'mbleft':"%.2f" % (bytes_left / MEBI),
|
|
'mb':"%.2f" % (bytes / MEBI),
|
|
'size': format_bytes(bytes),
|
|
'sizeleft':format_bytes(bytes_left),
|
|
'nzf_id':nzf_id,
|
|
'age':calc_age(date),
|
|
'checked':checked}
|
|
active.append(line)
|
|
break
|
|
|
|
info['active_files'] = active
|
|
return info
|
|
|
|
|
|
def save_details(self, nzo_id, args, kwargs):
|
|
index = kwargs.get('index', None)
|
|
name = kwargs.get('name', None)
|
|
pp = kwargs.get('pp', None)
|
|
script = kwargs.get('script', None)
|
|
cat = kwargs.get('cat', None)
|
|
priority = kwargs.get('priority', None)
|
|
nzo = sabnzbd.nzbqueue.get_nzo(nzo_id)
|
|
|
|
if index != None:
|
|
NzbQueue.do.switch(nzo_id, index)
|
|
if name != None:
|
|
NzbQueue.do.change_name(nzo_id, special_fixer(name))
|
|
if cat != None:
|
|
NzbQueue.do.change_cat(nzo_id,cat)
|
|
if script != None:
|
|
NzbQueue.do.change_script(nzo_id,script)
|
|
if pp != None:
|
|
NzbQueue.do.change_opts(nzo_id,pp)
|
|
if priority != None and nzo and nzo.priority != int(priority):
|
|
NzbQueue.do.set_priority(nzo_id, priority)
|
|
|
|
args = [arg for arg in args if arg != 'save']
|
|
extra = '/'.join(args)
|
|
url = cherrypy._urljoin(self.__root, extra)
|
|
if url and not url.endswith('/'):
|
|
url += '/'
|
|
raise dcRaiser(url, {})
|
|
|
|
def bulk_operation(self, nzo_id, kwargs):
|
|
self.__cached_selection = kwargs
|
|
if kwargs['action_key'] == 'Delete':
|
|
for key in kwargs:
|
|
if kwargs[key] == 'on':
|
|
NzbQueue.do.remove_nzf(nzo_id, key)
|
|
|
|
elif kwargs['action_key'] == 'Top' or kwargs['action_key'] == 'Up' or \
|
|
kwargs['action_key'] == 'Down' or kwargs['action_key'] == 'Bottom':
|
|
nzf_ids = []
|
|
for key in kwargs:
|
|
if kwargs[key] == 'on':
|
|
nzf_ids.append(key)
|
|
if kwargs['action_key'] == 'Top':
|
|
NzbQueue.do.move_top_bulk(nzo_id, nzf_ids)
|
|
elif kwargs['action_key'] == 'Up':
|
|
NzbQueue.do.move_up_bulk(nzo_id, nzf_ids)
|
|
elif kwargs['action_key'] == 'Down':
|
|
NzbQueue.do.move_down_bulk(nzo_id, nzf_ids)
|
|
elif kwargs['action_key'] == 'Bottom':
|
|
NzbQueue.do.move_bottom_bulk(nzo_id, nzf_ids)
|
|
|
|
if sabnzbd.nzbqueue.get_nzo(nzo_id):
|
|
url = cherrypy._urljoin(self.__root, nzo_id)
|
|
else:
|
|
url = cherrypy._urljoin(self.__root, '../queue')
|
|
if url and not url.endswith('/'):
|
|
url += '/'
|
|
raise dcRaiser(url, kwargs)
|
|
|
|
|
|
#------------------------------------------------------------------------------
|
|
class QueuePage(object):
|
|
def __init__(self, web_dir, root, prim):
|
|
self.__root = root
|
|
self.__web_dir = web_dir
|
|
self.__verbose = False
|
|
self.__verbose_list = []
|
|
self.__prim = prim
|
|
|
|
@cherrypy.expose
|
|
def index(self, **kwargs):
|
|
start = kwargs.get('start')
|
|
limit = kwargs.get('limit')
|
|
dummy2 = kwargs.get('dummy2')
|
|
|
|
info, pnfo_list, bytespersec, self.__verbose_list, self.__dict__ = build_queue(self.__web_dir, self.__root, self.__verbose,\
|
|
self.__prim, self.__verbose_list, self.__dict__, start=start, limit=limit, dummy2=dummy2, trans=True)
|
|
|
|
template = Template(file=os.path.join(self.__web_dir, 'queue.tmpl'),
|
|
filter=FILTER, searchList=[info], compilerSettings=DIRECTIVES)
|
|
return template.respond()
|
|
|
|
|
|
|
|
@cherrypy.expose
|
|
def delete(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
uid = kwargs.get('uid')
|
|
del_files = int_conv(kwargs.get('del_files'))
|
|
if uid:
|
|
NzbQueue.do.remove(uid, False, keep_basic=not del_files, del_files=del_files)
|
|
raise queueRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def purge(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
NzbQueue.do.remove_all()
|
|
raise queueRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def removeNzf(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
nzo_id = kwargs.get('nzo_id')
|
|
nzf_id = kwargs.get('nzf_id')
|
|
if nzo_id and nzf_id:
|
|
NzbQueue.do.remove_nzf(nzo_id, nzf_id)
|
|
raise queueRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def tog_verbose(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
self.__verbose = not self.__verbose
|
|
raise queueRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def tog_uid_verbose(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
uid = kwargs.get('uid')
|
|
if self.__verbose_list.count(uid):
|
|
self.__verbose_list.remove(uid)
|
|
else:
|
|
self.__verbose_list.append(uid)
|
|
raise queueRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def change_queue_complete_action(self, **kwargs):
|
|
"""
|
|
Action or script to be performed once the queue has been completed
|
|
Scripts are prefixed with 'script_'
|
|
"""
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
action = kwargs.get('action')
|
|
sabnzbd.change_queue_complete_action(action)
|
|
raise queueRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def switch(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
uid1 = kwargs.get('uid1')
|
|
uid2 = kwargs.get('uid2')
|
|
if uid1 and uid2:
|
|
NzbQueue.do.switch(uid1, uid2)
|
|
raise queueRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def change_opts(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
nzo_id = kwargs.get('nzo_id')
|
|
pp = kwargs.get('pp', '')
|
|
if nzo_id and pp and pp.isdigit():
|
|
NzbQueue.do.change_opts(nzo_id, int(pp))
|
|
raise queueRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def change_script(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
nzo_id = kwargs.get('nzo_id')
|
|
script = kwargs.get('script', '')
|
|
if nzo_id and script:
|
|
if script == 'None':
|
|
script = None
|
|
NzbQueue.do.change_script(nzo_id, script)
|
|
raise queueRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def change_cat(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
nzo_id = kwargs.get('nzo_id')
|
|
cat = kwargs.get('cat', '')
|
|
if nzo_id and cat:
|
|
if cat == 'None':
|
|
cat = None
|
|
NzbQueue.do.change_cat(nzo_id, cat)
|
|
cat, pp, script, priority = cat_to_opts(cat)
|
|
NzbQueue.do.change_script(nzo_id, script)
|
|
NzbQueue.do.change_opts(nzo_id, pp)
|
|
NzbQueue.do.set_priority(nzo_id, priority)
|
|
|
|
raise queueRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def shutdown(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg:
|
|
yield msg
|
|
else:
|
|
yield "Initiating shutdown..."
|
|
sabnzbd.halt()
|
|
cherrypy.engine.exit()
|
|
yield "<br>SABnzbd-%s shutdown finished" % sabnzbd.__version__
|
|
sabnzbd.SABSTOP = True
|
|
|
|
@cherrypy.expose
|
|
def pause(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
scheduler.plan_resume(0)
|
|
Downloader.do.pause()
|
|
raise queueRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def resume(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
scheduler.plan_resume(0)
|
|
sabnzbd.unpause_all()
|
|
raise queueRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def pause_nzo(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
uid = kwargs.get('uid', '')
|
|
NzbQueue.do.pause_multiple_nzo(uid.split(','))
|
|
raise queueRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def resume_nzo(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
uid = kwargs.get('uid', '')
|
|
NzbQueue.do.resume_multiple_nzo(uid.split(','))
|
|
raise queueRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def set_priority(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
sabnzbd.nzbqueue.set_priority(kwargs.get('nzo_id'), kwargs.get('priority'))
|
|
raise queueRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def sort_by_avg_age(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
sabnzbd.nzbqueue.sort_queue('avg_age', kwargs.get('dir'))
|
|
raise queueRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def sort_by_name(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
sabnzbd.nzbqueue.sort_queue('name', kwargs.get('dir'))
|
|
raise queueRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def sort_by_size(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
sabnzbd.nzbqueue.sort_queue('size', kwargs.get('dir'))
|
|
raise queueRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def set_speedlimit(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
Downloader.do.limit_speed(int_conv(kwargs.get('value')))
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def set_pause(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
scheduler.plan_resume(int_conv(kwargs.get('value')))
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
class HistoryPage(object):
|
|
def __init__(self, web_dir, root, prim):
|
|
self.__root = root
|
|
self.__web_dir = web_dir
|
|
self.__verbose = False
|
|
self.__verbose_list = []
|
|
self.__failed_only = False
|
|
self.__prim = prim
|
|
|
|
@cherrypy.expose
|
|
def index(self, **kwargs):
|
|
start = kwargs.get('start')
|
|
limit = kwargs.get('limit')
|
|
search = kwargs.get('search')
|
|
failed_only = kwargs.get('failed_only')
|
|
if failed_only is None:
|
|
failed_only = self.__failed_only
|
|
|
|
history, pnfo_list, bytespersec = build_header(self.__prim)
|
|
|
|
history['isverbose'] = self.__verbose
|
|
history['failed_only'] = failed_only
|
|
|
|
if cfg.newzbin_username() and cfg.newzbin_password():
|
|
history['newzbinDetails'] = True
|
|
|
|
#history_items, total_bytes, bytes_beginning = sabnzbd.history_info()
|
|
#history['bytes_beginning'] = "%.2f" % (bytes_beginning / GIGI)
|
|
|
|
grand, month, week, day = BPSMeter.do.get_sums()
|
|
history['total_size'], history['month_size'], history['week_size'], history['day_size'] = \
|
|
to_units(grand), to_units(month), to_units(week), to_units(day)
|
|
|
|
history['lines'], history['fetched'], history['noofslots'] = build_history(limit=limit, start=start, verbose=self.__verbose, verbose_list=self.__verbose_list, search=search, failed_only=failed_only)
|
|
|
|
if search:
|
|
history['search'] = escape(search)
|
|
else:
|
|
history['search'] = ''
|
|
|
|
history['start'] = int_conv(start)
|
|
history['limit'] = int_conv(limit)
|
|
history['finish'] = history['start'] + history['limit']
|
|
if history['finish'] > history['noofslots']:
|
|
history['finish'] = history['noofslots']
|
|
if not history['finish']:
|
|
history['finish'] = history['fetched']
|
|
history['time_format'] = time_format
|
|
|
|
template = Template(file=os.path.join(self.__web_dir, 'history.tmpl'),
|
|
filter=FILTER, searchList=[history], compilerSettings=DIRECTIVES)
|
|
return template.respond()
|
|
|
|
@cherrypy.expose
|
|
def purge(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
history_db = cherrypy.thread_data.history_db
|
|
history_db.remove_history()
|
|
raise queueRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def delete(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
job = kwargs.get('job')
|
|
del_files = int_conv(kwargs.get('del_files'))
|
|
if job:
|
|
jobs = job.split(',')
|
|
for job in jobs:
|
|
del_hist_job(job, del_files=del_files)
|
|
raise queueRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def retry_pp(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
retry_job(kwargs.get('job'), kwargs.get('nzbfile'))
|
|
raise queueRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def purge_failed(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
history_db = cherrypy.thread_data.history_db
|
|
history_db.remove_failed()
|
|
raise queueRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def reset(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
#sabnzbd.reset_byte_counter()
|
|
raise queueRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def tog_verbose(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
jobs = kwargs.get('jobs')
|
|
if not jobs:
|
|
self.__verbose = not self.__verbose
|
|
self.__verbose_list = []
|
|
else:
|
|
if self.__verbose:
|
|
self.__verbose = False
|
|
else:
|
|
jobs = jobs.split(',')
|
|
for job in jobs:
|
|
if job in self.__verbose_list:
|
|
self.__verbose_list.remove(job)
|
|
else:
|
|
self.__verbose_list.append(job)
|
|
raise queueRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def tog_failed_only(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
self.__failed_only = not self.__failed_only
|
|
raise queueRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def scriptlog(self, **kwargs):
|
|
""" Duplicate of scriptlog of History, needed for some skins """
|
|
# No session key check, due to fixed URLs
|
|
|
|
name = kwargs.get('name')
|
|
if name:
|
|
history_db = cherrypy.thread_data.history_db
|
|
return ShowString(history_db.get_name(name), history_db.get_script_log(name))
|
|
else:
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def retry(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
url = kwargs.get('url', '').strip()
|
|
pp = kwargs.get('pp')
|
|
cat = kwargs.get('cat')
|
|
script = kwargs.get('script')
|
|
if url and (url.isdigit() or len(url)==5):
|
|
sabnzbd.add_msgid(url, pp, script, cat)
|
|
elif url:
|
|
sabnzbd.add_url(url, pp, script, cat, nzbname=kwargs.get('nzbname'))
|
|
if url:
|
|
return ShowOK(url)
|
|
else:
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
#------------------------------------------------------------------------------
|
|
class ConfigPage(object):
|
|
def __init__(self, web_dir, root, prim):
|
|
self.__root = root
|
|
self.__web_dir = web_dir
|
|
self.__prim = prim
|
|
self.directories = ConfigDirectories(web_dir, root+'directories/', prim)
|
|
self.email = ConfigEmail(web_dir, root+'email/', prim)
|
|
self.general = ConfigGeneral(web_dir, root+'general/', prim)
|
|
self.newzbin = ConfigNewzbin(web_dir, root+'newzbin/', prim)
|
|
self.rss = ConfigRss(web_dir, root+'rss/', prim)
|
|
self.scheduling = ConfigScheduling(web_dir, root+'scheduling/', prim)
|
|
self.server = ConfigServer(web_dir, root+'server/', prim)
|
|
self.switches = ConfigSwitches(web_dir, root+'switches/', prim)
|
|
self.categories = ConfigCats(web_dir, root+'categories/', prim)
|
|
self.sorting = ConfigSorting(web_dir, root+'sorting/', prim)
|
|
|
|
|
|
@cherrypy.expose
|
|
def index(self, **kwargs):
|
|
conf, pnfo_list, bytespersec = build_header(self.__prim)
|
|
|
|
conf['configfn'] = config.get_filename()
|
|
|
|
new = {}
|
|
for svr in config.get_servers():
|
|
new[svr] = {}
|
|
conf['servers'] = new
|
|
|
|
conf['folders'] = sabnzbd.nzbqueue.scan_jobs(all=False, action=False)
|
|
|
|
template = Template(file=os.path.join(self.__web_dir, 'config.tmpl'),
|
|
filter=FILTER, searchList=[conf], compilerSettings=DIRECTIVES)
|
|
return template.respond()
|
|
|
|
@cherrypy.expose
|
|
def restart(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg:
|
|
yield msg
|
|
else:
|
|
yield T('Initiating restart...<br />')
|
|
sabnzbd.halt()
|
|
yield T(' <br />SABnzbd shutdown finished.<br />Wait for about 5 second and then click the button below.<br /><br /><strong><a href="..">Refresh</a></strong><br />')
|
|
cherrypy.engine.restart()
|
|
|
|
@cherrypy.expose
|
|
def repair(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg:
|
|
yield msg
|
|
else:
|
|
sabnzbd.request_repair()
|
|
yield T('Initiating restart...<br />')
|
|
sabnzbd.halt()
|
|
yield T(' <br />SABnzbd shutdown finished.<br />Wait for about 5 second and then click the button below.<br /><br /><strong><a href="..">Refresh</a></strong><br />')
|
|
cherrypy.engine.restart()
|
|
|
|
@cherrypy.expose
|
|
def delete(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
orphan_delete(kwargs)
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def add(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
orphan_add(kwargs)
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
def orphan_delete(kwargs):
|
|
path = kwargs.get('name')
|
|
if path:
|
|
path = os.path.join(cfg.download_dir.get_path(), path)
|
|
clean_folder(os.path.join(path, JOB_ADMIN))
|
|
clean_folder(path)
|
|
|
|
def orphan_add(kwargs):
|
|
path = kwargs.get('name')
|
|
if path:
|
|
path = os.path.join(cfg.download_dir.get_path(), path)
|
|
sabnzbd.nzbqueue.repair_job(path, None)
|
|
|
|
|
|
#------------------------------------------------------------------------------
|
|
LIST_DIRPAGE = ( \
|
|
'download_dir', 'download_free', 'complete_dir', 'cache_dir', 'admin_dir',
|
|
'nzb_backup_dir', 'dirscan_dir', 'dirscan_speed', 'script_dir',
|
|
'email_dir', 'permissions', 'log_dir', 'password_file'
|
|
)
|
|
|
|
class ConfigDirectories(object):
|
|
def __init__(self, web_dir, root, prim):
|
|
self.__root = root
|
|
self.__web_dir = web_dir
|
|
self.__prim = prim
|
|
|
|
@cherrypy.expose
|
|
def index(self, **kwargs):
|
|
if cfg.configlock():
|
|
return Protected()
|
|
|
|
conf, pnfo_list, bytespersec = build_header(self.__prim)
|
|
|
|
for kw in LIST_DIRPAGE:
|
|
conf[kw] = config.get_config('misc', kw)()
|
|
|
|
conf['my_home'] = sabnzbd.DIR_HOME
|
|
conf['my_lcldata'] = sabnzbd.DIR_LCLDATA
|
|
|
|
# Temporary fix, problem with build_header
|
|
conf['restart_req'] = sabnzbd.RESTART_REQ
|
|
|
|
template = Template(file=os.path.join(self.__web_dir, 'config_directories.tmpl'),
|
|
filter=FILTER, searchList=[conf], compilerSettings=DIRECTIVES)
|
|
return template.respond()
|
|
|
|
@cherrypy.expose
|
|
def saveDirectories(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
|
|
cfg.complete_dir.set_create()
|
|
for kw in LIST_DIRPAGE:
|
|
value = kwargs.get(kw)
|
|
if value != None:
|
|
value = platform_encode(value)
|
|
msg = config.get_config('misc', kw).set(value)
|
|
if msg:
|
|
return badParameterResponse(msg)
|
|
|
|
config.save_config()
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
|
|
SWITCH_LIST = \
|
|
('par2_multicore', 'par_option', 'enable_unrar', 'enable_unzip', 'enable_filejoin',
|
|
'enable_tsjoin', 'send_group', 'fail_on_crc', 'top_only',
|
|
'enable_par_cleanup', 'auto_sort', 'check_new_rel', 'auto_disconnect',
|
|
'safe_postproc', 'no_dupes', 'replace_spaces', 'replace_dots', 'replace_illegal', 'auto_browser',
|
|
'ignore_samples', 'pause_on_post_processing', 'quick_check', 'nice', 'ionice',
|
|
'ssl_type', 'pre_script', 'pause_on_pwrar', 'ampm', 'sfv_check', 'folder_rename'
|
|
)
|
|
|
|
#------------------------------------------------------------------------------
|
|
class ConfigSwitches(object):
|
|
def __init__(self, web_dir, root, prim):
|
|
self.__root = root
|
|
self.__web_dir = web_dir
|
|
self.__prim = prim
|
|
|
|
@cherrypy.expose
|
|
def index(self, **kwargs):
|
|
if cfg.configlock():
|
|
return Protected()
|
|
|
|
conf, pnfo_list, bytespersec = build_header(self.__prim)
|
|
|
|
conf['nt'] = sabnzbd.WIN32
|
|
conf['have_nice'] = bool(sabnzbd.newsunpack.NICE_COMMAND)
|
|
conf['have_ionice'] = bool(sabnzbd.newsunpack.IONICE_COMMAND)
|
|
|
|
for kw in SWITCH_LIST:
|
|
conf[kw] = config.get_config('misc', kw)()
|
|
|
|
conf['script_list'] = list_scripts() or ['None']
|
|
conf['have_ampm'] = HAVE_AMPM
|
|
|
|
template = Template(file=os.path.join(self.__web_dir, 'config_switches.tmpl'),
|
|
filter=FILTER, searchList=[conf], compilerSettings=DIRECTIVES)
|
|
return template.respond()
|
|
|
|
@cherrypy.expose
|
|
def saveSwitches(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
|
|
for kw in SWITCH_LIST:
|
|
item = config.get_config('misc', kw)
|
|
value = platform_encode(kwargs.get(kw))
|
|
msg = item.set(value)
|
|
if msg:
|
|
return badParameterResponse(msg)
|
|
|
|
config.save_config()
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
|
|
#------------------------------------------------------------------------------
|
|
GENERAL_LIST = (
|
|
'host', 'port', 'username', 'password', 'disable_api_key',
|
|
'refresh_rate', 'cache_limit',
|
|
'enable_https', 'https_port', 'https_cert', 'https_key'
|
|
)
|
|
|
|
class ConfigGeneral(object):
|
|
def __init__(self, web_dir, root, prim):
|
|
self.__root = root
|
|
self.__web_dir = web_dir
|
|
self.__prim = prim
|
|
|
|
@cherrypy.expose
|
|
def index(self, **kwargs):
|
|
def ListColors(web_dir):
|
|
lst = []
|
|
web_dir = os.path.join(sabnzbd.DIR_INTERFACES, web_dir)
|
|
dd = os.path.abspath(web_dir + '/templates/static/stylesheets/colorschemes')
|
|
if (not dd) or (not os.access(dd, os.R_OK)):
|
|
return lst
|
|
for color in globber(dd):
|
|
col = os.path.basename(color).replace('.css','')
|
|
if col != "_svn" and col != ".svn":
|
|
lst.append(col)
|
|
return lst
|
|
|
|
def add_color(dir, color):
|
|
if dir:
|
|
if not color:
|
|
try:
|
|
color = DEF_SKIN_COLORS[dir.lower()]
|
|
except KeyError:
|
|
return dir
|
|
return '%s - %s' % (dir, color)
|
|
else:
|
|
return ''
|
|
|
|
if cfg.configlock():
|
|
return Protected()
|
|
|
|
conf, pnfo_list, bytespersec = build_header(self.__prim)
|
|
|
|
conf['configfn'] = config.get_filename()
|
|
|
|
# Temporary fix, problem with build_header
|
|
conf['restart_req'] = sabnzbd.RESTART_REQ
|
|
|
|
if sabnzbd.newswrapper.HAVE_SSL:
|
|
conf['have_ssl'] = 1
|
|
else:
|
|
conf['have_ssl'] = 0
|
|
|
|
wlist = []
|
|
wlist2 = ['None']
|
|
interfaces = globber(sabnzbd.DIR_INTERFACES)
|
|
for k in interfaces:
|
|
if k.endswith(DEF_STDINTF):
|
|
interfaces.remove(k)
|
|
interfaces.insert(0, k)
|
|
break
|
|
for web in interfaces:
|
|
rweb = os.path.basename(web)
|
|
if rweb != '.svn' and rweb != '_svn' and os.access(web + '/' + DEF_MAIN_TMPL, os.R_OK):
|
|
cols = ListColors(rweb)
|
|
if cols:
|
|
for col in cols:
|
|
if rweb != 'Mobile':
|
|
wlist.append(add_color(rweb, col))
|
|
wlist2.append(add_color(rweb, col))
|
|
else:
|
|
if rweb != 'Mobile':
|
|
wlist.append(rweb)
|
|
wlist2.append(rweb)
|
|
conf['web_list'] = wlist
|
|
conf['web_list2'] = wlist2
|
|
|
|
# Obsolete template variables, must exist and have a value
|
|
conf['web_colors'] = ['None']
|
|
conf['web_color'] = 'None'
|
|
conf['web_colors2'] = ['None']
|
|
conf['web_color2'] = 'None'
|
|
|
|
conf['web_dir'] = add_color(cfg.web_dir(), cfg.web_color())
|
|
conf['web_dir2'] = add_color(cfg.web_dir2(), cfg.web_color2())
|
|
|
|
conf['language'] = cfg.language()
|
|
list = list_languages()
|
|
if len(list) < 2:
|
|
list = []
|
|
conf['lang_list'] = list
|
|
|
|
conf['disable_api_key'] = cfg.disable_key()
|
|
conf['host'] = cfg.cherryhost()
|
|
conf['port'] = cfg.cherryport()
|
|
conf['https_port'] = cfg.https_port()
|
|
conf['https_cert'] = cfg.https_cert()
|
|
conf['https_key'] = cfg.https_key()
|
|
conf['enable_https'] = cfg.enable_https()
|
|
conf['username'] = cfg.username()
|
|
conf['password'] = cfg.password.get_stars()
|
|
conf['bandwidth_limit'] = cfg.bandwidth_limit()
|
|
conf['refresh_rate'] = cfg.refresh_rate()
|
|
conf['cache_limit'] = cfg.cache_limit()
|
|
conf['cleanup_list'] = cfg.cleanup_list.get_string()
|
|
|
|
template = Template(file=os.path.join(self.__web_dir, 'config_general.tmpl'),
|
|
filter=FILTER, searchList=[conf], compilerSettings=DIRECTIVES)
|
|
return template.respond()
|
|
|
|
@cherrypy.expose
|
|
def saveGeneral(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
|
|
# Special handling for cache_limitstr
|
|
#kwargs['cache_limit'] = kwargs.get('cache_limitstr')
|
|
|
|
# Handle general options
|
|
for kw in GENERAL_LIST:
|
|
item = config.get_config('misc', kw)
|
|
value = platform_encode(kwargs.get(kw))
|
|
msg = item.set(value)
|
|
if msg:
|
|
return badParameterResponse(msg)
|
|
|
|
# Handle special options
|
|
language = kwargs.get('language')
|
|
if language and language != cfg.language():
|
|
cfg.language.set(language)
|
|
set_language(language)
|
|
sabnzbd.api.cache_skin_trans()
|
|
|
|
cleanup_list = kwargs.get('cleanup_list')
|
|
if cleanup_list and sabnzbd.WIN32:
|
|
cleanup_list = cleanup_list.lower()
|
|
cfg.cleanup_list.set(cleanup_list)
|
|
|
|
web_dir = kwargs.get('web_dir')
|
|
web_dir2 = kwargs.get('web_dir2')
|
|
change_web_dir(web_dir)
|
|
try:
|
|
web_dir2, web_color2 = web_dir2.split(' - ')
|
|
except:
|
|
web_color2 = ''
|
|
web_dir2_path = real_path(sabnzbd.DIR_INTERFACES, web_dir2)
|
|
|
|
if web_dir2 == 'None':
|
|
cfg.web_dir2.set('')
|
|
elif os.path.exists(web_dir2_path):
|
|
cfg.web_dir2.set(web_dir2)
|
|
cfg.web_color2.set(web_color2)
|
|
|
|
bandwidth_limit = kwargs.get('bandwidth_limit')
|
|
if bandwidth_limit != None:
|
|
bandwidth_limit = int_conv(bandwidth_limit)
|
|
cfg.bandwidth_limit.set(bandwidth_limit)
|
|
|
|
config.save_config()
|
|
|
|
# Update CherryPy authentication
|
|
set_auth(cherrypy.config)
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def generateAPIKey(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
|
|
logging.debug('API Key Changed')
|
|
cfg.api_key.set(config.create_api_key())
|
|
config.save_config()
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
def change_web_dir(web_dir):
|
|
try:
|
|
web_dir, web_color = web_dir.split(' - ')
|
|
except:
|
|
try:
|
|
web_color = DEF_SKIN_COLORS[web_dir.lower()]
|
|
except:
|
|
web_color = ''
|
|
|
|
web_dir_path = real_path(sabnzbd.DIR_INTERFACES, web_dir)
|
|
|
|
if not os.path.exists(web_dir_path):
|
|
return badParameterResponse('Cannot find web template: %s' % unicoder(web_dir_path))
|
|
else:
|
|
cfg.web_dir.set(web_dir)
|
|
cfg.web_color.set(web_color)
|
|
|
|
|
|
#------------------------------------------------------------------------------
|
|
|
|
class ConfigServer(object):
|
|
def __init__(self, web_dir, root, prim):
|
|
self.__root = root
|
|
self.__web_dir = web_dir
|
|
self.__prim = prim
|
|
|
|
@cherrypy.expose
|
|
def index(self, **kwargs):
|
|
if cfg.configlock():
|
|
return Protected()
|
|
|
|
conf, pnfo_list, bytespersec = build_header(self.__prim)
|
|
|
|
new = {}
|
|
servers = config.get_servers()
|
|
for svr in servers:
|
|
new[svr] = servers[svr].get_dict(safe=True)
|
|
t, m, w, d = BPSMeter.do.amounts(svr)
|
|
if t:
|
|
new[svr]['amounts'] = to_units(t), to_units(m), to_units(w), to_units(d)
|
|
conf['servers'] = new
|
|
|
|
if sabnzbd.newswrapper.HAVE_SSL:
|
|
conf['have_ssl'] = 1
|
|
else:
|
|
conf['have_ssl'] = 0
|
|
|
|
template = Template(file=os.path.join(self.__web_dir, 'config_server.tmpl'),
|
|
filter=FILTER, searchList=[conf], compilerSettings=DIRECTIVES)
|
|
return template.respond()
|
|
|
|
|
|
@cherrypy.expose
|
|
def addServer(self, **kwargs):
|
|
return handle_server(kwargs, self.__root, True)
|
|
|
|
|
|
@cherrypy.expose
|
|
def saveServer(self, **kwargs):
|
|
return handle_server(kwargs, self.__root)
|
|
|
|
@cherrypy.expose
|
|
def testServer(self, **kwargs):
|
|
return handle_server_test(kwargs, self.__root)
|
|
|
|
|
|
@cherrypy.expose
|
|
def delServer(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
kwargs['section'] = 'servers'
|
|
kwargs['keyword'] = kwargs.get('server')
|
|
del_from_section(kwargs)
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
|
|
#------------------------------------------------------------------------------
|
|
def unique_svr_name(server):
|
|
""" Return a unique variant on given server name
|
|
"""
|
|
num = 0
|
|
svr = 1
|
|
new_name = server
|
|
while svr:
|
|
if num:
|
|
new_name = '%s@%d' % (server, num)
|
|
else:
|
|
new_name = '%s' % server
|
|
svr = config.get_config('servers', new_name)
|
|
num += 1
|
|
return new_name
|
|
|
|
|
|
def handle_server(kwargs, root=None, new_svr=False):
|
|
""" Internal server handler """
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
|
|
host = kwargs.get('host', '').strip()
|
|
if not host:
|
|
return badParameterResponse(T('Server address required'))
|
|
|
|
port = kwargs.get('port', '').strip()
|
|
if not port:
|
|
if not kwargs.get('ssl', '').strip():
|
|
port = '119'
|
|
else:
|
|
port = '563'
|
|
kwargs['port'] = port
|
|
|
|
if kwargs.get('connections', '').strip() == '':
|
|
kwargs['connections'] = '1'
|
|
|
|
msg = check_server(host, port)
|
|
if msg:
|
|
return msg
|
|
|
|
# Default server name is just the host name
|
|
server = host
|
|
|
|
svr = None
|
|
old_server = kwargs.get('server')
|
|
if old_server:
|
|
svr = config.get_config('servers', old_server)
|
|
if svr:
|
|
server = old_server
|
|
else:
|
|
svr = config.get_config('servers', server)
|
|
|
|
if new_svr:
|
|
server = unique_svr_name(server)
|
|
|
|
if svr and not new_svr:
|
|
for kw in ('fillserver', 'ssl', 'enable', 'optional'):
|
|
if kw not in kwargs.keys():
|
|
kwargs[kw] = None
|
|
svr.set_dict(kwargs)
|
|
else:
|
|
old_server = None
|
|
config.ConfigServer(server, kwargs)
|
|
|
|
config.save_config()
|
|
Downloader.do.update_server(old_server, server)
|
|
if root:
|
|
raise dcRaiser(root, kwargs)
|
|
|
|
|
|
def handle_server_test(kwargs, root):
|
|
result, msg = test_nntp_server_dict(kwargs)
|
|
return msg
|
|
|
|
#------------------------------------------------------------------------------
|
|
|
|
class ConfigRss(object):
|
|
def __init__(self, web_dir, root, prim):
|
|
self.__root = root
|
|
self.__web_dir = web_dir
|
|
self.__prim = prim
|
|
self.__refresh_readout = None # Set to URL when new readout is needed
|
|
self.__refresh_download = False
|
|
self.__refresh_force = False
|
|
self.__refresh_ignore = False
|
|
|
|
@cherrypy.expose
|
|
def index(self, **kwargs):
|
|
if cfg.configlock():
|
|
return Protected()
|
|
|
|
conf, pnfo_list, bytespersec = build_header(self.__prim)
|
|
|
|
conf['script_list'] = list_scripts(default=True)
|
|
pick_script = conf['script_list'] != []
|
|
|
|
conf['cat_list'] = list_cats(default=True)
|
|
pick_cat = conf['cat_list'] != []
|
|
|
|
conf['rss_rate'] = cfg.rss_rate()
|
|
|
|
rss = {}
|
|
feeds = config.get_rss()
|
|
for feed in feeds:
|
|
rss[feed] = feeds[feed].get_dict()
|
|
filters = feeds[feed].filters()
|
|
rss[feed]['filters'] = filters
|
|
rss[feed]['filtercount'] = len(filters)
|
|
|
|
rss[feed]['pick_cat'] = pick_cat
|
|
rss[feed]['pick_script'] = pick_script
|
|
rss[feed]['link'] = urllib.quote_plus(feed)
|
|
|
|
active_feed = kwargs.get('feed', '')
|
|
conf['active_feed'] = active_feed
|
|
conf['rss'] = rss
|
|
|
|
if active_feed:
|
|
readout = bool(self.__refresh_readout)
|
|
logging.debug('RSS READOUT = %s', readout)
|
|
if not readout:
|
|
self.__refresh_download = False
|
|
self.__refresh_force = False
|
|
self.__refresh_ignore = False
|
|
msg = sabnzbd.rss.run_feed(active_feed, download=self.__refresh_download, force=self.__refresh_force, \
|
|
ignoreFirst=self.__refresh_ignore, readout=readout)
|
|
self.__refresh_readout = None
|
|
conf['error'] = msg
|
|
|
|
conf['downloaded'], conf['matched'], conf['unmatched'] = GetRssLog(active_feed)
|
|
|
|
|
|
# Find a unique new Feed name
|
|
unum = 1
|
|
txt = Ta('Feed') #: Used as default Feed name in Config->RSS
|
|
while txt + str(unum) in feeds:
|
|
unum += 1
|
|
conf['feed'] = txt + str(unum)
|
|
|
|
template = Template(file=os.path.join(self.__web_dir, 'config_rss.tmpl'),
|
|
filter=FILTER, searchList=[conf], compilerSettings=DIRECTIVES)
|
|
return template.respond()
|
|
|
|
@cherrypy.expose
|
|
def save_rss_rate(self, **kwargs):
|
|
""" Save changed RSS automatic readout rate """
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
cfg.rss_rate.set(kwargs.get('rss_rate'))
|
|
raise rssRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def upd_rss_feed(self, **kwargs):
|
|
""" Update Feed level attributes """
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
if kwargs.get('enable') is not None:
|
|
del kwargs['enable']
|
|
try:
|
|
cfg = config.get_rss()[kwargs.get('feed')]
|
|
except KeyError:
|
|
cfg = None
|
|
if cfg and Strip(kwargs.get('uri')):
|
|
cfg.set_dict(kwargs)
|
|
config.save_config()
|
|
|
|
raise rssRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def toggle_rss_feed(self, **kwargs):
|
|
""" Toggle automatic read-out flag of Feed """
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
try:
|
|
item = config.get_rss()[kwargs.get('feed')]
|
|
except KeyError:
|
|
item = None
|
|
if cfg:
|
|
item.enable.set(not item.enable())
|
|
config.save_config()
|
|
if kwargs.get('table'):
|
|
raise dcRaiser(self.__root, kwargs)
|
|
else:
|
|
raise rssRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def add_rss_feed(self, **kwargs):
|
|
""" Add one new RSS feed definition """
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
feed= Strip(kwargs.get('feed')).strip('[]')
|
|
uri = Strip(kwargs.get('uri'))
|
|
try:
|
|
cfg = config.get_rss()[feed]
|
|
except KeyError:
|
|
cfg = None
|
|
if (not cfg) and uri:
|
|
config.ConfigRSS(feed, kwargs)
|
|
# Clear out any existing reference to this feed name
|
|
# Otherwise first-run detection can fail
|
|
sabnzbd.rss.clear_feed(feed)
|
|
config.save_config()
|
|
self.__refresh_readout = feed
|
|
self.__refresh_download = False
|
|
self.__refresh_force = False
|
|
self.__refresh_ignore = True
|
|
raise rssRaiser(self.__root, kwargs)
|
|
else:
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def upd_rss_filter(self, **kwargs):
|
|
""" Save updated filter definition """
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
try:
|
|
cfg = config.get_rss()[kwargs.get('feed')]
|
|
except KeyError:
|
|
raise rssRaiser(self.__root, kwargs)
|
|
|
|
pp = kwargs.get('pp')
|
|
if IsNone(pp): pp = ''
|
|
script = ConvertSpecials(kwargs.get('script'))
|
|
cat = ConvertSpecials(kwargs.get('cat'))
|
|
prio = ConvertSpecials(kwargs.get('priority'))
|
|
|
|
cfg.filters.update(int(kwargs.get('index', 0)), (cat, pp, script, kwargs.get('filter_type'), \
|
|
platform_encode(kwargs.get('filter_text')), prio ))
|
|
config.save_config()
|
|
raise rssRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def pos_rss_filter(self, **kwargs):
|
|
""" Change position of a filter """
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
feed = kwargs.get('feed')
|
|
current = kwargs.get('current', 0)
|
|
new = kwargs.get('new', 0)
|
|
|
|
try:
|
|
cfg = config.get_rss()[feed]
|
|
except KeyError:
|
|
raise rssRaiser(self.__root, kwargs)
|
|
|
|
if current != new:
|
|
cfg.filters.move(int(current), int(new))
|
|
config.save_config()
|
|
raise rssRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def del_rss_feed(self, *args, **kwargs):
|
|
""" Remove complete RSS feed """
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
kwargs['section'] = 'rss'
|
|
kwargs['keyword'] = kwargs.get('feed')
|
|
del_from_section(kwargs)
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def del_rss_filter(self, **kwargs):
|
|
""" Remove one RSS filter """
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
try:
|
|
cfg = config.get_rss()[kwargs.get('feed')]
|
|
except KeyError:
|
|
raise rssRaiser(self.__root, kwargs)
|
|
|
|
cfg.filters.delete(int(kwargs.get('index', 0)))
|
|
config.save_config()
|
|
raise rssRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def download_rss_feed(self, *args, **kwargs):
|
|
""" Force download of all matching jobs in a feed """
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
if 'feed' in kwargs:
|
|
feed = kwargs['feed']
|
|
self.__refresh_readout = feed
|
|
self.__refresh_download = True
|
|
self.__refresh_force = True
|
|
self.__refresh_ignore = False
|
|
raise rssRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def test_rss_feed(self, *args, **kwargs):
|
|
""" Read the feed content again and show results """
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
if 'feed' in kwargs:
|
|
feed = kwargs['feed']
|
|
self.__refresh_readout = feed
|
|
self.__refresh_download = False
|
|
self.__refresh_force = False
|
|
self.__refresh_ignore = True
|
|
raise rssRaiser(self.__root, kwargs)
|
|
|
|
|
|
@cherrypy.expose
|
|
def download(self, **kwargs):
|
|
""" Download NZB from provider (Download button) """
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
feed = kwargs.get('feed')
|
|
url = kwargs.get('url')
|
|
att = sabnzbd.rss.lookup_url(feed, url)
|
|
if att:
|
|
pp = att.get('pp')
|
|
cat = att.get('cat')
|
|
script = att.get('script')
|
|
prio = att.get('prio')
|
|
nzbname = att.get('nzbname')
|
|
|
|
if url and url.isdigit():
|
|
sabnzbd.add_msgid(url, pp, script, cat, prio, nzbname)
|
|
elif url:
|
|
sabnzbd.add_url(url, pp, script, cat, prio, nzbname)
|
|
# Need to pass the title instead
|
|
sabnzbd.rss.flag_downloaded(feed, url)
|
|
raise rssRaiser(self.__root, kwargs)
|
|
|
|
|
|
#------------------------------------------------------------------------------
|
|
_SCHED_ACTIONS = ('resume', 'pause', 'pause_all', 'shutdown', 'restart', 'speedlimit',
|
|
'pause_post', 'resume_post', 'scan_folder', 'rss_scan')
|
|
|
|
class ConfigScheduling(object):
|
|
def __init__(self, web_dir, root, prim):
|
|
self.__root = root
|
|
self.__web_dir = web_dir
|
|
self.__prim = prim
|
|
|
|
@cherrypy.expose
|
|
def index(self, **kwargs):
|
|
def get_days():
|
|
days = {}
|
|
days["*"] = T('Daily')
|
|
days["1"] = T('Monday')
|
|
days["2"] = T('Tuesday')
|
|
days["3"] = T('Wednesday')
|
|
days["4"] = T('Thursday')
|
|
days["5"] = T('Friday')
|
|
days["6"] = T('Saturday')
|
|
days["7"] = T('Sunday')
|
|
return days
|
|
|
|
if cfg.configlock():
|
|
return Protected()
|
|
|
|
conf, pnfo_list, bytespersec = build_header(self.__prim)
|
|
|
|
actions = []
|
|
actions.extend(_SCHED_ACTIONS)
|
|
days = get_days()
|
|
conf['schedlines'] = []
|
|
snum = 1
|
|
conf['taskinfo'] = []
|
|
for ev in scheduler.sort_schedules(forward=True):
|
|
line = ev[3]
|
|
conf['schedlines'].append(line)
|
|
try:
|
|
m, h, day, action = line.split(' ', 3)
|
|
except:
|
|
continue
|
|
action = action.strip()
|
|
if action in actions:
|
|
action = Ttemplate("sch-" + action)
|
|
else:
|
|
try:
|
|
act, server = action.split()
|
|
except ValueError:
|
|
act = ''
|
|
if act in ('enable_server', 'disable_server'):
|
|
action = Ttemplate("sch-" + act) + ' ' + server
|
|
item = (snum, h, '%02d' % int(m), days.get(day, '**'), action)
|
|
conf['taskinfo'].append(item)
|
|
snum += 1
|
|
|
|
|
|
actions_lng = {}
|
|
for action in actions:
|
|
actions_lng[action] = Ttemplate("sch-" + action)
|
|
for server in config.get_servers():
|
|
actions.append(server)
|
|
actions_lng[server] = server
|
|
conf['actions'] = actions
|
|
conf['actions_lng'] = actions_lng
|
|
|
|
template = Template(file=os.path.join(self.__web_dir, 'config_scheduling.tmpl'),
|
|
filter=FILTER, searchList=[conf], compilerSettings=DIRECTIVES)
|
|
return template.respond()
|
|
|
|
@cherrypy.expose
|
|
def addSchedule(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
|
|
minute = kwargs.get('minute')
|
|
hour = kwargs.get('hour')
|
|
dayofweek = kwargs.get('dayofweek')
|
|
action = kwargs.get('action')
|
|
arguments = kwargs.get('arguments')
|
|
|
|
arguments = arguments.strip().lower()
|
|
if arguments in ('on', 'enable'):
|
|
arguments = '1'
|
|
elif arguments in ('off','disable'):
|
|
arguments = '0'
|
|
|
|
if minute and hour and dayofweek and action:
|
|
if (action == 'speedlimit') and arguments.isdigit():
|
|
pass
|
|
elif action in _SCHED_ACTIONS:
|
|
arguments = ''
|
|
elif action.find(':') > 0:
|
|
if arguments == '1':
|
|
arguments = action
|
|
action = 'enable_server'
|
|
else:
|
|
arguments = action
|
|
action = 'disable_server'
|
|
else:
|
|
action = None
|
|
|
|
if action:
|
|
sched = cfg.schedules()
|
|
sched.append('%s %s %s %s %s' %
|
|
(minute, hour, dayofweek, action, arguments))
|
|
cfg.schedules.set(sched)
|
|
|
|
config.save_config()
|
|
scheduler.restart(force=True)
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def delSchedule(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
|
|
schedules = cfg.schedules()
|
|
line = kwargs.get('line')
|
|
if line and line in schedules:
|
|
schedules.remove(line)
|
|
cfg.schedules.set(schedules)
|
|
config.save_config()
|
|
scheduler.restart(force=True)
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
#------------------------------------------------------------------------------
|
|
class ConfigNewzbin(object):
|
|
def __init__(self, web_dir, root, prim):
|
|
self.__root = root
|
|
self.__web_dir = web_dir
|
|
self.__prim = prim
|
|
self.__bookmarks = []
|
|
|
|
@cherrypy.expose
|
|
def index(self, **kwargs):
|
|
if cfg.configlock():
|
|
return Protected()
|
|
|
|
conf, pnfo_list, bytespersec = build_header(self.__prim)
|
|
|
|
conf['username_newzbin'] = cfg.newzbin_username()
|
|
conf['password_newzbin'] = cfg.newzbin_password.get_stars()
|
|
conf['newzbin_bookmarks'] = int(cfg.newzbin_bookmarks())
|
|
conf['newzbin_unbookmark'] = int(cfg.newzbin_unbookmark())
|
|
conf['bookmark_rate'] = cfg.bookmark_rate()
|
|
|
|
conf['bookmarks_list'] = self.__bookmarks
|
|
|
|
conf['matrix_username'] = cfg.matrix_username()
|
|
conf['matrix_apikey'] = cfg.matrix_apikey()
|
|
conf['matrix_del_bookmark'] = int(cfg.matrix_del_bookmark())
|
|
|
|
template = Template(file=os.path.join(self.__web_dir, 'config_newzbin.tmpl'),
|
|
filter=FILTER, searchList=[conf], compilerSettings=DIRECTIVES)
|
|
return template.respond()
|
|
|
|
@cherrypy.expose
|
|
def saveNewzbin(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
|
|
cfg.newzbin_username.set(kwargs.get('username_newzbin'))
|
|
cfg.newzbin_password.set(kwargs.get('password_newzbin'))
|
|
cfg.newzbin_bookmarks.set(kwargs.get('newzbin_bookmarks'))
|
|
cfg.newzbin_unbookmark.set(kwargs.get('newzbin_unbookmark'))
|
|
cfg.bookmark_rate.set(kwargs.get('bookmark_rate'))
|
|
|
|
cfg.matrix_username.set(kwargs.get('matrix_username'))
|
|
cfg.matrix_apikey.set(kwargs.get('matrix_apikey'))
|
|
cfg.matrix_del_bookmark.set(kwargs.get('matrix_del_bookmark'))
|
|
|
|
config.save_config()
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def saveMatrix(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
|
|
cfg.matrix_username.set(kwargs.get('matrix_username'))
|
|
cfg.matrix_apikey.set(kwargs.get('matrix_apikey'))
|
|
cfg.matrix_del_bookmark.set(kwargs.get('matrix_del_bookmark'))
|
|
|
|
config.save_config()
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
|
|
@cherrypy.expose
|
|
def getBookmarks(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
Bookmarks.do.run()
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def showBookmarks(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
self.__bookmarks = Bookmarks.do.bookmarksList()
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def hideBookmarks(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
self.__bookmarks = []
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
#------------------------------------------------------------------------------
|
|
|
|
class ConfigCats(object):
|
|
def __init__(self, web_dir, root, prim):
|
|
self.__root = root
|
|
self.__web_dir = web_dir
|
|
self.__prim = prim
|
|
|
|
@cherrypy.expose
|
|
def index(self, **kwargs):
|
|
if cfg.configlock():
|
|
return Protected()
|
|
|
|
conf, pnfo_list, bytespersec = build_header(self.__prim)
|
|
|
|
if cfg.newzbin_username() and cfg.newzbin_password():
|
|
conf['newzbinDetails'] = True
|
|
|
|
conf['script_list'] = list_scripts(default=True)
|
|
|
|
categories = config.get_categories()
|
|
conf['have_cats'] = len(categories) > 1
|
|
conf['defdir'] = cfg.complete_dir.get_path()
|
|
|
|
|
|
empty = { 'name':'', 'pp':'-1', 'script':'', 'dir':'', 'newzbin':'', 'priority':DEFAULT_PRIORITY }
|
|
slotinfo = []
|
|
for cat in sorted(categories.keys()):
|
|
slot = categories[cat].get_dict()
|
|
slot['name'] = cat
|
|
slotinfo.append(slot)
|
|
slotinfo.insert(1, empty)
|
|
conf['slotinfo'] = slotinfo
|
|
|
|
template = Template(file=os.path.join(self.__web_dir, 'config_cat.tmpl'),
|
|
filter=FILTER, searchList=[conf], compilerSettings=DIRECTIVES)
|
|
return template.respond()
|
|
|
|
@cherrypy.expose
|
|
def delete(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
kwargs['section'] = 'categories'
|
|
kwargs['keyword'] = kwargs.get('name')
|
|
del_from_section(kwargs)
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def save(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
|
|
name = kwargs.get('name')
|
|
if name == '*':
|
|
newname = name
|
|
else:
|
|
newname = kwargs.get('newname', '').strip(' []')
|
|
if newname:
|
|
if name:
|
|
config.delete('categories', name)
|
|
name = newname.lower()
|
|
if kwargs.get('dir'):
|
|
kwargs['dir'] = platform_encode(kwargs['dir'])
|
|
config.ConfigCat(name, kwargs)
|
|
|
|
config.save_config()
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
|
|
SORT_LIST = ( \
|
|
'enable_tv_sorting', 'tv_sort_string', 'tv_categories',
|
|
'enable_movie_sorting', 'movie_sort_string', 'movie_sort_extra', 'movie_extra_folder',
|
|
'enable_date_sorting', 'date_sort_string', 'movie_categories', 'date_categories'
|
|
)
|
|
|
|
#------------------------------------------------------------------------------
|
|
class ConfigSorting(object):
|
|
def __init__(self, web_dir, root, prim):
|
|
self.__root = root
|
|
self.__web_dir = web_dir
|
|
self.__prim = prim
|
|
|
|
@cherrypy.expose
|
|
def index(self, **kwargs):
|
|
if cfg.configlock():
|
|
return Protected()
|
|
|
|
conf, pnfo_list, bytespersec = build_header(self.__prim)
|
|
conf['complete_dir'] = cfg.complete_dir.get_path()
|
|
|
|
for kw in SORT_LIST:
|
|
conf[kw] = config.get_config('misc', kw)()
|
|
conf['cat_list'] = list_cats(True)
|
|
#tvSortList = []
|
|
|
|
template = Template(file=os.path.join(self.__web_dir, 'config_sorting.tmpl'),
|
|
filter=FILTER, searchList=[conf], compilerSettings=DIRECTIVES)
|
|
return template.respond()
|
|
|
|
@cherrypy.expose
|
|
def saveSorting(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
|
|
try:
|
|
kwargs['movie_categories'] = kwargs['movie_cat']
|
|
except:
|
|
pass
|
|
try:
|
|
kwargs['date_categories'] = kwargs['date_cat']
|
|
except:
|
|
pass
|
|
try:
|
|
kwargs['tv_categories'] = kwargs['tv_cat']
|
|
except:
|
|
pass
|
|
|
|
for kw in SORT_LIST:
|
|
item = config.get_config('misc', kw)
|
|
value = platform_encode(kwargs.get(kw))
|
|
msg = item.set(value)
|
|
if msg:
|
|
return badParameterResponse(msg)
|
|
|
|
config.save_config()
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
|
|
#------------------------------------------------------------------------------
|
|
|
|
class ConnectionInfo(object):
|
|
def __init__(self, web_dir, root, prim):
|
|
self.__root = root
|
|
self.__web_dir = web_dir
|
|
self.__prim = prim
|
|
self.__lastmail = None
|
|
|
|
@cherrypy.expose
|
|
def index(self, **kwargs):
|
|
header, pnfo_list, bytespersec = build_header(self.__prim)
|
|
|
|
header['logfile'] = sabnzbd.LOGFILE
|
|
header['weblogfile'] = sabnzbd.WEBLOGFILE
|
|
header['loglevel'] = str(cfg.log_level())
|
|
|
|
header['lastmail'] = self.__lastmail
|
|
|
|
header['folders'] = sabnzbd.nzbqueue.scan_jobs(all=False, action=False)
|
|
header['configfn'] = config.get_filename()
|
|
|
|
|
|
header['servers'] = []
|
|
|
|
for server in Downloader.do.servers[:]:
|
|
busy = []
|
|
connected = 0
|
|
|
|
for nw in server.idle_threads[:]:
|
|
if nw.connected:
|
|
connected += 1
|
|
|
|
for nw in server.busy_threads[:]:
|
|
article = nw.article
|
|
art_name = ""
|
|
nzf_name = ""
|
|
nzo_name = ""
|
|
|
|
if article:
|
|
nzf = article.nzf
|
|
nzo = nzf.nzo
|
|
|
|
art_name = xml_name(article.article)
|
|
#filename field is not always present
|
|
try:
|
|
nzf_name = xml_name(nzf.filename)
|
|
except: #attribute error
|
|
nzf_name = xml_name(nzf.subject)
|
|
nzo_name = xml_name(nzo.final_name)
|
|
|
|
busy.append((nw.thrdnum, art_name, nzf_name, nzo_name))
|
|
|
|
if nw.connected:
|
|
connected += 1
|
|
|
|
if server.warning and not (connected or server.errormsg):
|
|
connected = unicoder(server.warning)
|
|
|
|
if server.request and not server.info:
|
|
connected = T(' Resolving address')
|
|
busy.sort()
|
|
|
|
header['servers'].append((server.id, '', connected, busy, server.ssl,
|
|
server.active, server.errormsg, server.fillserver, server.optional))
|
|
|
|
wlist = []
|
|
for w in sabnzbd.GUIHANDLER.content():
|
|
w = w.replace('WARNING', Ta('WARNING:')).replace('ERROR', Ta('ERROR:'))
|
|
wlist.append(xml_name(w))
|
|
header['warnings'] = wlist
|
|
|
|
template = Template(file=os.path.join(self.__web_dir, 'connection_info.tmpl'),
|
|
filter=FILTER, searchList=[header], compilerSettings=DIRECTIVES)
|
|
return template.respond()
|
|
|
|
@cherrypy.expose
|
|
def disconnect(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
Downloader.do.disconnect()
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def testmail(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
logging.info("Sending testmail")
|
|
pack = {}
|
|
pack['download'] = ['action 1', 'action 2']
|
|
pack['unpack'] = ['action 1', 'action 2']
|
|
|
|
self.__lastmail = emailer.endjob('I had a d\xe8ja vu', 123, 'unknown', True,
|
|
os.path.normpath(os.path.join(cfg.complete_dir.get_path(), '/unknown/I had a d\xe8ja vu')),
|
|
str(123*MEBI), pack, 'my_script', 'Line 1\nLine 2\nLine 3\nd\xe8ja vu\n', 0)
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def showlog(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
try:
|
|
sabnzbd.LOGHANDLER.flush()
|
|
except:
|
|
pass
|
|
return cherrypy.lib.static.serve_file(sabnzbd.LOGFILE, "application/x-download", "attachment")
|
|
|
|
@cherrypy.expose
|
|
def showweb(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
if sabnzbd.WEBLOGFILE:
|
|
return cherrypy.lib.static.serve_file(sabnzbd.WEBLOGFILE, "application/x-download", "attachment")
|
|
else:
|
|
return "Web logging is off!"
|
|
|
|
@cherrypy.expose
|
|
def clearwarnings(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
sabnzbd.GUIHANDLER.clear()
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def change_loglevel(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
cfg.log_level.set(kwargs.get('loglevel'))
|
|
config.save_config()
|
|
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def unblock_server(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
Downloader.do.unblock(kwargs.get('server'))
|
|
# Short sleep so that UI shows new server status
|
|
time.sleep(1.0)
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def delete(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
orphan_delete(kwargs)
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
@cherrypy.expose
|
|
def add(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
orphan_add(kwargs)
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
|
|
def Protected():
|
|
return badParameterResponse("Configuration is locked")
|
|
|
|
def badParameterResponse(msg):
|
|
"""Return a html page with error message and a 'back' button
|
|
"""
|
|
return '''
|
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
|
|
<html>
|
|
<head>
|
|
<title>SABnzbd+ %s - %s/title>
|
|
</head>
|
|
<body>
|
|
<h3>%s</h3>
|
|
%s
|
|
<br><br>
|
|
<FORM><INPUT TYPE="BUTTON" VALUE="%s" ONCLICK="history.go(-1)"></FORM>
|
|
</body>
|
|
</html>
|
|
''' % (sabnzbd.__version__, T('ERROR:'), T('Incorrect parameter'), unicoder(msg), T('Back'))
|
|
|
|
def ShowFile(name, path):
|
|
"""Return a html page listing a file and a 'back' button
|
|
"""
|
|
try:
|
|
f = open(path, "r")
|
|
msg = TRANS(f.read())
|
|
f.close()
|
|
except:
|
|
msg = "FILE NOT FOUND\n"
|
|
|
|
return '''
|
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
|
|
<html>
|
|
<head>
|
|
<title>%s</title>
|
|
</head>
|
|
<body>
|
|
<FORM><INPUT TYPE="BUTTON" VALUE="%s" ONCLICK="history.go(-1)"></FORM>
|
|
<h3>%s</h3>
|
|
<code><pre>
|
|
%s
|
|
</pre></code><br/><br/>
|
|
</body>
|
|
</html>
|
|
''' % (name, T('Back'), name, escape(msg))
|
|
|
|
def ShowString(name, string):
|
|
"""Return a html page listing a file and a 'back' button
|
|
"""
|
|
try:
|
|
msg = TRANS(string)
|
|
except:
|
|
msg = "Encoding Error\n"
|
|
|
|
return '''
|
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
|
|
<html>
|
|
<head>
|
|
<title>%s</title>
|
|
</head>
|
|
<body>
|
|
<FORM><INPUT TYPE="BUTTON" VALUE="%s" ONCLICK="history.go(-1)"></FORM>
|
|
<h3>%s</h3>
|
|
<code><pre>
|
|
%s
|
|
</pre></code><br/><br/>
|
|
</body>
|
|
</html>
|
|
''' % (xml_name(name), T('Back'), xml_name(name), escape(unicoder(msg)))
|
|
|
|
|
|
def ShowOK(url):
|
|
return '''
|
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
|
|
<html>
|
|
<head>
|
|
<title>%s</title>
|
|
</head>
|
|
<body>
|
|
<FORM><INPUT TYPE="BUTTON" VALUE="%s" ONCLICK="history.go(-1)"></FORM>
|
|
<br/><br/>
|
|
%s
|
|
<br/><br/>
|
|
</body>
|
|
</html>
|
|
''' % (escape(url), T('Back'), T('Job "%s" was re-added to the queue') % escape(url))
|
|
|
|
|
|
|
|
def GetRssLog(feed):
|
|
def make_item(job):
|
|
url = job.get('url', '')
|
|
title = job.get('title', '')
|
|
if url.isdigit():
|
|
title = '<a href="https://www.newzbin.com/browse/post/%s/" target="_blank">%s</a>' % (url, title)
|
|
else:
|
|
title = xml_name(title)
|
|
if 'nzbindex.nl/' in url or 'nzbindex.com/' in url or 'nzbclub.com/' in url:
|
|
nzbname = ""
|
|
else:
|
|
nzbname = xml_name(title)
|
|
return url, \
|
|
title, \
|
|
'*' * int(job.get('status', '').endswith('*')), \
|
|
job.get('rule', 0), \
|
|
nzbname
|
|
|
|
jobs = sabnzbd.rss.show_result(feed)
|
|
names = jobs.keys()
|
|
# Sort in the order the jobs came from the feed
|
|
names.sort(lambda x, y: jobs[x].get('order', 0) - jobs[y].get('order', 0))
|
|
|
|
done = [xml_name(jobs[job]['title']) for job in names if jobs[job]['status'][0] == 'D']
|
|
good = [make_item(jobs[job]) for job in names if jobs[job]['status'][0] == 'G']
|
|
bad = [make_item(jobs[job]) for job in names if jobs[job]['status'][0] == 'B']
|
|
|
|
return done, good, bad
|
|
|
|
def ShowRssLog(feed, all):
|
|
"""Return a html page listing an RSS log and a 'back' button
|
|
"""
|
|
jobs = sabnzbd.rss.show_result(feed)
|
|
names = jobs.keys()
|
|
# Sort in the order the jobs came from the feed
|
|
names.sort(lambda x, y: jobs[x].get('order', 0) - jobs[y].get('order', 0))
|
|
|
|
qfeed = escape(feed.replace('/','%2F').replace('?', '%3F'))
|
|
|
|
doneStr = []
|
|
for x in names:
|
|
job = jobs[x]
|
|
if job['status'][0] == 'D':
|
|
doneStr.append('%s<br/>' % xml_name(job['title']))
|
|
|
|
goodStr = []
|
|
for x in names:
|
|
job = jobs[x]
|
|
if job['status'][0] == 'G':
|
|
goodStr.append('')
|
|
|
|
badStr = []
|
|
for x in names:
|
|
job = jobs[x]
|
|
if job['status'][0] == 'B':
|
|
badStr.append('')
|
|
|
|
if all:
|
|
return '''
|
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
|
|
<html>
|
|
<head>
|
|
<title>%s</title>
|
|
</head>
|
|
<body>
|
|
<form>
|
|
<input type="submit" onclick="this.form.action='.'; this.form.submit(); return false;" value="%s"/>
|
|
</form>
|
|
<h3>%s</h3>
|
|
%s<br/><br/>
|
|
<b>%s</b><br/>
|
|
%s
|
|
<br/>
|
|
<b>%s</b><br/>
|
|
%s
|
|
<br/>
|
|
<b>%s</b><br/>
|
|
%s
|
|
<br/>
|
|
</body>
|
|
</html>
|
|
''' % (escape(feed), T('Back'), escape(feed), T('Jobs marked with a \'*\' will not be automatically downloaded.'), T('Matched'), \
|
|
''.join(goodStr), T('Not matched'), ''.join(badStr), T('Downloaded'), ''.join(doneStr))
|
|
else:
|
|
return '''
|
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
|
|
<html>
|
|
<head>
|
|
<title>%s</title>
|
|
</head>
|
|
<body>
|
|
<form>
|
|
<input type="submit" onclick="this.form.action='.'; this.form.submit(); return false;" value="%s"/>
|
|
</form>
|
|
<h3>%s</h3>
|
|
<b>%s</b><br/>
|
|
%s
|
|
<br/>
|
|
</body>
|
|
</html>
|
|
''' % (escape(feed), T('Back'), escape(feed), T('Downloaded so far'), ''.join(doneStr))
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------
|
|
LIST_EMAIL = (
|
|
'email_endjob', 'email_full',
|
|
'email_server', 'email_to', 'email_from',
|
|
'email_account', 'email_pwd', 'email_dir', 'email_rss'
|
|
)
|
|
|
|
class ConfigEmail(object):
|
|
def __init__(self, web_dir, root, prim):
|
|
self.__root = root
|
|
self.__web_dir = web_dir
|
|
self.__prim = prim
|
|
|
|
@cherrypy.expose
|
|
def index(self, **kwargs):
|
|
if cfg.configlock():
|
|
return Protected()
|
|
|
|
conf, pnfo_list, bytespersec = build_header(self.__prim)
|
|
|
|
conf['my_home'] = sabnzbd.DIR_HOME
|
|
conf['my_lcldata'] = sabnzbd.DIR_LCLDATA
|
|
|
|
for kw in LIST_EMAIL:
|
|
conf[kw] = config.get_config('misc', kw).get_string()
|
|
|
|
template = Template(file=os.path.join(self.__web_dir, 'config_email.tmpl'),
|
|
filter=FILTER, searchList=[conf], compilerSettings=DIRECTIVES)
|
|
return template.respond()
|
|
|
|
@cherrypy.expose
|
|
def saveEmail(self, **kwargs):
|
|
msg = check_session(kwargs)
|
|
if msg: return msg
|
|
|
|
for kw in LIST_EMAIL:
|
|
msg = config.get_config('misc', kw).set(platform_encode(kwargs.get(kw)))
|
|
if msg:
|
|
return badParameterResponse(T('Incorrect value for %s: %s') % (kw, unicoder(msg)))
|
|
|
|
config.save_config()
|
|
raise dcRaiser(self.__root, kwargs)
|
|
|
|
|
|
def rss_history(url, limit=50, search=None):
|
|
url = url.replace('rss','')
|
|
|
|
youngest = None
|
|
|
|
rss = RSS()
|
|
rss.channel.title = "SABnzbd History"
|
|
rss.channel.description = "Overview of completed downloads"
|
|
rss.channel.link = "http://sourceforge.net/projects/sabnzbdplus/"
|
|
rss.channel.language = "en"
|
|
|
|
items, fetched_items, max_items = build_history(limit=limit, search=search)
|
|
|
|
for history in items:
|
|
item = Item()
|
|
|
|
item.pubDate = std_time(history['completed'])
|
|
item.title = history['name']
|
|
|
|
if not youngest:
|
|
youngest = history['completed']
|
|
elif history['completed'] < youngest:
|
|
youngest = history['completed']
|
|
|
|
if history['report']:
|
|
item.link = "https://www.newzbin.com/browse/post/%s/" % history['report']
|
|
elif history['url_info']:
|
|
item.link = history['url_info']
|
|
else:
|
|
item.link = url
|
|
|
|
stageLine = []
|
|
for stage in history['stage_log']:
|
|
stageLine.append("<tr><dt>Stage %s</dt>" % stage['name'])
|
|
actions = []
|
|
for action in stage['actions']:
|
|
actions.append("<dd>%s</dd>" % (action))
|
|
actions.sort()
|
|
actions.reverse()
|
|
for act in actions:
|
|
stageLine.append(act)
|
|
stageLine.append("</tr>")
|
|
item.description = ''.join(stageLine)
|
|
rss.addItem(item)
|
|
|
|
rss.channel.lastBuildDate = std_time(youngest)
|
|
rss.channel.pubDate = std_time(time.time())
|
|
|
|
return rss.write()
|
|
|
|
|
|
def rss_warnings():
|
|
""" Return an RSS feed with last warnings/errors
|
|
"""
|
|
rss = RSS()
|
|
rss.channel.title = "SABnzbd Warnings"
|
|
rss.channel.description = "Overview of warnings/errors"
|
|
rss.channel.link = "http://sourceforge.net/projects/sabnzbdplus/"
|
|
rss.channel.language = "en"
|
|
|
|
for warn in sabnzbd.GUIHANDLER.content():
|
|
item = Item()
|
|
item.title = warn
|
|
rss.addItem(item)
|
|
|
|
rss.channel.lastBuildDate = std_time(time.time())
|
|
rss.channel.pubDate = rss.channel.lastBuildDate
|
|
return rss.write()
|
|
|