Merge pull request #825 from JackDandy/feature/ChangeShutdown

Change consolidate shutdown with restart, improve systemd support, br…
This commit is contained in:
JackDandy 2016-11-13 01:56:14 +00:00 committed by GitHub
commit 6da0f7f7fe
9 changed files with 361 additions and 382 deletions

View file

@ -191,6 +191,7 @@
* Change improve performance and reduce start up time * Change improve performance and reduce start up time
* Fix button "Checkout branch" when stuck on disabled * Fix button "Checkout branch" when stuck on disabled
* Add 'Download Log' to 'Logs & Errors' page * Add 'Download Log' to 'Logs & Errors' page
* Change consolidate shutdown with restart, improve systemd support, bring order to on-init globals
[develop changelog] [develop changelog]
* Change send nzb data to NZBGet for Anizb instead of url * Change send nzb data to NZBGet for Anizb instead of url

View file

@ -80,48 +80,69 @@ class SickGear(object):
sickbeard.events = Events(self.shutdown) sickbeard.events = Events(self.shutdown)
# daemon constants # daemon constants
self.runAsDaemon = False self.run_as_daemon = False
self.CREATEPID = False self.create_pid = False
self.PIDFILE = '' self.pid_file = ''
self.run_as_systemd = False
self.console_logging = False
# webserver constants # webserver constants
self.webserver = None self.webserver = None
self.forceUpdate = False self.force_update = False
self.forcedPort = None self.forced_port = None
self.noLaunch = False self.no_launch = False
self.web_options = None
self.webhost = None
self.start_port = None
self.log_dir = None
@staticmethod @staticmethod
def help_message(): def help_message():
""" """
print help message for commandline options print help message for commandline options
""" """
help_msg = '\n' help_msg = ['']
help_msg += 'Usage: %s <option> <another option>\n' % sickbeard.MY_FULLNAME help_msg += ['Usage: %s <option> <another option>\n' % sickbeard.MY_FULLNAME]
help_msg += '\n' help_msg += ['Options:\n']
help_msg += 'Options:\n'
help_msg += '\n'
help_msg += ' -h --help Prints this message\n'
help_msg += ' -f --forceupdate Force update all shows in the DB (from tvdb) on startup\n'
help_msg += ' -q --quiet Disables logging to console\n'
help_msg += ' --nolaunch Suppress launching web browser on startup\n'
if sys.platform == 'win32': help_tmpl = ' %-10s%-17s%s'
help_msg += ' -d --daemon Running as real daemon is not supported on Windows\n' for ln in [
help_msg += ' On Windows, --daemon is substituted with: --quiet --nolaunch\n' ('-h', '--help', 'Prints this message'),
('-f', '--forceupdate', 'Force update all shows in the DB (from tvdb) on startup'),
('-q', '--quiet', 'Disables logging to console'),
('', '--nolaunch', 'Suppress launching web browser on startup')
]:
help_msg += [help_tmpl % ln]
if 'win32' == sys.platform:
for ln in [
('-d', '--daemon', 'Running as daemon is not supported on Windows'),
('', '', 'On Windows, --daemon is substituted with: --quiet --nolaunch')
]:
help_msg += [help_tmpl % ln]
else: else:
help_msg += ' -d --daemon Run as double forked daemon (includes options --quiet --nolaunch)\n' for ln in [
help_msg += ' --pidfile=<path> Combined with --daemon creates a pidfile (full path including filename)\n' ('-d', '--daemon', 'Run as double forked daemon (includes options --quiet --nolaunch)'),
('-s', '--systemd', 'Run as systemd service (includes options --quiet --nolaunch)'),
('', '--pidfile=<path>', 'Combined with --daemon creates a pidfile (full path including filename)')
]:
help_msg += [help_tmpl % ln]
help_msg += ' -p <port> --port=<port> Override default/configured port to listen on\n' for ln in [
help_msg += ' --datadir=<path> Override folder (full path) as location for\n' ('-p <port>', '--port=<port>', 'Override default/configured port to listen on'),
help_msg += ' storing database, configfile, cache, logfiles \n' ('', '--datadir=<path>', 'Override folder (full path) as location for'),
help_msg += ' Default: %s\n' % sickbeard.PROG_DIR ('', '', 'storing database, configfile, cache, logfiles'),
help_msg += ' --config=<path> Override config filename (full path including filename)\n' ('', '', 'Default: %s' % sickbeard.PROG_DIR),
help_msg += ' to load configuration from \n' ('', '--config=<path>', 'Override config filename (full path including filename)'),
help_msg += ' Default: config.ini in %s or --datadir location\n' % sickbeard.PROG_DIR ('', '', 'to load configuration from'),
help_msg += ' --noresize Prevent resizing of the banner/posters even if PIL is installed\n' ('', '', 'Default: config.ini in %s or --datadir location' % sickbeard.PROG_DIR),
('', '--noresize', 'Prevent resizing of the banner/posters even if PIL is installed')
]:
help_msg += [help_tmpl % ln]
return help_msg return '\n'.join(help_msg)
def start(self): def start(self):
# do some preliminary stuff # do some preliminary stuff
@ -150,23 +171,23 @@ class SickGear(object):
try: try:
# pylint: disable=E1101 # pylint: disable=E1101
# On non-unicode builds this will raise an AttributeError, if encoding type is not valid it throws a LookupError # On non-unicode builds this raises an AttributeError, if encoding type is not valid it throws a LookupError
sys.setdefaultencoding(sickbeard.SYS_ENCODING) sys.setdefaultencoding(sickbeard.SYS_ENCODING)
except: except (StandardError, Exception):
print('Sorry, you MUST add the SickGear folder to the PYTHONPATH environment variable') print('Sorry, you MUST add the SickGear folder to the PYTHONPATH environment variable')
print('or find another way to force Python to use %s for string encoding.' % sickbeard.SYS_ENCODING) print('or find another way to force Python to use %s for string encoding.' % sickbeard.SYS_ENCODING)
sys.exit(1) sys.exit(1)
# Need console logging for SickBeard.py and SickBeard-console.exe # Need console logging for SickBeard.py and SickBeard-console.exe
self.consoleLogging = (not hasattr(sys, 'frozen')) or (sickbeard.MY_NAME.lower().find('-console') > 0) self.console_logging = (not hasattr(sys, 'frozen')) or (sickbeard.MY_NAME.lower().find('-console') > 0)
# Rename the main thread # Rename the main thread
threading.currentThread().name = 'MAIN' threading.currentThread().name = 'MAIN'
try: try:
opts, args = getopt.getopt(sys.argv[1:], 'hfqdp::', opts, args = getopt.getopt(sys.argv[1:], 'hfqdsp::',
['help', 'forceupdate', 'quiet', 'nolaunch', 'daemon', 'pidfile=', 'port=', ['help', 'forceupdate', 'quiet', 'nolaunch', 'daemon', 'systemd', 'pidfile=',
'datadir=', 'config=', 'noresize']) # @UnusedVariable 'port=', 'datadir=', 'config=', 'noresize']) # @UnusedVariable
except getopt.GetoptError: except getopt.GetoptError:
sys.exit(self.help_message()) sys.exit(self.help_message())
@ -177,43 +198,50 @@ class SickGear(object):
# For now we'll just silence the logging # For now we'll just silence the logging
if o in ('-q', '--quiet'): if o in ('-q', '--quiet'):
self.consoleLogging = False self.console_logging = False
# Should we update (from indexer) all shows in the DB right away? # Should we update (from indexer) all shows in the DB right away?
if o in ('-f', '--forceupdate'): if o in ('-f', '--forceupdate'):
self.forceUpdate = True self.force_update = True
# Suppress launching web browser # Suppress launching web browser
# Needed for OSes without default browser assigned # Needed for OSes without default browser assigned
# Prevent duplicate browser window when restarting in the app # Prevent duplicate browser window when restarting in the app
if o in ('--nolaunch',): if o in ('--nolaunch',):
self.noLaunch = True self.no_launch = True
# Override default/configured port # Override default/configured port
if o in ('-p', '--port'): if o in ('-p', '--port'):
try: try:
self.forcedPort = int(a) self.forced_port = int(a)
except ValueError: except ValueError:
sys.exit('Port: %s is not a number. Exiting.' % a) sys.exit('Port: %s is not a number. Exiting.' % a)
# Run as a double forked daemon # Run as a double forked daemon
if o in ('-d', '--daemon'): if o in ('-d', '--daemon'):
self.runAsDaemon = True self.run_as_daemon = True
# When running as daemon disable consoleLogging and don't start browser # When running as daemon disable consoleLogging and don't start browser
self.consoleLogging = False self.console_logging = False
self.noLaunch = True self.no_launch = True
if sys.platform == 'win32': if 'win32' == sys.platform:
self.runAsDaemon = False self.run_as_daemon = False
# Run as a systemd service
if o in ('-s', '--systemd') and 'win32' != sys.platform:
self.run_as_systemd = True
self.run_as_daemon = False
self.console_logging = False
self.no_launch = True
# Write a pidfile if requested # Write a pidfile if requested
if o in ('--pidfile',): if o in ('--pidfile',):
self.CREATEPID = True self.create_pid = True
self.PIDFILE = str(a) self.pid_file = str(a)
# If the pidfile already exists, sickbeard may still be running, so exit # If the pidfile already exists, sickbeard may still be running, so exit
if os.path.exists(self.PIDFILE): if os.path.exists(self.pid_file):
sys.exit('PID file: %s already exists. Exiting.' % self.PIDFILE) sys.exit('PID file: %s already exists. Exiting.' % self.pid_file)
# Specify folder to load the config file from # Specify folder to load the config file from
if o in ('--config',): if o in ('--config',):
@ -228,19 +256,19 @@ class SickGear(object):
sickbeard.NO_RESIZE = True sickbeard.NO_RESIZE = True
# The pidfile is only useful in daemon mode, make sure we can write the file properly # The pidfile is only useful in daemon mode, make sure we can write the file properly
if self.CREATEPID: if self.create_pid:
if self.runAsDaemon: if self.run_as_daemon:
pid_dir = os.path.dirname(self.PIDFILE) pid_dir = os.path.dirname(self.pid_file)
if not os.access(pid_dir, os.F_OK): if not os.access(pid_dir, os.F_OK):
sys.exit(u"PID dir: %s doesn't exist. Exiting." % pid_dir) sys.exit(u"PID dir: %s doesn't exist. Exiting." % pid_dir)
if not os.access(pid_dir, os.W_OK): if not os.access(pid_dir, os.W_OK):
sys.exit(u'PID dir: %s must be writable (write permissions). Exiting.' % pid_dir) sys.exit(u'PID dir: %s must be writable (write permissions). Exiting.' % pid_dir)
else: else:
if self.consoleLogging: if self.console_logging:
print(u'Not running in daemon mode. PID file creation disabled') print(u'Not running in daemon mode. PID file creation disabled')
self.CREATEPID = False self.create_pid = False
# If they don't specify a config file then put it in the data dir # If they don't specify a config file then put it in the data dir
if not sickbeard.CONFIG_FILE: if not sickbeard.CONFIG_FILE:
@ -266,7 +294,7 @@ class SickGear(object):
% os.path.dirname(sickbeard.CONFIG_FILE)) % os.path.dirname(sickbeard.CONFIG_FILE))
os.chdir(sickbeard.DATA_DIR) os.chdir(sickbeard.DATA_DIR)
if self.consoleLogging: if self.console_logging:
print(u'Starting up SickGear from %s' % sickbeard.CONFIG_FILE) print(u'Starting up SickGear from %s' % sickbeard.CONFIG_FILE)
# Load the config and publish it to the sickbeard package # Load the config and publish it to the sickbeard package
@ -307,19 +335,19 @@ class SickGear(object):
print(u'Rollback of [%s] successful.' % d) print(u'Rollback of [%s] successful.' % d)
# Initialize the config and our threads # Initialize the config and our threads
sickbeard.initialize(consoleLogging=self.consoleLogging) sickbeard.initialize(console_logging=self.console_logging)
if self.runAsDaemon: if self.run_as_daemon:
self.daemonize() self.daemonize()
# Get PID # Get PID
sickbeard.PID = os.getpid() sickbeard.PID = os.getpid()
if self.forcedPort: if self.forced_port:
logger.log(u'Forcing web server to port %s' % self.forcedPort) logger.log(u'Forcing web server to port %s' % self.forced_port)
self.startPort = self.forcedPort self.start_port = self.forced_port
else: else:
self.startPort = sickbeard.WEB_PORT self.start_port = sickbeard.WEB_PORT
if sickbeard.WEB_LOG: if sickbeard.WEB_LOG:
self.log_dir = sickbeard.LOG_DIR self.log_dir = sickbeard.LOG_DIR
@ -338,7 +366,7 @@ class SickGear(object):
# web server options # web server options
self.web_options = { self.web_options = {
'port': int(self.startPort), 'port': int(self.start_port),
'host': self.webhost, 'host': self.webhost,
'data_root': os.path.join(sickbeard.PROG_DIR, 'gui', sickbeard.GUI_NAME), 'data_root': os.path.join(sickbeard.PROG_DIR, 'gui', sickbeard.GUI_NAME),
'web_root': sickbeard.WEB_ROOT, 'web_root': sickbeard.WEB_ROOT,
@ -358,24 +386,26 @@ class SickGear(object):
self.webserver = WebServer(self.web_options) self.webserver = WebServer(self.web_options)
self.webserver.start() self.webserver.start()
except Exception: except (StandardError, Exception):
logger.log(u'Unable to start web server, is something else running on port %d?' % self.startPort, logger.log(u'Unable to start web server, is something else running on port %d?' % self.start_port,
logger.ERROR) logger.ERROR)
if sickbeard.LAUNCH_BROWSER and not self.runAsDaemon: if self.run_as_systemd:
self.exit(0)
if sickbeard.LAUNCH_BROWSER and not self.no_launch:
logger.log(u'Launching browser and exiting', logger.ERROR) logger.log(u'Launching browser and exiting', logger.ERROR)
sickbeard.launch_browser(self.startPort) sickbeard.launch_browser(self.start_port)
os._exit(1) self.exit(1)
# Check if we need to perform a restore first # Check if we need to perform a restore first
restoreDir = os.path.join(sickbeard.DATA_DIR, 'restore') restore_dir = os.path.join(sickbeard.DATA_DIR, 'restore')
if os.path.exists(restoreDir): if os.path.exists(restore_dir):
if self.restore(restoreDir, sickbeard.DATA_DIR): if self.restore(restore_dir, sickbeard.DATA_DIR):
logger.log(u'Restore successful...') logger.log(u'Restore successful...')
else: else:
logger.log_error_and_exit(u'Restore FAILED!') logger.log_error_and_exit(u'Restore FAILED!')
# Build from the DB to start with # Build from the DB to start with
self.loadShowsFromDB() self.load_shows_from_db()
# Fire up all our threads # Fire up all our threads
sickbeard.start() sickbeard.start()
@ -395,12 +425,12 @@ class SickGear(object):
failed_history.trimHistory() failed_history.trimHistory()
# Start an update if we're supposed to # Start an update if we're supposed to
if self.forceUpdate or sickbeard.UPDATE_SHOWS_ON_START: if self.force_update or sickbeard.UPDATE_SHOWS_ON_START:
sickbeard.showUpdateScheduler.action.run(force=True) # @UndefinedVariable sickbeard.showUpdateScheduler.action.run(force=True) # @UndefinedVariable
# Launch browser # Launch browser
if sickbeard.LAUNCH_BROWSER and not (self.noLaunch or self.runAsDaemon): if sickbeard.LAUNCH_BROWSER and not self.no_launch:
sickbeard.launch_browser(self.startPort) sickbeard.launch_browser(self.start_port)
# main loop # main loop
while True: while True:
@ -415,9 +445,9 @@ class SickGear(object):
try: try:
pid = os.fork() # @UndefinedVariable - only available in UNIX pid = os.fork() # @UndefinedVariable - only available in UNIX
if pid != 0: if pid != 0:
os._exit(0) self.exit(0)
except OSError as e: except OSError as er:
sys.stderr.write('fork #1 failed: %d (%s)\n' % (e.errno, e.strerror)) sys.stderr.write('fork #1 failed: %d (%s)\n' % (er.errno, er.strerror))
sys.exit(1) sys.exit(1)
os.setsid() # @UndefinedVariable - only available in UNIX os.setsid() # @UndefinedVariable - only available in UNIX
@ -430,20 +460,20 @@ class SickGear(object):
try: try:
pid = os.fork() # @UndefinedVariable - only available in UNIX pid = os.fork() # @UndefinedVariable - only available in UNIX
if pid != 0: if pid != 0:
os._exit(0) self.exit(0)
except OSError as e: except OSError as er:
sys.stderr.write('fork #2 failed: %d (%s)\n' % (e.errno, e.strerror)) sys.stderr.write('fork #2 failed: %d (%s)\n' % (er.errno, er.strerror))
sys.exit(1) sys.exit(1)
# Write pid # Write pid
if self.CREATEPID: if self.create_pid:
pid = str(os.getpid()) pid = str(os.getpid())
logger.log(u'Writing PID: %s to %s' % (pid, self.PIDFILE)) logger.log(u'Writing PID: %s to %s' % (pid, self.pid_file))
try: try:
open(self.PIDFILE, 'w').write('%s\n' % pid) open(self.pid_file, 'w').write('%s\n' % pid)
except IOError as e: except IOError as er:
logger.log_error_and_exit( logger.log_error_and_exit('Unable to write PID file: %s Error: %s [%s]' % (
u'Unable to write PID file: %s Error: %s [%s]' % (self.PIDFILE, e.strerror, e.errno)) self.pid_file, er.strerror, er.errno))
# Redirect all output # Redirect all output
sys.stdout.flush() sys.stdout.flush()
@ -458,10 +488,10 @@ class SickGear(object):
os.dup2(stderr.fileno(), sys.stderr.fileno()) os.dup2(stderr.fileno(), sys.stderr.fileno())
@staticmethod @staticmethod
def remove_pid_file(PIDFILE): def remove_pid_file(pidfile):
try: try:
if os.path.exists(PIDFILE): if os.path.exists(pidfile):
os.remove(PIDFILE) os.remove(pidfile)
except (IOError, OSError): except (IOError, OSError):
return False return False
@ -469,43 +499,42 @@ class SickGear(object):
return True return True
@staticmethod @staticmethod
def loadShowsFromDB(): def load_shows_from_db():
""" """
Populates the showList with shows from the database Populates the showList with shows from the database
""" """
logger.log(u'Loading initial show list') logger.log(u'Loading initial show list')
myDB = db.DBConnection() my_db = db.DBConnection()
sqlResults = myDB.select('SELECT * FROM tv_shows') sql_results = my_db.select('SELECT * FROM tv_shows')
sickbeard.showList = [] sickbeard.showList = []
for sqlShow in sqlResults: for sqlShow in sql_results:
try: try:
curShow = TVShow(int(sqlShow['indexer']), int(sqlShow['indexer_id'])) cur_show = TVShow(int(sqlShow['indexer']), int(sqlShow['indexer_id']))
curShow.nextEpisode() cur_show.nextEpisode()
sickbeard.showList.append(curShow) sickbeard.showList.append(cur_show)
except Exception as e: except Exception as er:
logger.log( logger.log('There was an error creating the show in %s: %s' % (
u'There was an error creating the show in %s: %s' % (sqlShow['location'], str(e).decode('utf-8', sqlShow['location'], str(er).decode('utf-8', 'replace')), logger.ERROR)
'replace')),
logger.ERROR)
def restore(self, srcDir, dstDir): @staticmethod
def restore(src_dir, dst_dir):
try: try:
for file in os.listdir(srcDir): for filename in os.listdir(src_dir):
srcFile = os.path.join(srcDir, file) src_file = os.path.join(src_dir, filename)
dstFile = os.path.join(dstDir, file) dst_file = os.path.join(dst_dir, filename)
bakFile = os.path.join(dstDir, file + '.bak') bak_file = os.path.join(dst_dir, '%s.bak' % filename)
shutil.move(dstFile, bakFile) shutil.move(dst_file, bak_file)
shutil.move(srcFile, dstFile) shutil.move(src_file, dst_file)
os.rmdir(srcDir) os.rmdir(src_dir)
return True return True
except: except (StandardError, Exception):
return False return False
def shutdown(self, type): def shutdown(self, ev_type):
if sickbeard.started: if sickbeard.started:
# stop all tasks # stop all tasks
sickbeard.halt() sickbeard.halt()
@ -519,14 +548,15 @@ class SickGear(object):
self.webserver.shutDown() self.webserver.shutDown()
try: try:
self.webserver.join(10) self.webserver.join(10)
except: except (StandardError, Exception):
pass pass
# if run as daemon delete the pidfile # if run as daemon delete the pidfile
if self.runAsDaemon and self.CREATEPID: if self.run_as_daemon and self.create_pid:
self.remove_pid_file(self.PIDFILE) self.remove_pid_file(self.pid_file)
if sickbeard.events.SystemEvent.RESTART == ev_type:
if type == sickbeard.events.SystemEvent.RESTART:
install_type = sickbeard.versionCheckScheduler.action.install_type install_type = sickbeard.versionCheckScheduler.action.install_type
popen_list = [] popen_list = []
@ -536,6 +566,12 @@ class SickGear(object):
if popen_list: if popen_list:
popen_list += sickbeard.MY_ARGS popen_list += sickbeard.MY_ARGS
if self.run_as_systemd:
logger.log(u'Restarting SickGear with exit(1) handler and %s' % popen_list)
logger.close()
self.exit(1)
if '--nolaunch' not in popen_list: if '--nolaunch' not in popen_list:
popen_list += ['--nolaunch'] popen_list += ['--nolaunch']
logger.log(u'Restarting SickGear with %s' % popen_list) logger.log(u'Restarting SickGear with %s' % popen_list)
@ -543,8 +579,11 @@ class SickGear(object):
subprocess.Popen(popen_list, cwd=os.getcwd()) subprocess.Popen(popen_list, cwd=os.getcwd())
# system exit # system exit
os._exit(0) self.exit(0)
@staticmethod
def exit(code):
os._exit(code)
if __name__ == '__main__': if __name__ == '__main__':
if sys.hexversion >= 0x020600F0: if sys.hexversion >= 0x020600F0:

View file

@ -56,7 +56,7 @@
<span class="component-desc"> <span class="component-desc">
<select name="tag" id="tag" class="form-control form-control-inline input-sm"> <select name="tag" id="tag" class="form-control form-control-inline input-sm">
#for $tag in $sickbeard.SHOW_TAGS: #for $tag in $sickbeard.SHOW_TAGS:
<option value="$tag" #if $tag == $sickbeard.DEFAULT_SHOW_TAG then 'selected="selected"' else ''#>$tag</option> <option value="$tag" #if $tag == $getattr(sickbeard, 'SHOW_TAG_DEFAULT', $getattr(sickbeard, 'DEFAULT_SHOW_TAG', None)) then 'selected="selected"' else ''#>$tag</option>
#end for #end for
</select> </select>
<span>and display on the show list page under this section</span> <span>and display on the show list page under this section</span>

View file

@ -1,3 +1,10 @@
##
#set sgHost = $sbHost
#set sgPort = $sbHttpPort
#set sgRoot = $sbRoot
#set sgUseHttps = $sbHttpsEnabled
#set themeSpinner = '-dark' if 'dark' == $sbThemeName else ''
##
<!DOCTYPE html> <!DOCTYPE html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
@ -26,26 +33,12 @@
<meta name="msapplication-config" content="$sbRoot/css/browserconfig.xml"> <meta name="msapplication-config" content="$sbRoot/css/browserconfig.xml">
<script type="text/javascript" src="$sbRoot/js/lib/jquery-1.8.3.min.js?v=$sbPID"></script> <script type="text/javascript" src="$sbRoot/js/lib/jquery-1.8.3.min.js?v=$sbPID"></script>
#try:
#set sgHost = $sbHost
#set sgPort = $sbHttpPort
#set sgUseHttps = $sbHttpsEnabled
#set themeSpinner = $sbThemeName
#except NameMapper.NotFound:
#set sgHost = 'localhost'
#set sgPort = $sickbeard.WEB_PORT
#set sgUseHttps = False
#set themeSpinner = $sickbeard.THEME_NAME
#end try
#set themeSpinner = '-dark' if 'dark' == themeSpinner else ''
##
<script type="text/javascript" charset="utf-8"> <script type="text/javascript" charset="utf-8">
<!-- <!--
\$.SickGear = { \$.SickGear = {
Root: '$sbRoot',
Host: '$sgHost', Host: '$sgHost',
Port: '$sgPort', Port: '$sgPort',
Root: '$sgRoot',
UseHttps: #echo ('!1', '!0')[False != $sgUseHttps and 0 != $sgUseHttps]# UseHttps: #echo ('!1', '!0')[False != $sgUseHttps and 0 != $sgUseHttps]#
}; };
//--> //-->
@ -58,12 +51,12 @@ body{padding-top:0 !important}.sglogo{display:block;width:138px;height:74px;marg
</style> </style>
<link rel="stylesheet" type="text/css" href="$sbRoot/css/style.css?v=$sbPID"> <link rel="stylesheet" type="text/css" href="$sbRoot/css/style.css?v=$sbPID">
<link rel="stylesheet" type="text/css" href="$sbRoot/css/#echo ('dark', 'light')['' == themeSpinner]#.css?v=$sbPID"> <link rel="stylesheet" type="text/css" href="$sbRoot/css/#echo ('dark', 'light')['' == $themeSpinner]#.css?v=$sbPID">
</head><body><span class="sglogo"></span> </head><body><span class="sglogo"></span>
<div class="bfr"><img src="$sbRoot/images/loading16${themeSpinner}.gif" /><img src="$sbRoot/images/yes16.png" /><img src="$sbRoot/images/no16.png" /></div> <div class="bfr"><img src="$sbRoot/images/loading16${themeSpinner}.gif" /><img src="$sbRoot/images/yes16.png" /><img src="$sbRoot/images/no16.png" /></div>
<h2 class="sub-title">Performing Restart</h2> <h2 class="sub-title">Performing #echo ('Restart', 'Shutdown')[$shutdown]#</h2>
<div id="shut_down_message"> <div id="shut_down_message">
<span class="desc"><span class="grey-text">Waiting for SickGear to</span> shut down: </span> <span class="desc"><span class="grey-text">Waiting for SickGear to</span> shut down: </span>
@ -71,17 +64,23 @@ body{padding-top:0 !important}.sglogo{display:block;width:138px;height:74px;marg
</div> </div>
<div class="hide" id="restart_message"> <div class="hide" id="restart_message">
#if not $shutdown
<span class="desc"><span class="grey-text">Waiting for SickGear to</span> start up: </span> <span class="desc"><span class="grey-text">Waiting for SickGear to</span> start up: </span>
<span class="images"><i class="spinner"></i><span class="hide-yes"><i class="yes"></i></span><span class="hide-no"><i class="no"></i></span></span> <span class="images"><i class="spinner"></i><span class="hide-yes"><i class="yes"></i></span><span class="hide-no"><i class="no"></i></span></span>
#end if
</div> </div>
<div class="hide" id="refresh_message"> <div class="hide" id="refresh_message">
#if not $shutdown
<span class="desc"><span class="grey-text">Waiting for SickGear </span> home page: </span> <span class="desc"><span class="grey-text">Waiting for SickGear </span> home page: </span>
<span class="images"><i class="spinner"></i></span> <span class="images"><i class="spinner"></i></span>
#end if
</div> </div>
<div class="hide" id="restart_fail_message"> <div class="hide" id="restart_fail_message">
#if not $shutdown
<span class="red-text">Error:</span> The restart timed out, perhaps something prevented SickGear from starting ? <span class="red-text">Error:</span> The restart timed out, perhaps something prevented SickGear from starting ?
#end if
</div> </div>
</body></html> </body></html>

View file

@ -11,8 +11,7 @@
# Putting a minus (-) in front of file means no error warning if the file doesn't exist # Putting a minus (-) in front of file means no error warning if the file doesn't exist
# #
# - Adjust ExecStart= to point to your python and SickGear executables. # - Adjust ExecStart= to point to your python and SickGear executables.
# The FIRST token of the command line must be an ABSOLUTE FILE NAME, # The FIRST token of the command line must be an ABSOLUTE FILE NAME, followed by arguments for the process.
# then followed by arguments for the process.
# If no --datadir is given, data is stored in same dir as SickBeard.py # If no --datadir is given, data is stored in same dir as SickBeard.py
# Arguments can also be set in EnvironmentFile (except python) # Arguments can also be set in EnvironmentFile (except python)
# #
@ -21,16 +20,6 @@
# graphical.target equates to runlevel 5 (multi-user X11 graphical mode) # graphical.target equates to runlevel 5 (multi-user X11 graphical mode)
# #
### Example Using SickGear as daemon with pid file
# Type=forking
# PIDFile=/var/run/sickgear/sickgear.pid
# ExecStart=/usr/bin/python2 /opt/sickgear/app/SickBeard.py -q --daemon --nolaunch --pidfile=/var/run/sickgear/sickgear.pid --datadir=/opt/sickgear/data
## Example Using SickGear as daemon without pid file
# Type=forking
# GuessMainPID=no
# ExecStart=/usr/bin/python2 /opt/sickgear/app/SickBeard.py -q --daemon --nolaunch --datadir=/opt/sickgear/data
### Example Using simple ### Example Using simple
# Type=simple # Type=simple
# ExecStart=/usr/bin/python2 /opt/sickgear/app/SickBeard.py -q --nolaunch # ExecStart=/usr/bin/python2 /opt/sickgear/app/SickBeard.py -q --nolaunch
@ -43,15 +32,16 @@
### Configuration ### Configuration
[Unit] [Unit]
Description=SickGear Daemon Description=SickGear Service
[Service] [Service]
User=sickgear User=sickgear
Group=sickgear Group=sickgear
Type=forking Environment=PYTHONUNBUFFERED=true
GuessMainPID=no ExecStart=/usr/bin/python2 /opt/sickgear/app/SickBeard.py --systemd --datadir=/opt/sickgear/data
ExecStart=/usr/bin/python2 /opt/sickgear/app/SickBeard.py -q --daemon --nolaunch --datadir=/opt/sickgear/data KillMode=process
Restart=on-failure
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

View file

@ -83,7 +83,7 @@ searchQueueScheduler = None
properFinderScheduler = None properFinderScheduler = None
autoPostProcesserScheduler = None autoPostProcesserScheduler = None
subtitlesFinderScheduler = None subtitlesFinderScheduler = None
traktCheckerScheduler = None # traktCheckerScheduler = None
background_mapping_task = None background_mapping_task = None
showList = None showList = None
@ -146,11 +146,8 @@ TRASH_ROTATE_LOGS = False
HOME_SEARCH_FOCUS = True HOME_SEARCH_FOCUS = True
SORT_ARTICLE = False SORT_ARTICLE = False
DEBUG = False DEBUG = False
DISPLAY_BACKGROUND = False
DISPLAY_BACKGROUND_TRANSPARENT = None
DISPLAY_ALL_SEASONS = True
SHOW_TAGS = [] SHOW_TAGS = []
DEFAULT_SHOW_TAG = '' SHOW_TAG_DEFAULT = ''
SHOWLIST_TAGVIEW = '' SHOWLIST_TAGVIEW = ''
METADATA_XBMC = None METADATA_XBMC = None
@ -203,13 +200,13 @@ TORRENT_DIR = None
DOWNLOAD_PROPERS = False DOWNLOAD_PROPERS = False
CHECK_PROPERS_INTERVAL = None CHECK_PROPERS_INTERVAL = None
ALLOW_HIGH_PRIORITY = False ALLOW_HIGH_PRIORITY = False
NEWZNAB_DATA = ''
AUTOPOSTPROCESSER_FREQUENCY = None AUTOPOSTPROCESSER_FREQUENCY = None
RECENTSEARCH_FREQUENCY = None RECENTSEARCH_FREQUENCY = 0
UPDATE_FREQUENCY = None UPDATE_FREQUENCY = None
RECENTSEARCH_STARTUP = False RECENTSEARCH_STARTUP = False
BACKLOG_FREQUENCY = None BACKLOG_FREQUENCY = None
BACKLOG_STARTUP = False
BACKLOG_NOFULL = False BACKLOG_NOFULL = False
DEFAULT_AUTOPOSTPROCESSER_FREQUENCY = 10 DEFAULT_AUTOPOSTPROCESSER_FREQUENCY = 10
@ -458,7 +455,8 @@ EXTRA_SCRIPTS = []
GIT_PATH = None GIT_PATH = None
IGNORE_WORDS = 'core2hd, hevc, MrLss, reenc, x265, danish, deutsch, dutch, flemish, french, german, italian, nordic, norwegian, portuguese, spanish, swedish, turkish' IGNORE_WORDS = 'core2hd, hevc, MrLss, reenc, x265, danish, deutsch, dutch, flemish, french, ' + \
'german, italian, nordic, norwegian, portuguese, spanish, swedish, turkish'
REQUIRE_WORDS = '' REQUIRE_WORDS = ''
CALENDAR_UNPROTECTED = False CALENDAR_UNPROTECTED = False
@ -499,81 +497,119 @@ def get_backlog_cycle_time():
return max([cycletime, 720]) return max([cycletime, 720])
def initialize(consoleLogging=True): def initialize(console_logging=True):
with INIT_LOCK: with INIT_LOCK:
global BRANCH, GIT_REMOTE, CUR_COMMIT_HASH, CUR_COMMIT_BRANCH, ACTUAL_LOG_DIR, LOG_DIR, FILE_LOGGING_PRESET, WEB_PORT, WEB_LOG, ENCRYPTION_VERSION, WEB_ROOT, WEB_USERNAME, WEB_PASSWORD, WEB_HOST, WEB_IPV6, USE_API, API_KEY, ENABLE_HTTPS, HTTPS_CERT, HTTPS_KEY, \ # Misc
HANDLE_REVERSE_PROXY, USE_NZBS, USE_TORRENTS, NZB_METHOD, NZB_DIR, DOWNLOAD_PROPERS, CHECK_PROPERS_INTERVAL, ALLOW_HIGH_PRIORITY, TORRENT_METHOD, \ global __INITIALIZED__, showList, providerList, newznabProviderList, torrentRssProviderList, \
SAB_USERNAME, SAB_PASSWORD, SAB_APIKEY, SAB_CATEGORY, SAB_HOST, \ WEB_HOST, WEB_ROOT, ACTUAL_CACHE_DIR, CACHE_DIR, ZONEINFO_DIR, ADD_SHOWS_WO_DIR, CREATE_MISSING_SHOW_DIRS, \
NZBGET_USERNAME, NZBGET_PASSWORD, NZBGET_CATEGORY, NZBGET_PRIORITY, NZBGET_HOST, NZBGET_USE_HTTPS, backlogSearchScheduler, \ RECENTSEARCH_STARTUP, NAMING_FORCE_FOLDERS, SOCKET_TIMEOUT, DEBUG, INDEXER_DEFAULT, REMOVE_FILENAME_CHARS, \
TORRENT_USERNAME, TORRENT_PASSWORD, TORRENT_HOST, TORRENT_PATH, TORRENT_SEED_TIME, TORRENT_PAUSED, TORRENT_HIGH_BANDWIDTH, TORRENT_LABEL, TORRENT_VERIFY_CERT, \ CONFIG_FILE
USE_EMBY, EMBY_UPDATE_LIBRARY, EMBY_HOST, EMBY_APIKEY, \ # Schedulers
USE_KODI, KODI_ALWAYS_ON, KODI_NOTIFY_ONSNATCH, KODI_NOTIFY_ONDOWNLOAD, KODI_NOTIFY_ONSUBTITLEDOWNLOAD,\ # global traktCheckerScheduler
KODI_UPDATE_FULL, KODI_UPDATE_ONLYFIRST, KODI_UPDATE_LIBRARY, KODI_HOST, KODI_USERNAME, KODI_PASSWORD, \ global recentSearchScheduler, backlogSearchScheduler, showUpdateScheduler, \
USE_XBMC, XBMC_ALWAYS_ON, XBMC_NOTIFY_ONSNATCH, XBMC_NOTIFY_ONDOWNLOAD, XBMC_NOTIFY_ONSUBTITLEDOWNLOAD,\ versionCheckScheduler, showQueueScheduler, searchQueueScheduler, \
XBMC_UPDATE_FULL, XBMC_UPDATE_ONLYFIRST, XBMC_UPDATE_LIBRARY, XBMC_HOST, XBMC_USERNAME, XBMC_PASSWORD, \ properFinderScheduler, autoPostProcesserScheduler, subtitlesFinderScheduler, background_mapping_task
USE_PLEX, PLEX_NOTIFY_ONSNATCH, PLEX_NOTIFY_ONDOWNLOAD, PLEX_NOTIFY_ONSUBTITLEDOWNLOAD, \ # Add Show Defaults
PLEX_UPDATE_LIBRARY, PLEX_SERVER_HOST, PLEX_HOST, PLEX_USERNAME, PLEX_PASSWORD, \ global STATUS_DEFAULT, QUALITY_DEFAULT, SHOW_TAG_DEFAULT, FLATTEN_FOLDERS_DEFAULT, SUBTITLES_DEFAULT, \
USE_TRAKT, TRAKT_CONNECTED_ACCOUNT, TRAKT_ACCOUNTS, TRAKT_MRU, TRAKT_VERIFY, TRAKT_REMOVE_WATCHLIST, TRAKT_TIMEOUT, TRAKT_USE_WATCHLIST, TRAKT_METHOD_ADD, TRAKT_START_PAUSED, traktCheckerScheduler, TRAKT_SYNC, TRAKT_DEFAULT_INDEXER, TRAKT_REMOVE_SERIESLIST, TRAKT_UPDATE_COLLECTION, \ WANTED_BEGIN_DEFAULT, WANTED_LATEST_DEFAULT, SCENE_DEFAULT, ANIME_DEFAULT
BACKLOG_FREQUENCY, DEFAULT_BACKLOG_FREQUENCY, MIN_BACKLOG_FREQUENCY, MAX_BACKLOG_FREQUENCY, BACKLOG_STARTUP, BACKLOG_NOFULL, SKIP_REMOVED_FILES, \ # Post processing
showUpdateScheduler, __INITIALIZED__, LAUNCH_BROWSER, TRASH_REMOVE_SHOW, TRASH_ROTATE_LOGS, HOME_SEARCH_FOCUS, SORT_ARTICLE, showList, loadingShowList, UPDATE_SHOWS_ON_START, SHOW_UPDATE_HOUR, \ global KEEP_PROCESSED_DIR
NEWZNAB_DATA, INDEXER_DEFAULT, INDEXER_TIMEOUT, USENET_RETENTION, TORRENT_DIR, \ # Views
QUALITY_DEFAULT, FLATTEN_FOLDERS_DEFAULT, SUBTITLES_DEFAULT, STATUS_DEFAULT, WANTED_BEGIN_DEFAULT, WANTED_LATEST_DEFAULT, RECENTSEARCH_STARTUP, \ global GUI_NAME, HOME_LAYOUT, POSTER_SORTBY, POSTER_SORTDIR, DISPLAY_SHOW_SPECIALS, \
GROWL_NOTIFY_ONSNATCH, GROWL_NOTIFY_ONDOWNLOAD, GROWL_NOTIFY_ONSUBTITLEDOWNLOAD, TWITTER_NOTIFY_ONSNATCH, TWITTER_NOTIFY_ONDOWNLOAD, TWITTER_NOTIFY_ONSUBTITLEDOWNLOAD, \ EPISODE_VIEW_LAYOUT, EPISODE_VIEW_SORT, EPISODE_VIEW_DISPLAY_PAUSED, \
USE_GROWL, GROWL_HOST, GROWL_PASSWORD, USE_PROWL, PROWL_NOTIFY_ONSNATCH, PROWL_NOTIFY_ONDOWNLOAD, PROWL_NOTIFY_ONSUBTITLEDOWNLOAD, PROWL_API, PROWL_PRIORITY, PROG_DIR, \ EPISODE_VIEW_MISSED_RANGE, HISTORY_LAYOUT
USE_PYTIVO, PYTIVO_NOTIFY_ONSNATCH, PYTIVO_NOTIFY_ONDOWNLOAD, PYTIVO_NOTIFY_ONSUBTITLEDOWNLOAD, PYTIVO_UPDATE_LIBRARY, PYTIVO_HOST, PYTIVO_SHARE_NAME, PYTIVO_TIVO_NAME, \ # Gen Config/Misc
global LAUNCH_BROWSER, UPDATE_SHOWS_ON_START, SHOW_UPDATE_HOUR, \
TRASH_REMOVE_SHOW, TRASH_ROTATE_LOGS, ACTUAL_LOG_DIR, LOG_DIR, INDEXER_TIMEOUT, ROOT_DIRS, \
VERSION_NOTIFY, AUTO_UPDATE, UPDATE_FREQUENCY, NOTIFY_ON_UPDATE
# Gen Config/Interface
global THEME_NAME, DEFAULT_HOME, SHOWLIST_TAGVIEW, SHOW_TAGS, \
HOME_SEARCH_FOCUS, USE_IMDB_INFO, IMDB_ACCOUNTS, SORT_ARTICLE, FUZZY_DATING, TRIM_ZERO, \
DATE_PRESET, TIME_PRESET, TIME_PRESET_W_SECONDS, TIMEZONE_DISPLAY, \
WEB_USERNAME, WEB_PASSWORD, CALENDAR_UNPROTECTED, USE_API, API_KEY, WEB_PORT, WEB_LOG, \
ENABLE_HTTPS, HTTPS_CERT, HTTPS_KEY, WEB_IPV6, HANDLE_REVERSE_PROXY
# Gen Config/Advanced
global BRANCH, CUR_COMMIT_BRANCH, GIT_REMOTE, CUR_COMMIT_HASH, GIT_PATH, CPU_PRESET, ANON_REDIRECT, \
ENCRYPTION_VERSION, PROXY_SETTING, PROXY_INDEXERS, FILE_LOGGING_PRESET
# Search Settings/Episode
global DOWNLOAD_PROPERS, CHECK_PROPERS_INTERVAL, RECENTSEARCH_FREQUENCY, \
BACKLOG_DAYS, BACKLOG_NOFULL, BACKLOG_FREQUENCY, USENET_RETENTION, IGNORE_WORDS, REQUIRE_WORDS, \
ALLOW_HIGH_PRIORITY, SEARCH_UNAIRED, UNAIRED_RECENT_SEARCH_ONLY
# Search Settings/NZB search
global USE_NZBS, NZB_METHOD, NZB_DIR, SAB_HOST, SAB_USERNAME, SAB_PASSWORD, SAB_APIKEY, SAB_CATEGORY, \
NZBGET_USE_HTTPS, NZBGET_HOST, NZBGET_USERNAME, NZBGET_PASSWORD, NZBGET_CATEGORY, NZBGET_PRIORITY
# Search Settings/Torrent search
global USE_TORRENTS, TORRENT_METHOD, TORRENT_DIR, TORRENT_HOST, TORRENT_USERNAME, TORRENT_PASSWORD, \
TORRENT_LABEL, TORRENT_PATH, TORRENT_SEED_TIME, TORRENT_PAUSED, TORRENT_HIGH_BANDWIDTH, TORRENT_VERIFY_CERT
# Search Providers
global PROVIDER_ORDER, NEWZNAB_DATA, PROVIDER_HOMES
# Subtitles
global USE_SUBTITLES, SUBTITLES_LANGUAGES, SUBTITLES_DIR, SUBTITLES_FINDER_FREQUENCY, \
SUBTITLES_HISTORY, SUBTITLES_SERVICES_ENABLED, SUBTITLES_SERVICES_LIST
# Post Processing/Post-Processing
global TV_DOWNLOAD_DIR, PROCESS_METHOD, PROCESS_AUTOMATICALLY, AUTOPOSTPROCESSER_FREQUENCY, \
POSTPONE_IF_SYNC_FILES, EXTRA_SCRIPTS, \
DEFAULT_AUTOPOSTPROCESSER_FREQUENCY, MIN_AUTOPOSTPROCESSER_FREQUENCY, \
UNPACK, SKIP_REMOVED_FILES, MOVE_ASSOCIATED_FILES, NFO_RENAME, RENAME_EPISODES, AIRDATE_EPISODES, \
USE_FAILED_DOWNLOADS, DELETE_FAILED
# Post Processing/Episode Naming
global NAMING_PATTERN, NAMING_MULTI_EP, NAMING_STRIP_YEAR, NAMING_CUSTOM_ABD, NAMING_ABD_PATTERN, \
NAMING_CUSTOM_SPORTS, NAMING_SPORTS_PATTERN, \
NAMING_CUSTOM_ANIME, NAMING_ANIME_PATTERN, NAMING_ANIME_MULTI_EP, NAMING_ANIME
# Post Processing/Metadata
global metadata_provider_dict, METADATA_KODI, METADATA_MEDE8ER, METADATA_XBMC, METADATA_MEDIABROWSER, \
METADATA_PS3, METADATA_TIVO, METADATA_WDTV, METADATA_XBMC_12PLUS
# Notification Settings/HT and NAS
global USE_EMBY, EMBY_UPDATE_LIBRARY, EMBY_HOST, EMBY_APIKEY, \
USE_KODI, KODI_ALWAYS_ON, KODI_UPDATE_LIBRARY, KODI_UPDATE_FULL, KODI_UPDATE_ONLYFIRST, \
KODI_HOST, KODI_USERNAME, KODI_PASSWORD, KODI_NOTIFY_ONSNATCH, \
KODI_NOTIFY_ONDOWNLOAD, KODI_NOTIFY_ONSUBTITLEDOWNLOAD, \
USE_XBMC, XBMC_ALWAYS_ON, XBMC_NOTIFY_ONSNATCH, XBMC_NOTIFY_ONDOWNLOAD, XBMC_NOTIFY_ONSUBTITLEDOWNLOAD, \
XBMC_UPDATE_LIBRARY, XBMC_UPDATE_FULL, XBMC_UPDATE_ONLYFIRST, XBMC_HOST, XBMC_USERNAME, XBMC_PASSWORD, \
USE_PLEX, PLEX_USERNAME, PLEX_PASSWORD, PLEX_UPDATE_LIBRARY, PLEX_SERVER_HOST, \
PLEX_NOTIFY_ONSNATCH, PLEX_NOTIFY_ONDOWNLOAD, PLEX_NOTIFY_ONSUBTITLEDOWNLOAD, PLEX_HOST, \
USE_NMJ, NMJ_HOST, NMJ_DATABASE, NMJ_MOUNT, \
USE_NMJv2, NMJv2_HOST, NMJv2_DATABASE, NMJv2_DBLOC, \
USE_SYNOINDEX, \
USE_SYNOLOGYNOTIFIER, SYNOLOGYNOTIFIER_NOTIFY_ONSNATCH, \
SYNOLOGYNOTIFIER_NOTIFY_ONDOWNLOAD, SYNOLOGYNOTIFIER_NOTIFY_ONSUBTITLEDOWNLOAD, \
USE_PYTIVO, PYTIVO_HOST, PYTIVO_SHARE_NAME, PYTIVO_TIVO_NAME, \
PYTIVO_NOTIFY_ONSNATCH, PYTIVO_NOTIFY_ONDOWNLOAD, PYTIVO_NOTIFY_ONSUBTITLEDOWNLOAD, PYTIVO_UPDATE_LIBRARY
# Notification Settings/Devices
global USE_GROWL, GROWL_NOTIFY_ONSNATCH, GROWL_NOTIFY_ONDOWNLOAD, GROWL_NOTIFY_ONSUBTITLEDOWNLOAD, \
GROWL_HOST, GROWL_PASSWORD, \
USE_PROWL, PROWL_NOTIFY_ONSNATCH, PROWL_NOTIFY_ONDOWNLOAD, PROWL_NOTIFY_ONSUBTITLEDOWNLOAD, \
PROWL_API, PROWL_PRIORITY, \
USE_LIBNOTIFY, LIBNOTIFY_NOTIFY_ONSNATCH, LIBNOTIFY_NOTIFY_ONDOWNLOAD, \
LIBNOTIFY_NOTIFY_ONSUBTITLEDOWNLOAD, \
USE_PUSHOVER, PUSHOVER_NOTIFY_ONSNATCH, PUSHOVER_NOTIFY_ONDOWNLOAD, PUSHOVER_NOTIFY_ONSUBTITLEDOWNLOAD, \
PUSHOVER_USERKEY, PUSHOVER_APIKEY, PUSHOVER_PRIORITY, PUSHOVER_DEVICE, PUSHOVER_SOUND, \
USE_BOXCAR2, BOXCAR2_NOTIFY_ONSNATCH, BOXCAR2_NOTIFY_ONDOWNLOAD, BOXCAR2_NOTIFY_ONSUBTITLEDOWNLOAD, \
BOXCAR2_ACCESSTOKEN, BOXCAR2_SOUND, \
USE_NMA, NMA_NOTIFY_ONSNATCH, NMA_NOTIFY_ONDOWNLOAD, NMA_NOTIFY_ONSUBTITLEDOWNLOAD, NMA_API, NMA_PRIORITY, \ USE_NMA, NMA_NOTIFY_ONSNATCH, NMA_NOTIFY_ONDOWNLOAD, NMA_NOTIFY_ONSUBTITLEDOWNLOAD, NMA_API, NMA_PRIORITY, \
USE_PUSHALOT, PUSHALOT_NOTIFY_ONSNATCH, PUSHALOT_NOTIFY_ONDOWNLOAD, PUSHALOT_NOTIFY_ONSUBTITLEDOWNLOAD, PUSHALOT_AUTHORIZATIONTOKEN, \ USE_PUSHALOT, PUSHALOT_NOTIFY_ONSNATCH, PUSHALOT_NOTIFY_ONDOWNLOAD, \
USE_PUSHBULLET, PUSHBULLET_NOTIFY_ONSNATCH, PUSHBULLET_NOTIFY_ONDOWNLOAD, PUSHBULLET_NOTIFY_ONSUBTITLEDOWNLOAD, PUSHBULLET_ACCESS_TOKEN, PUSHBULLET_DEVICE_IDEN, \ PUSHALOT_NOTIFY_ONSUBTITLEDOWNLOAD, PUSHALOT_AUTHORIZATIONTOKEN, \
versionCheckScheduler, VERSION_NOTIFY, AUTO_UPDATE, NOTIFY_ON_UPDATE, PROCESS_AUTOMATICALLY, UNPACK, CPU_PRESET, \ USE_PUSHBULLET, PUSHBULLET_NOTIFY_ONSNATCH, PUSHBULLET_NOTIFY_ONDOWNLOAD, \
KEEP_PROCESSED_DIR, PROCESS_METHOD, TV_DOWNLOAD_DIR, MIN_RECENTSEARCH_FREQUENCY, DEFAULT_UPDATE_FREQUENCY, MIN_UPDATE_FREQUENCY, UPDATE_FREQUENCY, \ PUSHBULLET_NOTIFY_ONSUBTITLEDOWNLOAD, PUSHBULLET_ACCESS_TOKEN, PUSHBULLET_DEVICE_IDEN
showQueueScheduler, searchQueueScheduler, ROOT_DIRS, CACHE_DIR, ACTUAL_CACHE_DIR, ZONEINFO_DIR, TIMEZONE_DISPLAY, \ # Notification Settings/Social
NAMING_PATTERN, NAMING_MULTI_EP, NAMING_ANIME_MULTI_EP, NAMING_FORCE_FOLDERS, NAMING_ABD_PATTERN, NAMING_CUSTOM_ABD, NAMING_SPORTS_PATTERN, NAMING_CUSTOM_SPORTS, NAMING_ANIME_PATTERN, NAMING_CUSTOM_ANIME, NAMING_STRIP_YEAR, \ global USE_TWITTER, TWITTER_NOTIFY_ONSNATCH, TWITTER_NOTIFY_ONDOWNLOAD, TWITTER_NOTIFY_ONSUBTITLEDOWNLOAD, \
RENAME_EPISODES, AIRDATE_EPISODES, properFinderScheduler, PROVIDER_ORDER, PROVIDER_HOMES, autoPostProcesserScheduler, \ TWITTER_USERNAME, TWITTER_PASSWORD, TWITTER_PREFIX, \
providerList, newznabProviderList, torrentRssProviderList, \ USE_TRAKT, TRAKT_CONNECTED_ACCOUNT, TRAKT_ACCOUNTS, TRAKT_MRU, TRAKT_VERIFY, \
EXTRA_SCRIPTS, USE_TWITTER, TWITTER_USERNAME, TWITTER_PASSWORD, TWITTER_PREFIX, RECENTSEARCH_FREQUENCY, \ TRAKT_USE_WATCHLIST, TRAKT_REMOVE_WATCHLIST, TRAKT_TIMEOUT, TRAKT_METHOD_ADD, TRAKT_START_PAUSED, \
USE_BOXCAR2, BOXCAR2_ACCESSTOKEN, BOXCAR2_NOTIFY_ONDOWNLOAD, BOXCAR2_NOTIFY_ONSUBTITLEDOWNLOAD, BOXCAR2_NOTIFY_ONSNATCH, BOXCAR2_SOUND, \ TRAKT_SYNC, TRAKT_DEFAULT_INDEXER, TRAKT_REMOVE_SERIESLIST, TRAKT_UPDATE_COLLECTION, \
USE_PUSHOVER, PUSHOVER_USERKEY, PUSHOVER_APIKEY, PUSHOVER_NOTIFY_ONDOWNLOAD, PUSHOVER_NOTIFY_ONSUBTITLEDOWNLOAD, PUSHOVER_NOTIFY_ONSNATCH, PUSHOVER_PRIORITY, PUSHOVER_DEVICE, PUSHOVER_SOUND, \ USE_EMAIL, EMAIL_NOTIFY_ONSNATCH, EMAIL_NOTIFY_ONDOWNLOAD, EMAIL_NOTIFY_ONSUBTITLEDOWNLOAD, EMAIL_FROM, \
USE_LIBNOTIFY, LIBNOTIFY_NOTIFY_ONSNATCH, LIBNOTIFY_NOTIFY_ONDOWNLOAD, LIBNOTIFY_NOTIFY_ONSUBTITLEDOWNLOAD, USE_NMJ, NMJ_HOST, NMJ_DATABASE, NMJ_MOUNT, USE_NMJv2, NMJv2_HOST, NMJv2_DATABASE, NMJv2_DBLOC, USE_SYNOINDEX, \ EMAIL_HOST, EMAIL_PORT, EMAIL_TLS, EMAIL_USER, EMAIL_PASSWORD, EMAIL_LIST, EMAIL_OLD_SUBJECTS
USE_SYNOLOGYNOTIFIER, SYNOLOGYNOTIFIER_NOTIFY_ONSNATCH, SYNOLOGYNOTIFIER_NOTIFY_ONDOWNLOAD, SYNOLOGYNOTIFIER_NOTIFY_ONSUBTITLEDOWNLOAD, \ # Anime Settings
USE_EMAIL, EMAIL_OLD_SUBJECTS, EMAIL_HOST, EMAIL_PORT, EMAIL_TLS, EMAIL_USER, EMAIL_PASSWORD, EMAIL_FROM, EMAIL_NOTIFY_ONSNATCH, EMAIL_NOTIFY_ONDOWNLOAD, EMAIL_NOTIFY_ONSUBTITLEDOWNLOAD, EMAIL_LIST, \ global ANIME_TREAT_AS_HDTV, USE_ANIDB, ANIDB_USERNAME, ANIDB_PASSWORD, ANIDB_USE_MYLIST
METADATA_XBMC, METADATA_XBMC_12PLUS, METADATA_MEDIABROWSER, METADATA_PS3, METADATA_KODI, metadata_provider_dict, \
GIT_PATH, MOVE_ASSOCIATED_FILES, POSTPONE_IF_SYNC_FILES, recentSearchScheduler, NFO_RENAME, \
GUI_NAME, DEFAULT_HOME, HOME_LAYOUT, HISTORY_LAYOUT, DISPLAY_SHOW_SPECIALS, EPISODE_VIEW_LAYOUT, EPISODE_VIEW_SORT, EPISODE_VIEW_DISPLAY_PAUSED, EPISODE_VIEW_MISSED_RANGE, FUZZY_DATING, TRIM_ZERO, DATE_PRESET, TIME_PRESET, TIME_PRESET_W_SECONDS, THEME_NAME, \
POSTER_SORTBY, POSTER_SORTDIR, \
METADATA_WDTV, METADATA_TIVO, METADATA_MEDE8ER, IGNORE_WORDS, REQUIRE_WORDS, CALENDAR_UNPROTECTED, CREATE_MISSING_SHOW_DIRS, \
ADD_SHOWS_WO_DIR, REMOVE_FILENAME_CHARS, USE_SUBTITLES, SUBTITLES_LANGUAGES, SUBTITLES_DIR, SUBTITLES_SERVICES_LIST, SUBTITLES_SERVICES_ENABLED, SUBTITLES_HISTORY, SUBTITLES_FINDER_FREQUENCY, subtitlesFinderScheduler, \
USE_FAILED_DOWNLOADS, DELETE_FAILED, ANON_REDIRECT, TMDB_API_KEY, DEBUG, PROXY_SETTING, PROXY_INDEXERS, \
AUTOPOSTPROCESSER_FREQUENCY, DEFAULT_AUTOPOSTPROCESSER_FREQUENCY, MIN_AUTOPOSTPROCESSER_FREQUENCY, \
ANIME_DEFAULT, NAMING_ANIME, USE_ANIDB, ANIDB_USERNAME, ANIDB_PASSWORD, ANIDB_USE_MYLIST, \
SCENE_DEFAULT, BACKLOG_DAYS, SEARCH_UNAIRED, UNAIRED_RECENT_SEARCH_ONLY, ANIME_TREAT_AS_HDTV, \
COOKIE_SECRET, USE_IMDB_INFO, IMDB_ACCOUNTS, DISPLAY_BACKGROUND, DISPLAY_BACKGROUND_TRANSPARENT, DISPLAY_ALL_SEASONS, \
SHOW_TAGS, DEFAULT_SHOW_TAG, SHOWLIST_TAGVIEW, background_mapping_task, CACHE_IMAGE_URL_LIST
if __INITIALIZED__: if __INITIALIZED__:
return False return False
CheckSection(CFG, 'General') for stanza in ('General', 'Blackhole', 'SABnzbd', 'NZBget', 'Emby', 'Kodi', 'XBMC', 'PLEX',
CheckSection(CFG, 'Blackhole') 'Growl', 'Prowl', 'Twitter', 'Boxcar2', 'NMJ', 'NMJv2', 'Synology', 'SynologyNotifier',
CheckSection(CFG, 'SABnzbd') 'pyTivo', 'NMA', 'Pushalot', 'Pushbullet', 'Subtitles'):
CheckSection(CFG, 'NZBget') CheckSection(CFG, stanza)
CheckSection(CFG, 'Emby')
CheckSection(CFG, 'Kodi')
CheckSection(CFG, 'XBMC')
CheckSection(CFG, 'PLEX')
CheckSection(CFG, 'Growl')
CheckSection(CFG, 'Prowl')
CheckSection(CFG, 'Twitter')
CheckSection(CFG, 'Boxcar2')
CheckSection(CFG, 'NMJ')
CheckSection(CFG, 'NMJv2')
CheckSection(CFG, 'Synology')
CheckSection(CFG, 'SynologyNotifier')
CheckSection(CFG, 'pyTivo')
CheckSection(CFG, 'NMA')
CheckSection(CFG, 'Pushalot')
CheckSection(CFG, 'Pushbullet')
CheckSection(CFG, 'Subtitles')
# wanted branch # wanted branch
BRANCH = check_setting_str(CFG, 'General', 'branch', '') BRANCH = check_setting_str(CFG, 'General', 'branch', '')
@ -619,12 +655,9 @@ def initialize(consoleLogging=True):
TIME_PRESET_W_SECONDS = check_setting_str(CFG, 'GUI', 'time_preset', '%I:%M:%S %p') TIME_PRESET_W_SECONDS = check_setting_str(CFG, 'GUI', 'time_preset', '%I:%M:%S %p')
TIME_PRESET = TIME_PRESET_W_SECONDS.replace(u':%S', u'') TIME_PRESET = TIME_PRESET_W_SECONDS.replace(u':%S', u'')
TIMEZONE_DISPLAY = check_setting_str(CFG, 'GUI', 'timezone_display', 'network') TIMEZONE_DISPLAY = check_setting_str(CFG, 'GUI', 'timezone_display', 'network')
DISPLAY_BACKGROUND = bool(check_setting_int(CFG, 'General', 'display_background', 0))
DISPLAY_BACKGROUND_TRANSPARENT = check_setting_str(CFG, 'General', 'display_background_transparent',
'transparent')
DISPLAY_ALL_SEASONS = bool(check_setting_int(CFG, 'General', 'display_all_seasons', 1))
SHOW_TAGS = check_setting_str(CFG, 'GUI', 'show_tags', 'Show List').split(',') SHOW_TAGS = check_setting_str(CFG, 'GUI', 'show_tags', 'Show List').split(',')
DEFAULT_SHOW_TAG = check_setting_str(CFG, 'GUI', 'default_show_tag', 'Show List') SHOW_TAG_DEFAULT = check_setting_str(CFG, 'GUI', 'show_tag_default',
check_setting_str(CFG, 'GUI', 'default_show_tag', 'Show List'))
SHOWLIST_TAGVIEW = check_setting_str(CFG, 'GUI', 'showlist_tagview', 'standard') SHOWLIST_TAGVIEW = check_setting_str(CFG, 'GUI', 'showlist_tagview', 'standard')
ACTUAL_LOG_DIR = check_setting_str(CFG, 'General', 'log_dir', 'Logs') ACTUAL_LOG_DIR = check_setting_str(CFG, 'General', 'log_dir', 'Logs')
@ -639,17 +672,10 @@ def initialize(consoleLogging=True):
SOCKET_TIMEOUT = check_setting_int(CFG, 'General', 'socket_timeout', 30) SOCKET_TIMEOUT = check_setting_int(CFG, 'General', 'socket_timeout', 30)
socket.setdefaulttimeout(SOCKET_TIMEOUT) socket.setdefaulttimeout(SOCKET_TIMEOUT)
try:
WEB_PORT = check_setting_int(CFG, 'General', 'web_port', 8081)
except:
WEB_PORT = 8081
if WEB_PORT < 21 or WEB_PORT > 65535:
WEB_PORT = 8081
WEB_HOST = check_setting_str(CFG, 'General', 'web_host', '0.0.0.0') WEB_HOST = check_setting_str(CFG, 'General', 'web_host', '0.0.0.0')
WEB_IPV6 = bool(check_setting_int(CFG, 'General', 'web_ipv6', 0)) WEB_PORT = minimax(check_setting_int(CFG, 'General', 'web_port', 8081), 8081, 21, 65535)
WEB_ROOT = check_setting_str(CFG, 'General', 'web_root', '').rstrip('/') WEB_ROOT = check_setting_str(CFG, 'General', 'web_root', '').rstrip('/')
WEB_IPV6 = bool(check_setting_int(CFG, 'General', 'web_ipv6', 0))
WEB_LOG = bool(check_setting_int(CFG, 'General', 'web_log', 0)) WEB_LOG = bool(check_setting_int(CFG, 'General', 'web_log', 0))
ENCRYPTION_VERSION = check_setting_int(CFG, 'General', 'encryption_version', 0) ENCRYPTION_VERSION = check_setting_int(CFG, 'General', 'encryption_version', 0)
WEB_USERNAME = check_setting_str(CFG, 'General', 'web_username', '') WEB_USERNAME = check_setting_str(CFG, 'General', 'web_username', '')
@ -740,7 +766,6 @@ def initialize(consoleLogging=True):
ALLOW_HIGH_PRIORITY = bool(check_setting_int(CFG, 'General', 'allow_high_priority', 1)) ALLOW_HIGH_PRIORITY = bool(check_setting_int(CFG, 'General', 'allow_high_priority', 1))
RECENTSEARCH_STARTUP = bool(check_setting_int(CFG, 'General', 'recentsearch_startup', 0)) RECENTSEARCH_STARTUP = bool(check_setting_int(CFG, 'General', 'recentsearch_startup', 0))
BACKLOG_STARTUP = bool(check_setting_int(CFG, 'General', 'backlog_startup', 0))
BACKLOG_NOFULL = bool(check_setting_int(CFG, 'General', 'backlog_nofull', 0)) BACKLOG_NOFULL = bool(check_setting_int(CFG, 'General', 'backlog_nofull', 0))
SKIP_REMOVED_FILES = check_setting_int(CFG, 'General', 'skip_removed_files', 0) SKIP_REMOVED_FILES = check_setting_int(CFG, 'General', 'skip_removed_files', 0)
@ -1033,8 +1058,8 @@ def initialize(consoleLogging=True):
NEWZNAB_DATA = check_setting_str(CFG, 'Newznab', 'newznab_data', '') NEWZNAB_DATA = check_setting_str(CFG, 'Newznab', 'newznab_data', '')
newznabProviderList = providers.getNewznabProviderList(NEWZNAB_DATA) newznabProviderList = providers.getNewznabProviderList(NEWZNAB_DATA)
TORRENTRSS_DATA = check_setting_str(CFG, 'TorrentRss', 'torrentrss_data', '') torrentrss_data = check_setting_str(CFG, 'TorrentRss', 'torrentrss_data', '')
torrentRssProviderList = providers.getTorrentRssProviderList(TORRENTRSS_DATA) torrentRssProviderList = providers.getTorrentRssProviderList(torrentrss_data)
# dynamically load provider settings # dynamically load provider settings
for torrent_prov in [curProvider for curProvider in providers.sortedProviderList() for torrent_prov in [curProvider for curProvider in providers.sortedProviderList()
@ -1077,7 +1102,7 @@ def initialize(consoleLogging=True):
if hasattr(torrent_prov, 'enable_recentsearch'): if hasattr(torrent_prov, 'enable_recentsearch'):
torrent_prov.enable_recentsearch = bool(check_setting_int(CFG, prov_id_uc, torrent_prov.enable_recentsearch = bool(check_setting_int(CFG, prov_id_uc,
prov_id + '_enable_recentsearch', 1)) or \ prov_id + '_enable_recentsearch', 1)) or \
not getattr(torrent_prov, 'supports_backlog', True) not getattr(torrent_prov, 'supports_backlog')
if hasattr(torrent_prov, 'enable_backlog'): if hasattr(torrent_prov, 'enable_backlog'):
torrent_prov.enable_backlog = bool(check_setting_int(CFG, prov_id_uc, prov_id + '_enable_backlog', 1)) torrent_prov.enable_backlog = bool(check_setting_int(CFG, prov_id_uc, prov_id + '_enable_backlog', 1))
if hasattr(torrent_prov, 'search_mode'): if hasattr(torrent_prov, 'search_mode'):
@ -1104,7 +1129,7 @@ def initialize(consoleLogging=True):
if hasattr(nzb_prov, 'enable_recentsearch'): if hasattr(nzb_prov, 'enable_recentsearch'):
nzb_prov.enable_recentsearch = bool(check_setting_int(CFG, prov_id_uc, nzb_prov.enable_recentsearch = bool(check_setting_int(CFG, prov_id_uc,
prov_id + '_enable_recentsearch', 1)) or \ prov_id + '_enable_recentsearch', 1)) or \
not getattr(nzb_prov, 'supports_backlog', True) not getattr(nzb_prov, 'supports_backlog')
if hasattr(nzb_prov, 'enable_backlog'): if hasattr(nzb_prov, 'enable_backlog'):
nzb_prov.enable_backlog = bool(check_setting_int(CFG, prov_id_uc, prov_id + '_enable_backlog', 1)) nzb_prov.enable_backlog = bool(check_setting_int(CFG, prov_id_uc, prov_id + '_enable_backlog', 1))
@ -1113,7 +1138,7 @@ def initialize(consoleLogging=True):
save_config() save_config()
# start up all the threads # start up all the threads
logger.sb_log_instance.initLogging(consoleLogging=consoleLogging) logger.sb_log_instance.initLogging(consoleLogging=console_logging)
# initialize the main SB database # initialize the main SB database
my_db = db.DBConnection() my_db = db.DBConnection()
@ -1183,8 +1208,7 @@ def initialize(consoleLogging=True):
search_recent.RecentSearcher(), search_recent.RecentSearcher(),
cycleTime=update_interval, cycleTime=update_interval,
threadName='RECENTSEARCHER', threadName='RECENTSEARCHER',
run_delay=update_now if RECENTSEARCH_STARTUP run_delay=update_now if RECENTSEARCH_STARTUP else datetime.timedelta(minutes=5),
else datetime.timedelta(minutes=5),
prevent_cycle_run=searchQueueScheduler.action.is_recentsearch_in_progress) prevent_cycle_run=searchQueueScheduler.action.is_recentsearch_in_progress)
if [x for x in providers.sortedProviderList() if x.is_active() and if [x for x in providers.sortedProviderList() if x.is_active() and
@ -1229,17 +1253,16 @@ def initialize(consoleLogging=True):
# processors # processors
autoPostProcesserScheduler = scheduler.Scheduler( autoPostProcesserScheduler = scheduler.Scheduler(
auto_post_processer.PostProcesser(), auto_post_processer.PostProcesser(),
cycleTime=datetime.timedelta( cycleTime=datetime.timedelta(minutes=AUTOPOSTPROCESSER_FREQUENCY),
minutes=AUTOPOSTPROCESSER_FREQUENCY),
threadName='POSTPROCESSER', threadName='POSTPROCESSER',
silent=not PROCESS_AUTOMATICALLY) silent=not PROCESS_AUTOMATICALLY)
"""
traktCheckerScheduler = scheduler.Scheduler( traktCheckerScheduler = scheduler.Scheduler(
traktChecker.TraktChecker(), traktChecker.TraktChecker(),
cycleTime=datetime.timedelta(hours=1), cycleTime=datetime.timedelta(hours=1),
threadName='TRAKTCHECKER', threadName='TRAKTCHECKER',
silent=not USE_TRAKT) silent=not USE_TRAKT)
"""
subtitlesFinderScheduler = scheduler.Scheduler( subtitlesFinderScheduler = scheduler.Scheduler(
subtitles.SubtitlesFinder(), subtitles.SubtitlesFinder(),
cycleTime=datetime.timedelta(hours=SUBTITLES_FINDER_FREQUENCY), cycleTime=datetime.timedelta(hours=SUBTITLES_FINDER_FREQUENCY),
@ -1247,7 +1270,6 @@ def initialize(consoleLogging=True):
silent=not USE_SUBTITLES) silent=not USE_SUBTITLES)
showList = [] showList = []
loadingShowList = {}
background_mapping_task = threading.Thread(name='LOAD-MAPPINGS', target=indexermapper.load_mapped_ids) background_mapping_task = threading.Thread(name='LOAD-MAPPINGS', target=indexermapper.load_mapped_ids)
@ -1255,60 +1277,56 @@ def initialize(consoleLogging=True):
return True return True
def enabled_schedulers(is_init=False):
# ([], [traktCheckerScheduler])[USE_TRAKT] + \
for s in ([], [events])[is_init] + \
[recentSearchScheduler, backlogSearchScheduler, showUpdateScheduler,
versionCheckScheduler, showQueueScheduler, searchQueueScheduler] + \
([], [properFinderScheduler])[DOWNLOAD_PROPERS] + \
([], [autoPostProcesserScheduler])[PROCESS_AUTOMATICALLY] + \
([], [subtitlesFinderScheduler])[USE_SUBTITLES] + \
([events], [])[is_init]:
yield s
def start(): def start():
global __INITIALIZED__, backlogSearchScheduler, \ global started
showUpdateScheduler, versionCheckScheduler, showQueueScheduler, \
properFinderScheduler, autoPostProcesserScheduler, searchQueueScheduler, \
subtitlesFinderScheduler, USE_SUBTITLES, traktCheckerScheduler, \
recentSearchScheduler, events, started, background_mapping_task
with INIT_LOCK: with INIT_LOCK:
if __INITIALIZED__: if __INITIALIZED__:
# Load all Indexer mappings in background # Load all Indexer mappings in background
indexermapper.defunct_indexer = [i for i in indexerApi().all_indexers if indexerApi(i).config.get('defunct')] indexermapper.defunct_indexer = [
i for i in indexerApi().all_indexers if indexerApi(i).config.get('defunct')]
indexermapper.indexer_list = [i for i in indexerApi().all_indexers] indexermapper.indexer_list = [i for i in indexerApi().all_indexers]
background_mapping_task.start() background_mapping_task.start()
# start sysetm events queue for thread in enabled_schedulers(is_init=True):
events.start() thread.start()
# start the recent search scheduler
recentSearchScheduler.start()
# start the backlog scheduler
backlogSearchScheduler.start()
# start the show updater
showUpdateScheduler.start()
# start the version checker
versionCheckScheduler.start()
# start the queue checker
showQueueScheduler.start()
# start the search queue checker
searchQueueScheduler.start()
# start the queue checker
if DOWNLOAD_PROPERS:
properFinderScheduler.start()
# start the proper finder
if PROCESS_AUTOMATICALLY:
autoPostProcesserScheduler.start()
# start the subtitles finder
if USE_SUBTITLES:
subtitlesFinderScheduler.start()
# start the trakt checker
# if USE_TRAKT:
# traktCheckerScheduler.start()
started = True started = True
def restart(soft=True):
if soft:
halt()
save_all()
logger.log(u'Re-initializing all data')
initialize()
else:
events.put(events.SystemEvent.RESTART)
def sig_handler(signum=None, _=None):
is_ctrlbreak = 'win32' == sys.platform and signal.SIGBREAK == signum
msg = u'Signal "%s" found' % (signal.SIGINT == signum and 'CTRL-C' or is_ctrlbreak and 'CTRL+BREAK' or
signal.SIGTERM == signum and 'Termination' or signum)
if None is signum or signum in (signal.SIGINT, signal.SIGTERM) or is_ctrlbreak:
logger.log('%s, saving and exiting...' % msg)
events.put(events.SystemEvent.SHUTDOWN)
else:
logger.log('%s, not exiting' % msg)
def halt(): def halt():
global __INITIALIZED__, started global __INITIALIZED__, started
@ -1318,83 +1336,33 @@ def halt():
logger.log(u'Aborting all threads') logger.log(u'Aborting all threads')
schedulers = [ for thread in enabled_schedulers():
recentSearchScheduler,
backlogSearchScheduler,
showUpdateScheduler,
versionCheckScheduler,
showQueueScheduler,
searchQueueScheduler,
properFinderScheduler,
autoPostProcesserScheduler,
subtitlesFinderScheduler,
events
]
for thread in schedulers:
thread.stop.set() thread.stop.set()
for thread in schedulers: for thread in enabled_schedulers():
logger.log('Waiting for the %s thread to exit' % thread.name) logger.log('Waiting for the %s thread to exit' % thread.name)
try: try:
thread.join(10) thread.join(10)
except RuntimeError: except RuntimeError:
pass pass
if PROCESS_AUTOMATICALLY:
autoPostProcesserScheduler.stop.set()
logger.log(u'Waiting for the POSTPROCESSER thread to exit')
try:
autoPostProcesserScheduler.join(10)
except:
pass
if DOWNLOAD_PROPERS:
properFinderScheduler.stop.set()
logger.log(u'Waiting for the PROPERFINDER thread to exit')
try:
properFinderScheduler.join(10)
except:
pass
if USE_SUBTITLES:
subtitlesFinderScheduler.stop.set()
logger.log(u'Waiting for the SUBTITLESFINDER thread to exit')
try:
subtitlesFinderScheduler.join(10)
except:
pass
if ADBA_CONNECTION: if ADBA_CONNECTION:
try: try:
ADBA_CONNECTION.logout() ADBA_CONNECTION.logout()
except AniDBBannedError as e: except AniDBBannedError as e:
logger.log(u'ANIDB Error %s' % ex(e), logger.DEBUG) logger.log(u'ANIDB Error %s' % ex(e), logger.DEBUG)
except AniDBError as e: except AniDBError:
pass pass
logger.log(u'Waiting for the ANIDB CONNECTION thread to exit') logger.log(u'Waiting for the ANIDB CONNECTION thread to exit')
try: try:
ADBA_CONNECTION.join(10) ADBA_CONNECTION.join(10)
except: except (StandardError, Exception):
pass pass
__INITIALIZED__ = False __INITIALIZED__ = False
started = False started = False
def sig_handler(signum=None, frame=None):
is_ctrlbreak = 'win32' == sys.platform and signal.SIGBREAK == signum
msg = u'Signal "%s" found' % (signal.SIGINT == signum and 'CTRL-C' or is_ctrlbreak and 'CTRL+BREAK' or
signal.SIGTERM == signum and 'Termination' or signum)
if None is signum or signum in (signal.SIGINT, signal.SIGTERM) or is_ctrlbreak:
logger.log('%s, saving and exiting...' % msg)
events.put(events.SystemEvent.SHUTDOWN)
else:
logger.log('%s, not exiting' % msg)
def save_all(): def save_all():
global showList global showList
@ -1408,16 +1376,6 @@ def save_all():
save_config() save_config()
def restart(soft=True):
if soft:
halt()
save_all()
logger.log(u'Re-initializing all data')
initialize()
else:
events.put(events.SystemEvent.RESTART)
def save_config(): def save_config():
new_config = ConfigObj() new_config = ConfigObj()
new_config.filename = CONFIG_FILE new_config.filename = CONFIG_FILE
@ -1463,7 +1421,6 @@ def save_config():
new_config['General']['check_propers_interval'] = CHECK_PROPERS_INTERVAL new_config['General']['check_propers_interval'] = CHECK_PROPERS_INTERVAL
new_config['General']['allow_high_priority'] = int(ALLOW_HIGH_PRIORITY) new_config['General']['allow_high_priority'] = int(ALLOW_HIGH_PRIORITY)
new_config['General']['recentsearch_startup'] = int(RECENTSEARCH_STARTUP) new_config['General']['recentsearch_startup'] = int(RECENTSEARCH_STARTUP)
new_config['General']['backlog_startup'] = int(BACKLOG_STARTUP)
new_config['General']['backlog_nofull'] = int(BACKLOG_NOFULL) new_config['General']['backlog_nofull'] = int(BACKLOG_NOFULL)
new_config['General']['skip_removed_files'] = int(SKIP_REMOVED_FILES) new_config['General']['skip_removed_files'] = int(SKIP_REMOVED_FILES)
new_config['General']['quality_default'] = int(QUALITY_DEFAULT) new_config['General']['quality_default'] = int(QUALITY_DEFAULT)
@ -1501,9 +1458,6 @@ def save_config():
new_config['General']['sort_article'] = int(SORT_ARTICLE) new_config['General']['sort_article'] = int(SORT_ARTICLE)
new_config['General']['proxy_setting'] = PROXY_SETTING new_config['General']['proxy_setting'] = PROXY_SETTING
new_config['General']['proxy_indexers'] = int(PROXY_INDEXERS) new_config['General']['proxy_indexers'] = int(PROXY_INDEXERS)
new_config['General']['display_background'] = int(DISPLAY_BACKGROUND)
new_config['General']['display_background_transparent'] = DISPLAY_BACKGROUND_TRANSPARENT
new_config['General']['display_all_seasons'] = int(DISPLAY_ALL_SEASONS)
new_config['General']['metadata_xbmc'] = METADATA_XBMC new_config['General']['metadata_xbmc'] = METADATA_XBMC
new_config['General']['metadata_xbmc_12plus'] = METADATA_XBMC_12PLUS new_config['General']['metadata_xbmc_12plus'] = METADATA_XBMC_12PLUS
@ -1800,6 +1754,10 @@ def save_config():
new_config['GUI']['date_preset'] = DATE_PRESET new_config['GUI']['date_preset'] = DATE_PRESET
new_config['GUI']['time_preset'] = TIME_PRESET_W_SECONDS new_config['GUI']['time_preset'] = TIME_PRESET_W_SECONDS
new_config['GUI']['timezone_display'] = TIMEZONE_DISPLAY new_config['GUI']['timezone_display'] = TIMEZONE_DISPLAY
new_config['GUI']['show_tags'] = ','.join(SHOW_TAGS)
new_config['GUI']['showlist_tagview'] = SHOWLIST_TAGVIEW
new_config['GUI']['home_layout'] = HOME_LAYOUT new_config['GUI']['home_layout'] = HOME_LAYOUT
new_config['GUI']['history_layout'] = HISTORY_LAYOUT new_config['GUI']['history_layout'] = HISTORY_LAYOUT
new_config['GUI']['display_show_specials'] = int(DISPLAY_SHOW_SPECIALS) new_config['GUI']['display_show_specials'] = int(DISPLAY_SHOW_SPECIALS)
@ -1811,7 +1769,7 @@ def save_config():
new_config['GUI']['poster_sortdir'] = POSTER_SORTDIR new_config['GUI']['poster_sortdir'] = POSTER_SORTDIR
new_config['GUI']['show_tags'] = ','.join(SHOW_TAGS) new_config['GUI']['show_tags'] = ','.join(SHOW_TAGS)
new_config['GUI']['showlist_tagview'] = SHOWLIST_TAGVIEW new_config['GUI']['showlist_tagview'] = SHOWLIST_TAGVIEW
new_config['GUI']['default_show_tag'] = DEFAULT_SHOW_TAG new_config['GUI']['show_tag_default'] = SHOW_TAG_DEFAULT
new_config['Subtitles'] = {} new_config['Subtitles'] = {}
new_config['Subtitles']['use_subtitles'] = int(USE_SUBTITLES) new_config['Subtitles']['use_subtitles'] = int(USE_SUBTITLES)
@ -1842,14 +1800,11 @@ def save_config():
def launch_browser(start_port=None): def launch_browser(start_port=None):
if not start_port: if not start_port:
start_port = WEB_PORT start_port = WEB_PORT
if ENABLE_HTTPS: browser_url = 'http%s://localhost:%d%s' % (('s' or '')[not ENABLE_HTTPS], start_port, WEB_ROOT)
browser_url = 'https://localhost:%d%s' % (start_port, WEB_ROOT)
else:
browser_url = 'http://localhost:%d%s' % (start_port, WEB_ROOT)
try: try:
webbrowser.open(browser_url, 2, 1) webbrowser.open(browser_url, 2, 1)
except: except (StandardError, Exception):
try: try:
webbrowser.open(browser_url, 1, 1) webbrowser.open(browser_url, 1, 1)
except: except (StandardError, Exception):
logger.log(u'Unable to launch a browser', logger.ERROR) logger.log('Unable to launch a browser', logger.ERROR)

View file

@ -740,7 +740,6 @@ class ConfigMigrator():
def _migrate_v8(self): def _migrate_v8(self):
# removing settings from gui and making it a hidden debug option # removing settings from gui and making it a hidden debug option
sickbeard.RECENTSEARCH_STARTUP = False sickbeard.RECENTSEARCH_STARTUP = False
sickbeard.BACKLOG_STARTUP = False
def _migrate_v9(self): def _migrate_v9(self):
sickbeard.PUSHBULLET_ACCESS_TOKEN = check_setting_str(self.config_obj, 'Pushbullet', 'pushbullet_api', '') sickbeard.PUSHBULLET_ACCESS_TOKEN = check_setting_str(self.config_obj, 'Pushbullet', 'pushbullet_api', '')

View file

@ -1106,12 +1106,12 @@ class Home(MainHandler):
if str(pid) != str(sickbeard.PID): if str(pid) != str(sickbeard.PID):
return self.redirect('/home/') return self.redirect('/home/')
t = PageTemplate(headers=self.request.headers, file='restart.tmpl')
t.shutdown = True
sickbeard.events.put(sickbeard.events.SystemEvent.SHUTDOWN) sickbeard.events.put(sickbeard.events.SystemEvent.SHUTDOWN)
title = 'Shutting down' return t.respond()
message = 'SickGear is shutting down...'
return self._genericMessage(title, message)
def restart(self, pid=None): def restart(self, pid=None):
@ -1119,8 +1119,8 @@ class Home(MainHandler):
return self.redirect('/home/') return self.redirect('/home/')
t = PageTemplate(headers=self.request.headers, file='restart.tmpl') t = PageTemplate(headers=self.request.headers, file='restart.tmpl')
t.shutdown = False
# restart
sickbeard.events.put(sickbeard.events.SystemEvent.RESTART) sickbeard.events.put(sickbeard.events.SystemEvent.RESTART)
return t.respond() return t.respond()
@ -4275,7 +4275,7 @@ class ConfigGeneral(Config):
sickbeard.SCENE_DEFAULT = config.checkbox_to_value(default_scene) sickbeard.SCENE_DEFAULT = config.checkbox_to_value(default_scene)
sickbeard.SUBTITLES_DEFAULT = config.checkbox_to_value(default_subtitles) sickbeard.SUBTITLES_DEFAULT = config.checkbox_to_value(default_subtitles)
sickbeard.ANIME_DEFAULT = config.checkbox_to_value(default_anime) sickbeard.ANIME_DEFAULT = config.checkbox_to_value(default_anime)
sickbeard.DEFAULT_SHOW_TAG = default_tag sickbeard.SHOW_TAG_DEFAULT = default_tag
sickbeard.save_config() sickbeard.save_config()
@ -4311,7 +4311,6 @@ class ConfigGeneral(Config):
proxy_setting=None, proxy_indexers=None, anon_redirect=None, git_path=None, git_remote=None, calendar_unprotected=None, proxy_setting=None, proxy_indexers=None, anon_redirect=None, git_path=None, git_remote=None, calendar_unprotected=None,
fuzzy_dating=None, trim_zero=None, date_preset=None, date_preset_na=None, time_preset=None, fuzzy_dating=None, trim_zero=None, date_preset=None, date_preset_na=None, time_preset=None,
indexer_timeout=None, rootDir=None, theme_name=None, default_home=None, use_imdb_info=None, indexer_timeout=None, rootDir=None, theme_name=None, default_home=None, use_imdb_info=None,
display_background=None, display_background_transparent=None, display_all_seasons=None,
show_tags=None, showlist_tagview=None): show_tags=None, showlist_tagview=None):
results = [] results = []
@ -4331,9 +4330,6 @@ class ConfigGeneral(Config):
sickbeard.LAUNCH_BROWSER = config.checkbox_to_value(launch_browser) sickbeard.LAUNCH_BROWSER = config.checkbox_to_value(launch_browser)
sickbeard.HOME_SEARCH_FOCUS = config.checkbox_to_value(home_search_focus) sickbeard.HOME_SEARCH_FOCUS = config.checkbox_to_value(home_search_focus)
sickbeard.USE_IMDB_INFO = config.checkbox_to_value(use_imdb_info) sickbeard.USE_IMDB_INFO = config.checkbox_to_value(use_imdb_info)
sickbeard.DISPLAY_BACKGROUND = config.checkbox_to_value(display_background)
sickbeard.DISPLAY_BACKGROUND_TRANSPARENT = display_background_transparent
sickbeard.DISPLAY_ALL_SEASONS = config.checkbox_to_value(display_all_seasons)
sickbeard.SORT_ARTICLE = config.checkbox_to_value(sort_article) sickbeard.SORT_ARTICLE = config.checkbox_to_value(sort_article)
sickbeard.CPU_PRESET = cpu_preset sickbeard.CPU_PRESET = cpu_preset
sickbeard.FILE_LOGGING_PRESET = file_logging_preset sickbeard.FILE_LOGGING_PRESET = file_logging_preset

View file

@ -35,7 +35,7 @@ from sickbeard.name_parser.parser import NameParser
from sickbeard.tv import TVShow from sickbeard.tv import TVShow
class XEMBasicTests(test.SickbeardTestDBCase): class XEMBasicTests(test.SickbeardTestDBCase):
def loadShowsFromDB(self): def load_shows_from_db(self):
""" """
Populates the showList with shows from the database Populates the showList with shows from the database
""" """