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.
 
 
 
 
 

780 lines
26 KiB

#!/usr/bin/python -OO
# Copyright 2008 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 V2
# as published by the Free Software Foundation.
#
# 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.
import sys
if sys.hexversion < 0x020403F0:
print "Sorry, requires Python 2.4.3 or higher."
exit(1)
import logging
import logging.handlers
import os
import getopt
import signal
import re
import glob
import socket
if os.name=='nt':
import platform
try:
import Cheetah
if Cheetah.Version[0] != '2':
raise ValueError
except:
print "Sorry, requires Python module Cheetah 2.0rc7 or higher."
exit(1)
import cherrypy
import sabnzbd
from sabnzbd.utils.configobj import ConfigObj, ConfigObjError
from sabnzbd.__init__ import check_setting_str, check_setting_int, dir_setup
from sabnzbd.interface import *
from sabnzbd.constants import *
from sabnzbd.newsunpack import find_programs
from sabnzbd.misc import Get_User_ShellFolders, save_configfile, launch_a_browser, from_units, \
check_latest_version, Panic_Templ, Panic_Port, Panic_FWall, Panic, ExitSab, \
decodePassword, Notify, SplitHost, ConvertVersion
from threading import Thread
cfg = {}
#------------------------------------------------------------------------------
signal.signal(signal.SIGINT, sabnzbd.sig_handler)
signal.signal(signal.SIGTERM, sabnzbd.sig_handler)
try:
import win32api
win32api.SetConsoleCtrlHandler(sabnzbd.sig_handler, True)
except ImportError:
if os.name == 'nt':
print "Sorry, requires Python module PyWin32."
exit(1)
#------------------------------------------------------------------------------
class guiHandler(logging.Handler):
"""
Logging handler collects the last warnings/errors/exceptions
to be displayed in the web-gui
"""
def __init__(self, size):
"""
Initializes the handler
"""
logging.Handler.__init__(self)
self.size = size
self.store = []
def emit(self, record):
"""
Emit a record by adding it to our private queue
"""
if len(self.store) >= self.size:
# Loose the oldest record
self.store.pop(0)
self.store.append(self.format(record))
def clear(self):
self.store = []
def count(self):
return len(self.store)
def last(self):
if self.store:
return self.store[len(self.store)-1]
else:
return ""
def content(self):
"""
Return an array with last records
"""
return self.store
#------------------------------------------------------------------------------
def print_help():
print
print "Usage: %s [-f <configfile>] <other options>" % sabnzbd.MY_NAME
print
print "Options marked [*] are stored in the config file"
print
print "Options:"
print " -f --config-file <ini> Location of config file"
print " -s --server <srv:port> Listen on server:port [*]"
print " -t --templates <templ> Template directory [*]"
print " -2 --template2 <templ> Secondary template dir [*]"
print
print " -l --logging <0..2> Set logging level (0= least, 2= most) [*]"
print " -w --weblogging <0..1> Set cherrypy logging (0= off, 1= on) [*]"
print
print " -b --browser <0..1> Auto browser launch (0= off, 1= on) [*]"
if os.name != 'nt':
print " -d --daemon Fork daemon process"
print " --permissions Set the chmod mode (e.g. o=rwx,g=rwx) [*]"
else:
print " -d --daemon Use when run as a service"
print
print " --force Discard web-port timeout (see Wiki!)"
print " -h --help Print this message"
print " -v --version Print version information"
print " -c --clean Remove queue, cache and logs"
print " -p --pause Start in paused mode"
def print_version():
print """
%s-%s
Copyright (C) 2008, The SABnzbd-Team <team@sabnzbd.org>
SABnzbd comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it
under certain conditions. It is licensed under the
GNU GENERAL PUBLIC LICENSE Version 2.
""" % (sabnzbd.MY_NAME, sabnzbd.__version__)
def daemonize():
try:
pid = os.fork()
if pid > 0:
sys.exit(0)
except OSError:
print "fork() failed"
sys.exit(1)
os.chdir(sabnzbd.DIR_PROG)
os.setsid()
# Make sure I can read my own files and shut out others
prev= os.umask(0)
os.umask(prev and int('077',8))
try:
pid = os.fork()
if pid > 0:
sys.exit(0)
except OSError:
print "fork() failed"
sys.exit(1)
dev_null = file('/dev/null', 'r')
os.dup2(dev_null.fileno(), sys.stdin.fileno())
def Bail_Out(browserhost, cherryport):
"""Abort program because of CherryPy troubles
"""
logging.error("Failed to start web-interface")
Panic_Port(browserhost, cherryport)
sabnzbd.halt()
ExitSab(2)
def Web_Template(key, defweb, wdir):
""" Determine a correct web template set,
return full template path
"""
if wdir == None:
try:
wdir = cfg['misc'][key]
except:
wdir = ''
if not wdir:
wdir = defweb
cfg['misc'][key] = wdir
if not wdir:
# No default value defined, accept empty path
return ''
full_dir = real_path(sabnzbd.DIR_INTERFACES, wdir)
full_main = real_path(full_dir, DEF_MAIN_TMPL)
logging.info("Web dir is %s", full_dir)
if not os.path.exists(full_main):
logging.warning('Cannot find web template: %s, trying standard template', full_main)
full_dir = real_path(sabnzbd.DIR_INTERFACES, DEF_STDINTF)
full_main = real_path(full_dir, DEF_MAIN_TMPL)
if not os.path.exists(full_main):
logging.exception('Cannot find standard template: %s', full_dir)
Panic_Templ(full_dir)
ExitSab(1)
return real_path(full_dir, "templates")
def CheckColor(color, web_dir):
""" Check existence of color-scheme """
if color and os.path.exists(os.path.join(web_dir,'static/stylesheets/colorschemes/'+color+'.css')):
return color
else:
return ''
def GetProfileInfo(vista):
""" Get the default data locations
"""
ok = False
if sabnzbd.DAEMON:
# In daemon mode, do not try to access the user profile
# just assume that everything defaults to the program dir
sabnzbd.DIR_APPDATA = sabnzbd.DIR_PROG
sabnzbd.DIR_LCLDATA = sabnzbd.DIR_PROG
sabnzbd.DIR_HOME = sabnzbd.DIR_PROG
if os.name == 'nt':
# Ignore Win23 "logoff" signal
# This should work, but it doesn't
# Instead the signal_handler will ignore the "logoff" signal
signal.signal(5, signal.SIG_IGN)
ok = True
elif os.name == 'nt':
specials = Get_User_ShellFolders()
try:
sabnzbd.DIR_APPDATA = '%s\\%s' % (specials['AppData'], DEF_WORKDIR)
sabnzbd.DIR_LCLDATA = '%s\\%s' % (specials['Local AppData'], DEF_WORKDIR)
sabnzbd.DIR_HOME = specials['Personal']
ok = True
except:
try:
if vista:
root = os.environ['AppData']
user = os.environ['USERPROFILE']
sabnzbd.DIR_APPDATA = '%s\\%s' % (root.replace('\\Roaming', '\\Local'), DEF_WORKDIR)
sabnzbd.DIR_HOME = '%s\\Documents' % user
else:
root = os.environ['USERPROFILE']
sabnzbd.DIR_APPDATA = '%s\\%s' % (root, DEF_WORKDIR)
sabnzbd.DIR_HOME = root
try:
# Conversion to 8bit ASCII required for CherryPy
sabnzbd.DIR_APPDATA = sabnzbd.DIR_APPDATA.encode('latin-1')
sabnzbd.DIR_HOME = sabnzbd.DIR_HOME.encode('latin-1')
ok = True
except:
# If unconvertible characters exist, use MSDOS name
try:
sabnzbd.DIR_APPDATA = win32api.GetShortPathName(sabnzbd.DIR_APPDATA)
sabnzbd.DIR_HOME = win32api.GetShortPathName(sabnzbd.DIR_HOME)
ok = True
except:
pass
sabnzbd.DIR_LCLDATA = sabnzbd.DIR_APPDATA
except:
pass
else:
# Unix/Linux/OSX
sabnzbd.DIR_APPDATA = '%s/.%s' % (os.environ['HOME'], DEF_WORKDIR)
sabnzbd.DIR_LCLDATA = sabnzbd.DIR_APPDATA
sabnzbd.DIR_HOME = os.environ['HOME']
ok = True
if not ok:
Panic("Cannot access the user profile.",
"Please start with sabnzbd.ini file in another location")
ExitSab(2)
def main():
global cfg
sabnzbd.MY_FULLNAME = os.path.normpath(os.path.abspath(sys.argv[0]))
sabnzbd.MY_NAME = os.path.basename(sabnzbd.MY_FULLNAME)
sabnzbd.DIR_PROG = os.path.dirname(sabnzbd.MY_FULLNAME)
sabnzbd.DIR_INTERFACES = real_path(sabnzbd.DIR_PROG, DEF_INTERFACES)
# Need console logging for SABnzbd.py and SABnzbd-console.exe
consoleLogging = (not hasattr(sys, "frozen")) or (sabnzbd.MY_NAME.lower().find('-console') > 0)
LOGLEVELS = [ logging.WARNING, logging.INFO, logging.DEBUG ]
# Setup primary logging to prevent default console logging
gui_log = guiHandler(MAX_WARNINGS)
gui_log.setLevel(logging.WARNING)
format_gui = '%(asctime)s\n%(levelname)s\n%(message)s'
gui_log.setFormatter(logging.Formatter(format_gui))
sabnzbd.GUIHANDLER = gui_log
# Create logger
logger = logging.getLogger('')
logger.setLevel(logging.WARNING)
logger.addHandler(gui_log)
try:
opts, args = getopt.getopt(sys.argv[1:], "phdvncu:w:l:s:f:t:b:2:",
['pause', 'help', 'daemon', 'nobrowser', 'clean', 'logging=', \
'weblogging=', 'umask=', 'server=', 'templates', 'permissions=', \
'template2', 'browser=', 'config-file=', 'delay=', 'force', 'version'])
except getopt.GetoptError:
print_help()
ExitSab(2)
fork = False
pause = False
f = None
cherryhost = None
cherryport = None
cherrypylogging = None
sabnzbd.AUTOBROWSER = None
clean_up = False
logging_level = None
umask = None
web_dir = None
web_dir2 = None
delay = 0.0
vista = False
vista64 = False
force_web = False
for o, a in opts:
if (o in ('-d', '--daemon')):
if os.name != 'nt':
fork = True
sabnzbd.AUTOBROWSER = 0
sabnzbd.DAEMON = True
consoleLogging = False
if o in ('-h', '--help'):
print_help()
ExitSab(0)
if o in ('-f', '--config-file'):
f = a
if o in ('-t', '--templates'):
web_dir = a
if o in ('-2', '--template2'):
web_dir2 = a
if o in ('-s', '--server'):
(cherryhost, cherryport) = SplitHost(a)
if o in ('-n', '--nobrowser'):
sabnzbd.AUTOBROWSER = 0
if o in ('-b', '--browser'):
try:
sabnzbd.AUTOBROWSER = int(a)
except:
sabnzbd.AUTOBROWSER = 1
if o in ('-c', '--clean'):
clean_up= True
if o in ('-w', '--weblogging'):
try:
cherrypylogging = int(a)
except:
cherrypylogging = -1
if cherrypylogging < 0 or cherrypylogging > 1:
print_help()
ExitSab(1)
if o in ('-l', '--logging'):
try:
logging_level = int(a)
except:
logging_level = -1
if logging_level < 0 or logging_level > 2:
print_help()
ExitSab(1)
if o in ('--permissions'):
umask = a
if o in ('-v', '--version'):
print_version()
ExitSab(0)
if o in ('-p', '--pause'):
pause = True
#if o in ('--delay'):
# For debugging of memory leak only!!
#try:
# delay = float(a)
#except:
# pass
if o in ('--force'):
force_web = True
# Detect Vista or higher
if os.name == 'nt':
if platform.platform().find('Windows-32bit') >= 0:
vista = True
vista64 = 'ProgramFiles(x86)' in os.environ
if f:
# INI file given, simplest case
f = os.path.normpath(os.path.abspath(f))
else:
# No ini file given, need profile data
GetProfileInfo(vista)
# Find out where INI file is
f = os.path.abspath(sabnzbd.DIR_PROG + '/' + DEF_INI_FILE)
if not os.path.exists(f):
f = os.path.abspath(sabnzbd.DIR_LCLDATA + '/' + DEF_INI_FILE)
# If INI file at non-std location, then use program dir as $HOME
if sabnzbd.DIR_LCLDATA != os.path.dirname(f):
sabnzbd.DIR_HOME = os.path.dirname(f)
# All system data dirs are relative to the place we found the INI file
sabnzbd.DIR_LCLDATA = os.path.dirname(f)
if not os.path.exists(f):
# No file found, create default INI file
try:
if not os.path.exists(sabnzbd.DIR_LCLDATA):
os.makedirs(sabnzbd.DIR_LCLDATA)
fp = open(f, "w")
fp.write("__version__=%s\n[misc]\n[logging]\n" % CONFIG_VERSION)
fp.close()
except:
Panic('Cannot create file "%s".' % f, 'Check specified INI file location.')
ExitSab(1)
try:
cfg = ConfigObj(f)
try:
my_version = cfg['__version__']
except:
my_version = CONFIG_VERSION
cfg['__version__'] = my_version
except ConfigObjError, strerror:
Panic('"%s" is not a valid configuration file.' % f, \
'Specify a correct file or delete this file.')
ExitSab(1)
if cherrypylogging == None:
cherrypylogging = bool(check_setting_int(cfg, 'logging', 'enable_cherrypy_logging', 1))
else:
cfg['logging']['enable_cherrypy_logging'] = cherrypylogging
if logging_level == None:
logging_level = check_setting_int(cfg, 'logging', 'log_level', DEF_LOGLEVEL)
if logging_level > 2:
logging_level = 2
else:
cfg['logging']['log_level'] = logging_level
ver, testRelease = ConvertVersion(sabnzbd.__version__)
if testRelease:
logging_level = 2
cherrypylogging = True
my_logdir = dir_setup(cfg, 'log_dir', sabnzbd.DIR_LCLDATA, DEF_LOG_DIR)
if fork and not my_logdir:
print "Error:"
print "I refuse to fork without a log directory!"
sys.exit()
logdir = ""
logdir = dir_setup(cfg, 'log_dir', sabnzbd.DIR_LCLDATA, DEF_LOG_DIR)
if clean_up:
xlist= glob.glob(logdir + '/*')
for x in xlist:
if x.find(RSS_FILE_NAME) < 0:
os.remove(x)
try:
sabnzbd.LOGFILE = os.path.join(logdir, DEF_LOG_FILE)
logsize = check_setting_str(cfg, 'logging', 'max_log_size', '5M')
logsize = int(from_units(logsize))
rollover_log = logging.handlers.RotatingFileHandler(\
sabnzbd.LOGFILE, 'a+',
logsize,
check_setting_int(cfg, 'logging', 'log_backups', 5))
rollover_log.setLevel(LOGLEVELS[logging_level])
format = '%(asctime)s::%(levelname)s::%(message)s'
rollover_log.setFormatter(logging.Formatter(format))
sabnzbd.LOGHANDLER = rollover_log
logger.addHandler(rollover_log)
logger.setLevel(LOGLEVELS[logging_level])
except IOError:
print "Error:"
print "Can't write to logfile"
ExitSab(2)
if fork:
try:
sys.stderr.fileno
sys.stdout.fileno
my_logpath = dir_setup(cfg, 'log_dir', sabnzbd.DIR_LCLDATA, DEF_LOG_DIR)
ol_path = os.path.join(my_logpath, DEF_LOG_ERRFILE)
out_log = file(ol_path, 'a+', 0)
sys.stderr.flush()
sys.stdout.flush()
os.dup2(out_log.fileno(), sys.stderr.fileno())
os.dup2(out_log.fileno(), sys.stdout.fileno())
except AttributeError:
pass
else:
try:
sys.stderr.fileno
sys.stdout.fileno
if consoleLogging:
console = logging.StreamHandler()
console.setLevel(LOGLEVELS[logging_level])
console.setFormatter(logging.Formatter(format))
logger.addHandler(console)
except AttributeError:
pass
logging.info('--------------------------------')
logging.info('%s-%s (rev=%s)', sabnzbd.MY_NAME, sabnzbd.__version__, sabnzbd.__baseline__)
if os.name == 'nt':
suffix = ''
if vista: suffix = ' (=Vista)'
if vista64: suffix = ' (=Vista64)'
logging.info('Platform=%s%s Class=%s', platform.platform(), suffix, os.name)
else:
logging.info('Platform = %s', os.name)
logging.info('Python-version = %s', sys.version)
if testRelease:
logging.info('Test release, setting maximum logging levels')
if sabnzbd.AUTOBROWSER == None:
sabnzbd.AUTOBROWSER = bool(check_setting_int(cfg, 'misc', 'auto_browser', 1))
else:
cfg['misc']['auto_browser'] = sabnzbd.AUTOBROWSER
if umask == None:
umask = check_setting_str(cfg, 'misc', 'permissions', '')
if umask:
cfg['misc']['permissions'] = umask
sabnzbd.DEBUG_DELAY = delay
sabnzbd.CFG = cfg
init_ok = sabnzbd.initialize(pause, clean_up, evalSched=True)
if not init_ok:
logging.error('Initializing %s-%s failed, aborting',
sabnzbd.MY_NAME, sabnzbd.__version__)
ExitSab(2)
find_programs(sabnzbd.DIR_PROG)
if sabnzbd.decoder.HAVE_YENC:
logging.info("_yenc module... found!")
else:
if hasattr(sys, "frozen"):
logging.warning("_yenc module... NOT found!")
else:
logging.info("_yenc module... NOT found!")
if sabnzbd.nzbstuff.HAVE_CELEMENTTREE:
logging.info("celementtree module... found!")
else:
logging.info("celementtree module... NOT found!")
if sabnzbd.newsunpack.PAR2_COMMAND:
logging.info("par2 binary... found (%s)", sabnzbd.newsunpack.PAR2_COMMAND)
else:
logging.error("par2 binary... NOT found!")
if sabnzbd.newsunpack.RAR_COMMAND:
logging.info("rar binary... found (%s)", sabnzbd.newsunpack.RAR_COMMAND)
else:
logging.error("rar binary... NOT found")
if sabnzbd.newsunpack.ZIP_COMMAND:
logging.info("unzip binary... found (%s)", sabnzbd.newsunpack.ZIP_COMMAND)
else:
logging.error("unzip binary... NOT found!")
if sabnzbd.newswrapper.HAVE_SSL:
logging.info("pyOpenSSL... found (%s)", sabnzbd.newswrapper.HAVE_SSL)
else:
logging.info("pyOpenSSL... NOT found - try apt-get install python-pyopenssl (SSL is optional)")
if cherryhost == None:
cherryhost = check_setting_str(cfg, 'misc','host', DEF_HOST)
else:
cfg['misc']['host'] = cherryhost
# Get IP address, but discard APIPA/IPV6
# If only APIPA's or IPV6 are found, fall back to localhost
ipv4 = ipv6 = False
localhost = hostip = 'localhost'
try:
info = socket.getaddrinfo(socket.gethostname(), None)
except:
# Hostname does not resolve, use 0.0.0.0
cherryhost = '0.0.0.0'
info = socket.getaddrinfo(localhost, None)
for item in info:
ip = item[4][0]
if ip.find('169.254.') == 0:
pass # Is an APIPA
elif ip.find(':') >= 0:
ipv6 = True
elif ip.find('.') >= 0:
ipv4 = True
hostip = ip
break
if cherryhost == '':
if ipv6 and ipv4:
# To protect Firefox users, use numeric IP
cherryhost = hostip
browserhost = hostip
else:
cherryhost = socket.gethostname()
browserhost = cherryhost
elif cherryhost == '0.0.0.0':
# Just take the gamble for this
cherryhost = ''
browserhost = localhost
elif cherryhost.find('[') >= 0 or cherryhost.find(':') >= 0:
# IPV6
browserhost = cherryhost
elif cherryhost.replace('.', '').isdigit():
# IPV4 numerical
browserhost = cherryhost
elif cherryhost == localhost:
cherryhost = localhost
browserhost = localhost
else:
# If on Vista and/or APIPA, use numerical IP, to help FireFoxers
if ipv6 and ipv4:
cherryhost = hostip
browserhost = cherryhost
# Some systems don't like brackets in numerical ipv6
if cherryhost.find('[') >= 0:
try:
info = socket.getaddrinfo(cherryhost, None)
except:
cherryhost = cherryhost.strip('[]')
if ipv6 and ipv4 and \
(browserhost not in ('localhost', '127.0.0.1', '[::1]', '::1')):
sabnzbd.AMBI_LOCALHOST = True
logging.info("IPV6 has priority on this system, potential Firefox issue")
if ipv6 and ipv4 and cherryhost == '' and os.name == 'nt':
logging.warning("Please be aware the 0.0.0.0 hostname will need an IPv6 address for external access")
if cherryport == None:
if os.name == 'nt':
defport = DEF_PORT_WIN
else:
defport = DEF_PORT_UNIX
cherryport= check_setting_int(cfg, 'misc', 'port', defport)
else:
cfg['misc']['port'] = cherryport
log_dir = dir_setup(cfg, 'log_dir', sabnzbd.DIR_LCLDATA, DEF_LOG_DIR)
os.chdir(sabnzbd.DIR_PROG)
web_dir = Web_Template('web_dir', DEF_STDINTF, web_dir)
web_dir2 = Web_Template('web_dir2', '', web_dir2)
sabnzbd.WEB_DIR = web_dir
sabnzbd.WEB_DIR2 = web_dir2
sabnzbd.WEB_COLOR = CheckColor(sabnzbd.WEB_COLOR, web_dir)
sabnzbd.WEB_COLOR2 = CheckColor(sabnzbd.WEB_COLOR2, web_dir2)
cfg['misc']['web_color'] = sabnzbd.WEB_COLOR
cfg['misc']['web_color2'] = sabnzbd.WEB_COLOR2
sabnzbd.interface.USERNAME = check_setting_str(cfg, 'misc', 'username', '')
sabnzbd.interface.PASSWORD = decodePassword(check_setting_str(cfg, 'misc', 'password', '', False), 'web')
if fork and os.name != 'nt':
daemonize()
# Save the INI file
save_configfile(cfg)
logging.info('Starting %s-%s', sabnzbd.MY_NAME, sabnzbd.__version__)
try:
sabnzbd.start()
except:
logging.exception("Failed to start %s-%s", sabnzbd.MY_NAME, sabnzbd.__version__)
sabnzbd.halt()
cherrylogtoscreen = False
sabnzbd.WEBLOGFILE = None
if cherrypylogging:
if log_dir:
sabnzbd.WEBLOGFILE = os.path.join(log_dir, DEF_LOG_CHERRY);
if not fork:
try:
sys.stderr.fileno
sys.stdout.fileno
cherrylogtoscreen = True
except:
cherrylogtoscreen = False
cherrypy.tree.mount(LoginPage(web_dir, '/sabnzbd/', web_dir2, '/sabnzbd/m/'), '/')
cherrypy.config.update(updateMap={'server.environment': 'production',
'server.socketHost': cherryhost,
'server.socketPort': cherryport,
'server.logToScreen': cherrylogtoscreen,
'server.logFile': sabnzbd.WEBLOGFILE,
'sessionFilter.on': True,
'server.show_tracebacks': True,
'/sabnzbd': {'streamResponse': True},
'/sabnzbd/static': {'staticFilter.on': True, 'staticFilter.dir': os.path.join(web_dir, 'static')},
'/sabnzbd/m': {'streamResponse': True},
'/sabnzbd/m/static': {'staticFilter.on': True, 'staticFilter.dir': os.path.join(web_dir2, 'static')}
})
logging.info('Starting web-interface on %s:%s', cherryhost, cherryport)
sabnzbd.LOGLEVEL = logging_level
try:
cherrypy.server.start(init_only=True)
cherrypy.server.wait()
except cherrypy.NotReady, error:
if str(error) == 'Port not bound.':
if not force_web:
Panic_FWall(vista)
sabnzbd.halt()
ExitSab(2)
else:
Bail_Out(browserhost, cherryport)
except:
Bail_Out(browserhost, cherryport)
launch_a_browser("http://%s:%s/sabnzbd" % (browserhost, cherryport))
Notify("SAB_Launched", None)
# Now's the time to check for a new version
if sabnzbd.VERSION_CHECK:
check_latest_version()
# Have to keep this running, otherwise logging will terminate
while cherrypy.server.ready:
if (not testRelease) and sabnzbd.LOGLEVEL != logging_level:
logging_level = sabnzbd.LOGLEVEL
logger.setLevel(LOGLEVELS[logging_level])
time.sleep(3)
Notify("SAB_Shutdown", None)
if __name__ == '__main__':
main()