mirror of
https://github.com/moparisthebest/SickRage
synced 2025-01-07 11:58:01 -05:00
Fixed startup/restart/shutdown issues on Windows, Linux, FreeBSD platforms tested.
Fixed for updating issues. Fixed high cpu and memory usage.
This commit is contained in:
parent
1fc909299d
commit
12ac388dc2
227
SickBeard.py
227
SickBeard.py
@ -20,8 +20,10 @@
|
||||
# Check needed software dependencies to nudge users to fix their setup
|
||||
from __future__ import with_statement
|
||||
|
||||
import time
|
||||
import sys
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
if sys.version_info < (2, 6):
|
||||
print "Sorry, requires Python 2.6 or 2.7."
|
||||
@ -59,13 +61,12 @@ import sickbeard
|
||||
from sickbeard import db
|
||||
from sickbeard.tv import TVShow
|
||||
from sickbeard import logger
|
||||
from sickbeard import webserveInit
|
||||
from sickbeard.webserveInit import SRWebServer
|
||||
from sickbeard.version import SICKBEARD_VERSION
|
||||
from sickbeard.databases.mainDB import MIN_DB_VERSION
|
||||
from sickbeard.databases.mainDB import MAX_DB_VERSION
|
||||
|
||||
from lib.configobj import ConfigObj
|
||||
from tornado.ioloop import IOLoop
|
||||
from daemon import Daemon
|
||||
|
||||
signal.signal(signal.SIGINT, sickbeard.sig_handler)
|
||||
@ -73,14 +74,9 @@ signal.signal(signal.SIGTERM, sickbeard.sig_handler)
|
||||
|
||||
throwaway = datetime.datetime.strptime('20110101', '%Y%m%d')
|
||||
|
||||
restart = False
|
||||
daemon = None
|
||||
startPort = None
|
||||
forceUpdate = None
|
||||
noLaunch = None
|
||||
web_options = None
|
||||
class SickRage(object):
|
||||
|
||||
def loadShowsFromDB():
|
||||
def loadShowsFromDB(self):
|
||||
"""
|
||||
Populates the showList with shows from the database
|
||||
"""
|
||||
@ -101,9 +97,7 @@ def loadShowsFromDB():
|
||||
logger.ERROR)
|
||||
logger.log(traceback.format_exc(), logger.DEBUG)
|
||||
|
||||
# TODO: update the existing shows if the showlist has something in it
|
||||
|
||||
def restore(srcDir, dstDir):
|
||||
def restore(self, srcDir, dstDir):
|
||||
try:
|
||||
for file in os.listdir(srcDir):
|
||||
srcFile = os.path.join(srcDir, file)
|
||||
@ -117,22 +111,23 @@ def restore(srcDir, dstDir):
|
||||
except:
|
||||
return False
|
||||
|
||||
def main():
|
||||
"""
|
||||
TV for me
|
||||
"""
|
||||
|
||||
global daemon, startPort, forceUpdate, noLaunch, web_options
|
||||
def __init__(self):
|
||||
self.daemon = None
|
||||
self.webserver = None
|
||||
self.runAsDaemon = False
|
||||
self.CREATEPID = False
|
||||
self.PIDFILE = None
|
||||
self.forceUpdate = False
|
||||
self.forcedPort = None
|
||||
self.noLaunch = False
|
||||
|
||||
def start(self):
|
||||
# do some preliminary stuff
|
||||
sickbeard.MY_FULLNAME = os.path.normpath(os.path.abspath(__file__))
|
||||
sickbeard.MY_NAME = os.path.basename(sickbeard.MY_FULLNAME)
|
||||
sickbeard.PROG_DIR = os.path.dirname(sickbeard.MY_FULLNAME)
|
||||
sickbeard.DATA_DIR = sickbeard.PROG_DIR
|
||||
sickbeard.MY_ARGS = sys.argv[1:]
|
||||
sickbeard.DAEMON = False
|
||||
sickbeard.CREATEPID = False
|
||||
|
||||
sickbeard.SYS_ENCODING = None
|
||||
|
||||
try:
|
||||
@ -158,7 +153,7 @@ def main():
|
||||
sys.exit(1)
|
||||
|
||||
# Need console logging for SickBeard.py and SickBeard-console.exe
|
||||
consoleLogging = (not hasattr(sys, "frozen")) or (sickbeard.MY_NAME.lower().find('-console') > 0)
|
||||
self.consoleLogging = (not hasattr(sys, "frozen")) or (sickbeard.MY_NAME.lower().find('-console') > 0)
|
||||
|
||||
# Rename the main thread
|
||||
threading.currentThread().name = "MAIN"
|
||||
@ -171,38 +166,34 @@ def main():
|
||||
print "Available Options: --quiet, --forceupdate, --port, --daemon, --pidfile, --config, --datadir"
|
||||
sys.exit()
|
||||
|
||||
forceUpdate = False
|
||||
forcedPort = None
|
||||
noLaunch = False
|
||||
|
||||
for o, a in opts:
|
||||
# For now we'll just silence the logging
|
||||
if o in ('-q', '--quiet'):
|
||||
consoleLogging = False
|
||||
self.consoleLogging = False
|
||||
|
||||
# Should we update (from indexer) all shows in the DB right away?
|
||||
if o in ('-f', '--forceupdate'):
|
||||
forceUpdate = True
|
||||
self.forceUpdate = True
|
||||
|
||||
# Suppress launching web browser
|
||||
# Needed for OSes without default browser assigned
|
||||
# Prevent duplicate browser window when restarting in the app
|
||||
if o in ('--nolaunch',):
|
||||
noLaunch = True
|
||||
self.noLaunch = True
|
||||
|
||||
# Override default/configured port
|
||||
if o in ('-p', '--port'):
|
||||
forcedPort = int(a)
|
||||
self.forcedPort = int(a)
|
||||
|
||||
# Run as a double forked daemon
|
||||
if o in ('-d', '--daemon'):
|
||||
sickbeard.DAEMON = True
|
||||
self.runAsDaemon = True
|
||||
# When running as daemon disable consoleLogging and don't start browser
|
||||
consoleLogging = False
|
||||
noLaunch = True
|
||||
self.consoleLogging = False
|
||||
self.noLaunch = True
|
||||
|
||||
if sys.platform == 'win32':
|
||||
sickbeard.DAEMON = False
|
||||
self.runAsDaemon = False
|
||||
|
||||
# Specify folder to load the config file from
|
||||
if o in ('--config',):
|
||||
@ -218,27 +209,27 @@ def main():
|
||||
|
||||
# Write a pidfile if requested
|
||||
if o in ('--pidfile',):
|
||||
sickbeard.CREATEPID = True
|
||||
sickbeard.PIDFILE = str(a)
|
||||
self.CREATEPID = True
|
||||
self.PIDFILE = str(a)
|
||||
|
||||
# If the pidfile already exists, sickbeard may still be running, so exit
|
||||
if os.path.exists(sickbeard.PIDFILE):
|
||||
sys.exit("PID file: " + sickbeard.PIDFILE + " already exists. Exiting.")
|
||||
if os.path.exists(self.PIDFILE):
|
||||
sys.exit("PID file: " + self.PIDFILE + " already exists. Exiting.")
|
||||
|
||||
# The pidfile is only useful in daemon mode, make sure we can write the file properly
|
||||
if sickbeard.CREATEPID:
|
||||
if sickbeard.DAEMON:
|
||||
pid_dir = os.path.dirname(sickbeard.PIDFILE)
|
||||
if self.CREATEPID:
|
||||
if self.runAsDaemon:
|
||||
pid_dir = os.path.dirname(self.PIDFILE)
|
||||
if not os.access(pid_dir, os.F_OK):
|
||||
sys.exit("PID dir: " + pid_dir + " doesn't exist. Exiting.")
|
||||
if not os.access(pid_dir, os.W_OK):
|
||||
sys.exit("PID dir: " + pid_dir + " must be writable (write permissions). Exiting.")
|
||||
|
||||
else:
|
||||
if consoleLogging:
|
||||
if self.consoleLogging:
|
||||
sys.stdout.write("Not running in daemon mode. PID file creation disabled.\n")
|
||||
|
||||
sickbeard.CREATEPID = False
|
||||
self.CREATEPID = False
|
||||
|
||||
# If they don't specify a config file then put it in the data dir
|
||||
if not sickbeard.CONFIG_FILE:
|
||||
@ -266,16 +257,13 @@ def main():
|
||||
# Check if we need to perform a restore first
|
||||
restoreDir = os.path.join(sickbeard.DATA_DIR, 'restore')
|
||||
if os.path.exists(restoreDir):
|
||||
if restore(restoreDir, sickbeard.DATA_DIR):
|
||||
if self.restore(restoreDir, sickbeard.DATA_DIR):
|
||||
logger.log(u"Restore successful...")
|
||||
else:
|
||||
logger.log(u"Restore FAILED!", logger.ERROR)
|
||||
|
||||
os.chdir(sickbeard.DATA_DIR)
|
||||
|
||||
if consoleLogging:
|
||||
print "Starting up SickRage " + SICKBEARD_VERSION + " from " + sickbeard.CONFIG_FILE
|
||||
|
||||
# Load the config and publish it to the sickbeard package
|
||||
if not os.path.isfile(sickbeard.CONFIG_FILE):
|
||||
logger.log(u"Unable to find '" + sickbeard.CONFIG_FILE + "' , all settings will be default!", logger.ERROR)
|
||||
@ -297,36 +285,43 @@ def main():
|
||||
"If you have used other forks of SB, your database may be unusable due to their modifications.")
|
||||
|
||||
# Initialize the config and our threads
|
||||
sickbeard.initialize(consoleLogging=consoleLogging)
|
||||
sickbeard.initialize(consoleLogging=self.consoleLogging)
|
||||
|
||||
if forcedPort:
|
||||
logger.log(u"Forcing web server to port " + str(forcedPort))
|
||||
startPort = forcedPort
|
||||
if self.runAsDaemon:
|
||||
self.daemon = Daemon(self.PIDFILE or os.path.join(sickbeard.DATA_DIR, 'sickbeard.pid'))
|
||||
self.daemon.daemonize()
|
||||
|
||||
# Get PID
|
||||
sickbeard.PID = os.getpid()
|
||||
|
||||
if self.forcedPort:
|
||||
logger.log(u"Forcing web server to port " + str(self.forcedPort))
|
||||
self.startPort = self.forcedPort
|
||||
else:
|
||||
startPort = sickbeard.WEB_PORT
|
||||
self.startPort = sickbeard.WEB_PORT
|
||||
|
||||
if sickbeard.WEB_LOG:
|
||||
log_dir = sickbeard.LOG_DIR
|
||||
self.log_dir = sickbeard.LOG_DIR
|
||||
else:
|
||||
log_dir = None
|
||||
self.log_dir = None
|
||||
|
||||
# sickbeard.WEB_HOST is available as a configuration value in various
|
||||
# places but is not configurable. It is supported here for historic reasons.
|
||||
if sickbeard.WEB_HOST and sickbeard.WEB_HOST != '0.0.0.0':
|
||||
webhost = sickbeard.WEB_HOST
|
||||
self.webhost = sickbeard.WEB_HOST
|
||||
else:
|
||||
if sickbeard.WEB_IPV6:
|
||||
webhost = '::'
|
||||
self.webhost = '::'
|
||||
else:
|
||||
webhost = '0.0.0.0'
|
||||
self.webhost = '0.0.0.0'
|
||||
|
||||
# web server options
|
||||
web_options = {
|
||||
'port': int(startPort),
|
||||
'host': webhost,
|
||||
self.web_options = {
|
||||
'port': int(self.startPort),
|
||||
'host': self.webhost,
|
||||
'data_root': os.path.join(sickbeard.PROG_DIR, 'gui', sickbeard.GUI_NAME),
|
||||
'web_root': sickbeard.WEB_ROOT,
|
||||
'log_dir': log_dir,
|
||||
'log_dir': self.log_dir,
|
||||
'username': sickbeard.WEB_USERNAME,
|
||||
'password': sickbeard.WEB_PASSWORD,
|
||||
'enable_https': sickbeard.ENABLE_HTTPS,
|
||||
@ -335,69 +330,87 @@ def main():
|
||||
'https_key': sickbeard.HTTPS_KEY,
|
||||
}
|
||||
|
||||
# Start SickRage
|
||||
if daemon and daemon.is_running():
|
||||
daemon.restart(daemonize=sickbeard.DAEMON)
|
||||
else:
|
||||
daemon = SickRage(sickbeard.PIDFILE)
|
||||
daemon.start(daemonize=sickbeard.DAEMON)
|
||||
|
||||
class SickRage(Daemon):
|
||||
def run(self):
|
||||
global restart, startPort, forceUpdate, noLaunch, web_options
|
||||
|
||||
# Use this PID for everything
|
||||
sickbeard.PID = os.getpid()
|
||||
|
||||
# start web server
|
||||
try:
|
||||
webserveInit.initWebServer(web_options)
|
||||
self.webserver = SRWebServer(self.web_options)
|
||||
self.webserver.start()
|
||||
except IOError:
|
||||
logger.log(u"Unable to start web server, is something else running on port %d?" % startPort, logger.ERROR)
|
||||
if sickbeard.LAUNCH_BROWSER and not sickbeard.DAEMON:
|
||||
logger.log(u"Unable to start web server, is something else running on port %d?" % self.startPort,
|
||||
logger.ERROR)
|
||||
if sickbeard.LAUNCH_BROWSER and not self.runAsDaemon:
|
||||
logger.log(u"Launching browser and exiting", logger.ERROR)
|
||||
sickbeard.launchBrowser(startPort)
|
||||
sys.exit()
|
||||
sickbeard.launchBrowser(self.startPort)
|
||||
os._exit(1)
|
||||
|
||||
if self.consoleLogging:
|
||||
print "Starting up SickRage " + SICKBEARD_VERSION + " from " + sickbeard.CONFIG_FILE
|
||||
|
||||
# Build from the DB to start with
|
||||
loadShowsFromDB()
|
||||
self.loadShowsFromDB()
|
||||
|
||||
# Fire up all our threads
|
||||
sickbeard.start()
|
||||
|
||||
# Launch browser if we're supposed to
|
||||
if sickbeard.LAUNCH_BROWSER and not noLaunch:
|
||||
sickbeard.launchBrowser(startPort)
|
||||
|
||||
# Start an update if we're supposed to
|
||||
if forceUpdate or sickbeard.UPDATE_SHOWS_ON_START:
|
||||
if self.forceUpdate or sickbeard.UPDATE_SHOWS_ON_START:
|
||||
sickbeard.showUpdateScheduler.action.run(force=True) # @UndefinedVariable
|
||||
|
||||
if sickbeard.LAUNCH_BROWSER and not (noLaunch or sickbeard.DAEMON or restart):
|
||||
sickbeard.launchBrowser(startPort)
|
||||
if sickbeard.LAUNCH_BROWSER and not (self.noLaunch or self.runAsDaemon):
|
||||
sickbeard.launchBrowser(self.startPort)
|
||||
|
||||
# reset this if sickrage was restarted
|
||||
restart = False
|
||||
|
||||
# start IO loop
|
||||
IOLoop.current().start()
|
||||
|
||||
# close IO loop
|
||||
IOLoop.current().close(True)
|
||||
|
||||
# stop all tasks
|
||||
sickbeard.halt()
|
||||
|
||||
# save all shows to DB
|
||||
sickbeard.saveAll()
|
||||
while(sickbeard.started):
|
||||
time.sleep(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
if sys.hexversion >= 0x020600F0:
|
||||
freeze_support()
|
||||
|
||||
while(not sickbeard.shutdown):
|
||||
main()
|
||||
sr = None
|
||||
try:
|
||||
# init sickrage
|
||||
sr = SickRage()
|
||||
|
||||
logger.log("SickRage is restarting, please stand by ...")
|
||||
restart = True
|
||||
# start sickrage
|
||||
sr.start()
|
||||
|
||||
logger.log("Goodbye ...")
|
||||
# shutdown web server
|
||||
sr.webserver.shutDown()
|
||||
sr.webserver.join()
|
||||
sr.webserver = None
|
||||
|
||||
# if run as daemon delete the pidfile
|
||||
if sr.runAsDaemon:
|
||||
sr.daemon.delpid()
|
||||
|
||||
if not sickbeard.shutdown:
|
||||
install_type = sickbeard.versionCheckScheduler.action.install_type
|
||||
|
||||
popen_list = []
|
||||
|
||||
if install_type in ('git', 'source'):
|
||||
popen_list = [sys.executable, sickbeard.MY_FULLNAME]
|
||||
elif install_type == 'win':
|
||||
if hasattr(sys, 'frozen'):
|
||||
# c:\dir\to\updater.exe 12345 c:\dir\to\sickbeard.exe
|
||||
popen_list = [os.path.join(sickbeard.PROG_DIR, 'updater.exe'), str(sickbeard.PID), sys.executable]
|
||||
else:
|
||||
logger.log(u"Unknown SB launch method, please file a bug report about this", logger.ERROR)
|
||||
popen_list = [sys.executable, os.path.join(sickbeard.PROG_DIR, 'updater.py'), str(sickbeard.PID), sys.executable,
|
||||
sickbeard.MY_FULLNAME]
|
||||
|
||||
if popen_list:
|
||||
popen_list += sickbeard.MY_ARGS
|
||||
if '--nolaunch' not in popen_list:
|
||||
popen_list += ['--nolaunch']
|
||||
logger.log(u"Restarting SickRage with " + str(popen_list))
|
||||
logger.close()
|
||||
subprocess.Popen(popen_list, cwd=os.getcwd())
|
||||
|
||||
# exit process
|
||||
os._exit(0)
|
||||
except:
|
||||
if sr:
|
||||
logger.log(traceback.format_exc(), logger.ERROR)
|
||||
else:
|
||||
print(traceback.format_exc())
|
||||
sys.exit(1)
|
@ -1,26 +1,3 @@
|
||||
'''
|
||||
***
|
||||
Modified generic daemon class
|
||||
***
|
||||
|
||||
Author: http://www.jejik.com/articles/2007/02/
|
||||
a_simple_unix_linux_daemon_in_python/www.boxedice.com
|
||||
|
||||
License: http://creativecommons.org/licenses/by-sa/3.0/
|
||||
|
||||
Changes: 23rd Jan 2009 (David Mytton <david@boxedice.com>)
|
||||
- Replaced hard coded '/dev/null in __init__ with os.devnull
|
||||
- Added OS check to conditionally remove code that doesn't
|
||||
work on OS X
|
||||
- Added output to console on completion
|
||||
- Tidied up formatting
|
||||
11th Mar 2009 (David Mytton <david@boxedice.com>)
|
||||
- Fixed problem with daemon exiting on Python 2.4
|
||||
(before SystemExit was part of the Exception base)
|
||||
13th Aug 2010 (David Mytton <david@boxedice.com>
|
||||
- Fixed unhandled exception if PID file is empty
|
||||
'''
|
||||
|
||||
# Core modules
|
||||
import atexit
|
||||
import os
|
||||
@ -57,7 +34,7 @@ class Daemon(object):
|
||||
pid = os.fork()
|
||||
if pid > 0:
|
||||
# Exit first parent
|
||||
sys.exit(0)
|
||||
os._exit(0)
|
||||
except OSError, e:
|
||||
sys.stderr.write(
|
||||
"fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
|
||||
@ -73,7 +50,7 @@ class Daemon(object):
|
||||
pid = os.fork()
|
||||
if pid > 0:
|
||||
# Exit from second parent
|
||||
sys.exit(0)
|
||||
os._exit(0)
|
||||
except OSError, e:
|
||||
sys.stderr.write(
|
||||
"fork #2 failed: %d (%s)\n" % (e.errno, e.strerror))
|
||||
@ -93,11 +70,6 @@ class Daemon(object):
|
||||
os.dup2(so.fileno(), sys.stdout.fileno())
|
||||
os.dup2(se.fileno(), sys.stderr.fileno())
|
||||
|
||||
def sigtermhandler(signum, frame):
|
||||
self.daemon_alive = False
|
||||
signal.signal(signal.SIGTERM, sigtermhandler)
|
||||
signal.signal(signal.SIGINT, sigtermhandler)
|
||||
|
||||
if self.verbose >= 1:
|
||||
print "Started"
|
||||
|
||||
@ -110,12 +82,11 @@ class Daemon(object):
|
||||
def delpid(self):
|
||||
os.remove(self.pidfile)
|
||||
|
||||
def start(self, daemonize=True, *args, **kwargs):
|
||||
def start(self, *args, **kwargs):
|
||||
"""
|
||||
Start the daemon
|
||||
"""
|
||||
|
||||
if daemonize:
|
||||
if self.verbose >= 1:
|
||||
print "Starting..."
|
||||
|
||||
@ -136,17 +107,13 @@ class Daemon(object):
|
||||
|
||||
# Start the daemon
|
||||
self.daemonize()
|
||||
|
||||
self.run(*args, **kwargs)
|
||||
|
||||
def stop(self, daemonize=True):
|
||||
def stop(self):
|
||||
"""
|
||||
Stop the daemon
|
||||
"""
|
||||
|
||||
if not daemonize:
|
||||
return
|
||||
|
||||
if self.verbose >= 1:
|
||||
print "Stopping..."
|
||||
|
||||
@ -185,12 +152,12 @@ class Daemon(object):
|
||||
if self.verbose >= 1:
|
||||
print "Stopped"
|
||||
|
||||
def restart(self, daemonize=True):
|
||||
def restart(self):
|
||||
"""
|
||||
Restart the daemon
|
||||
"""
|
||||
self.stop(daemonize=daemonize)
|
||||
self.start(daemonize=daemonize)
|
||||
self.stop()
|
||||
self.start()
|
||||
|
||||
def get_pid(self):
|
||||
try:
|
||||
|
@ -174,10 +174,19 @@ class LockBase:
|
||||
else:
|
||||
self.tname = ""
|
||||
dirname = os.path.dirname(self.lock_file)
|
||||
|
||||
# unique name is mostly about the current process, but must
|
||||
# also contain the path -- otherwise, two adjacent locked
|
||||
# files conflict (one file gets locked, creating lock-file and
|
||||
# unique file, the other one gets locked, creating lock-file
|
||||
# and overwriting the already existing lock-file, then one
|
||||
# gets unlocked, deleting both lock-file and unique file,
|
||||
# finally the last lock errors out upon releasing.
|
||||
self.unique_name = os.path.join(dirname,
|
||||
"%s%s.%s" % (self.hostname,
|
||||
"%s%s.%s%s" % (self.hostname,
|
||||
self.tname,
|
||||
self.pid))
|
||||
self.pid,
|
||||
hash(self.path)))
|
||||
self.timeout = timeout
|
||||
|
||||
def acquire(self, timeout=None):
|
||||
|
@ -28,6 +28,7 @@ from urllib2 import getproxies
|
||||
from threading import Lock
|
||||
|
||||
# apparently py2exe won't build these unless they're imported somewhere
|
||||
import sys
|
||||
from sickbeard import providers, metadata, config, webserveInit
|
||||
from sickbeard.providers.generic import GenericProvider
|
||||
from providers import ezrss, tvtorrents, btn, newznab, womble, thepiratebay, torrentleech, kat, iptorrents, \
|
||||
@ -1269,7 +1270,7 @@ def halt():
|
||||
pass
|
||||
|
||||
__INITIALIZED__ = False
|
||||
|
||||
started = False
|
||||
|
||||
def remove_pid_file(PIDFILE):
|
||||
try:
|
||||
@ -1283,12 +1284,9 @@ def remove_pid_file(PIDFILE):
|
||||
|
||||
|
||||
def sig_handler(signum=None, frame=None):
|
||||
global shutdown
|
||||
|
||||
if type(signum) != type(None):
|
||||
logger.log(u"Signal %i caught, saving and exiting..." % int(signum))
|
||||
shutdown = True
|
||||
IOLoop.current().stop()
|
||||
saveAndShutdown()
|
||||
|
||||
def saveAll():
|
||||
global showList
|
||||
@ -1303,22 +1301,14 @@ def saveAll():
|
||||
save_config()
|
||||
|
||||
def saveAndShutdown(restart=False):
|
||||
global shutdown
|
||||
global shutdown, started
|
||||
|
||||
# flag restart/shutdown
|
||||
if not restart:
|
||||
shutdown = True
|
||||
|
||||
# stop tornado web server
|
||||
webserveInit.server.stop()
|
||||
|
||||
# stop all tasks
|
||||
halt()
|
||||
|
||||
# save all shows to db
|
||||
saveAll()
|
||||
|
||||
#stop tornado io loop
|
||||
IOLoop.current().stop()
|
||||
# proceed with shutdown
|
||||
started = False
|
||||
|
||||
def invoke_command(to_call, *args, **kwargs):
|
||||
|
||||
@ -1333,11 +1323,8 @@ def invoke_command(to_call, *args, **kwargs):
|
||||
def invoke_restart(soft=True):
|
||||
invoke_command(restart, soft=soft)
|
||||
|
||||
|
||||
def invoke_shutdown():
|
||||
global shutdown
|
||||
shutdown = True
|
||||
invoke_command(IOLoop.current().stop)
|
||||
invoke_command(saveAndShutdown, False)
|
||||
|
||||
def restart(soft=True):
|
||||
if soft:
|
||||
@ -1346,7 +1333,7 @@ def restart(soft=True):
|
||||
logger.log(u"Re-initializing all data")
|
||||
initialize()
|
||||
else:
|
||||
IOLoop.current().stop()
|
||||
saveAndShutdown(True)
|
||||
|
||||
|
||||
def save_config():
|
||||
|
@ -349,6 +349,8 @@ class BTNCache(tvcache.TVCache):
|
||||
if ci is not None:
|
||||
cl.append(ci)
|
||||
|
||||
time.sleep(.2)
|
||||
|
||||
if cl:
|
||||
myDB = self._getDB()
|
||||
myDB.mass_action(cl)
|
||||
|
@ -260,6 +260,8 @@ class HDBitsCache(tvcache.TVCache):
|
||||
if ci is not None:
|
||||
ql.append(ci)
|
||||
|
||||
time.sleep(.2)
|
||||
|
||||
if ql:
|
||||
myDB = self._getDB()
|
||||
myDB.mass_action(ql)
|
||||
|
@ -382,6 +382,8 @@ class HDTorrentsCache(tvcache.TVCache):
|
||||
if ci is not None:
|
||||
cl.append(ci)
|
||||
|
||||
time.sleep(.2)
|
||||
|
||||
if cl:
|
||||
myDB = self._getDB()
|
||||
myDB.mass_action(cl)
|
||||
|
@ -323,6 +323,8 @@ class IPTorrentsCache(tvcache.TVCache):
|
||||
if ci is not None:
|
||||
cl.append(ci)
|
||||
|
||||
time.sleep(.2)
|
||||
|
||||
if cl:
|
||||
myDB = self._getDB()
|
||||
myDB.mass_action(cl)
|
||||
|
@ -460,6 +460,8 @@ class KATCache(tvcache.TVCache):
|
||||
if ci is not None:
|
||||
cl.append(ci)
|
||||
|
||||
time.sleep(.2)
|
||||
|
||||
if cl:
|
||||
myDB = self._getDB()
|
||||
myDB.mass_action(cl)
|
||||
|
@ -347,6 +347,8 @@ class NewznabCache(tvcache.TVCache):
|
||||
if ci is not None:
|
||||
ql.append(ci)
|
||||
|
||||
time.sleep(.2)
|
||||
|
||||
if ql:
|
||||
myDB = self._getDB()
|
||||
myDB.mass_action(ql)
|
||||
|
@ -372,6 +372,8 @@ class NextGenCache(tvcache.TVCache):
|
||||
if ci is not None:
|
||||
cl.append(ci)
|
||||
|
||||
time.sleep(.2)
|
||||
|
||||
if cl:
|
||||
myDB = self._getDB()
|
||||
myDB.mass_action(cl)
|
||||
|
@ -345,6 +345,8 @@ class PublicHDCache(tvcache.TVCache):
|
||||
if ci is not None:
|
||||
ql.append(ci)
|
||||
|
||||
time.sleep(.2)
|
||||
|
||||
if ql:
|
||||
myDB = self._getDB()
|
||||
myDB.mass_action(ql)
|
||||
|
@ -367,6 +367,8 @@ class SCCCache(tvcache.TVCache):
|
||||
if ci is not None:
|
||||
cl.append(ci)
|
||||
|
||||
time.sleep(.2)
|
||||
|
||||
if cl:
|
||||
myDB = self._getDB()
|
||||
myDB.mass_action(cl)
|
||||
|
@ -307,6 +307,8 @@ class SpeedCDCache(tvcache.TVCache):
|
||||
if ci is not None:
|
||||
ql.append(ci)
|
||||
|
||||
time.sleep(.2)
|
||||
|
||||
if ql:
|
||||
myDB = self._getDB()
|
||||
myDB.mass_action(ql)
|
||||
|
@ -440,6 +440,8 @@ class ThePirateBayCache(tvcache.TVCache):
|
||||
if ci is not None:
|
||||
cl.append(ci)
|
||||
|
||||
time.sleep(.2)
|
||||
|
||||
if cl:
|
||||
myDB = self._getDB()
|
||||
myDB.mass_action(cl)
|
||||
|
@ -331,6 +331,8 @@ class TorrentDayCache(tvcache.TVCache):
|
||||
if ci is not None:
|
||||
cl.append(ci)
|
||||
|
||||
time.sleep(.2)
|
||||
|
||||
if cl:
|
||||
myDB = self._getDB()
|
||||
myDB.mass_action(cl)
|
||||
|
@ -326,6 +326,8 @@ class TorrentLeechCache(tvcache.TVCache):
|
||||
if ci is not None:
|
||||
cl.append(ci)
|
||||
|
||||
time.sleep(.2)
|
||||
|
||||
if cl:
|
||||
myDB = self._getDB()
|
||||
myDB.mass_action(cl)
|
||||
|
@ -15,6 +15,7 @@
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with SickRage. If not, see <http://www.gnu.org/licenses/>.
|
||||
import time
|
||||
|
||||
import sickbeard
|
||||
import generic
|
||||
@ -73,6 +74,8 @@ class WombleCache(tvcache.TVCache):
|
||||
if ci is not None:
|
||||
cl.append(ci)
|
||||
|
||||
time.sleep(.2)
|
||||
|
||||
if cl:
|
||||
myDB = self._getDB()
|
||||
myDB.mass_action(cl)
|
||||
|
@ -128,6 +128,8 @@ class TVCache():
|
||||
if ci is not None:
|
||||
cl.append(ci)
|
||||
|
||||
time.sleep(.2)
|
||||
|
||||
if cl:
|
||||
myDB = self._getDB()
|
||||
myDB.mass_action(cl)
|
||||
|
@ -1,5 +1,8 @@
|
||||
import os
|
||||
import traceback
|
||||
import socket
|
||||
import time
|
||||
import threading
|
||||
import sys
|
||||
import sickbeard
|
||||
import webserve
|
||||
import webapi
|
||||
@ -10,9 +13,6 @@ from tornado.web import Application, StaticFileHandler, RedirectHandler, HTTPErr
|
||||
from tornado.httpserver import HTTPServer
|
||||
from tornado.ioloop import IOLoop
|
||||
|
||||
server = None
|
||||
|
||||
|
||||
class MultiStaticFileHandler(StaticFileHandler):
|
||||
def initialize(self, paths, default_filename=None):
|
||||
self.paths = paths
|
||||
@ -34,37 +34,44 @@ class MultiStaticFileHandler(StaticFileHandler):
|
||||
# Oops file not found anywhere!
|
||||
raise HTTPError(404)
|
||||
|
||||
class SRWebServer(threading.Thread):
|
||||
def __init__(self, options=[], io_loop=None):
|
||||
threading.Thread.__init__(self)
|
||||
self.daemon = True
|
||||
self.alive = True
|
||||
self.name = "TORNADO"
|
||||
self.io_loop = io_loop or IOLoop.current()
|
||||
|
||||
def initWebServer(options={}):
|
||||
options.setdefault('port', 8081)
|
||||
options.setdefault('host', '0.0.0.0')
|
||||
options.setdefault('log_dir', None)
|
||||
options.setdefault('username', '')
|
||||
options.setdefault('password', '')
|
||||
options.setdefault('web_root', '/')
|
||||
assert isinstance(options['port'], int)
|
||||
assert 'data_root' in options
|
||||
self.options = options
|
||||
self.options.setdefault('port', 8081)
|
||||
self.options.setdefault('host', '0.0.0.0')
|
||||
self.options.setdefault('log_dir', None)
|
||||
self.options.setdefault('username', '')
|
||||
self.options.setdefault('password', '')
|
||||
self.options.setdefault('web_root', '/')
|
||||
assert isinstance(self.options['port'], int)
|
||||
assert 'data_root' in self.options
|
||||
|
||||
# tornado setup
|
||||
enable_https = options['enable_https']
|
||||
https_cert = options['https_cert']
|
||||
https_key = options['https_key']
|
||||
self.enable_https = self.options['enable_https']
|
||||
self.https_cert = self.options['https_cert']
|
||||
self.https_key = self.options['https_key']
|
||||
|
||||
if enable_https:
|
||||
if self.enable_https:
|
||||
# If either the HTTPS certificate or key do not exist, make some self-signed ones.
|
||||
if not (https_cert and os.path.exists(https_cert)) or not (https_key and os.path.exists(https_key)):
|
||||
if not create_https_certificates(https_cert, https_key):
|
||||
if not (self.https_cert and os.path.exists(self.https_cert)) or not (self.https_key and os.path.exists(self.https_key)):
|
||||
if not create_https_certificates(self.https_cert, self.https_key):
|
||||
logger.log(u"Unable to create CERT/KEY files, disabling HTTPS")
|
||||
sickbeard.ENABLE_HTTPS = False
|
||||
enable_https = False
|
||||
|
||||
if not (os.path.exists(https_cert) and os.path.exists(https_key)):
|
||||
if not (os.path.exists(self.https_cert) and os.path.exists(self.https_key)):
|
||||
logger.log(u"Disabled HTTPS because of missing CERT and KEY files", logger.WARNING)
|
||||
sickbeard.ENABLE_HTTPS = False
|
||||
enable_https = False
|
||||
|
||||
# Load the app
|
||||
app = Application([],
|
||||
self.app = Application([],
|
||||
debug=False,
|
||||
gzip=True,
|
||||
xheaders=sickbeard.HANDLE_REVERSE_PROXY,
|
||||
@ -72,47 +79,61 @@ def initWebServer(options={}):
|
||||
)
|
||||
|
||||
# Main Handler
|
||||
app.add_handlers(".*$", [
|
||||
(r"%s" % options['web_root'], RedirectHandler, {'url': '%s/home/' % options['web_root']}),
|
||||
(r'%s/api/(.*)(/?)' % options['web_root'], webapi.Api),
|
||||
(r'%s/(.*)(/?)' % options['web_root'], webserve.MainHandler)
|
||||
self.app.add_handlers(".*$", [
|
||||
(r"%s" % self.options['web_root'], RedirectHandler, {'url': '%s/home/' % self.options['web_root']}),
|
||||
(r'%s/api/(.*)(/?)' % self.options['web_root'], webapi.Api),
|
||||
(r'%s/(.*)(/?)' % self.options['web_root'], webserve.MainHandler)
|
||||
])
|
||||
|
||||
# Static Path Handler
|
||||
app.add_handlers(".*$", [
|
||||
(r'%s/(favicon\.ico)' % options['web_root'], MultiStaticFileHandler,
|
||||
{'paths': [os.path.join(options['data_root'], 'images/ico/favicon.ico')]}),
|
||||
(r'%s/%s/(.*)(/?)' % (options['web_root'], 'images'), MultiStaticFileHandler,
|
||||
{'paths': [os.path.join(options['data_root'], 'images'),
|
||||
self.app.add_handlers(".*$", [
|
||||
(r'%s/(favicon\.ico)' % self.options['web_root'], MultiStaticFileHandler,
|
||||
{'paths': [os.path.join(self.options['data_root'], 'images/ico/favicon.ico')]}),
|
||||
(r'%s/%s/(.*)(/?)' % (self.options['web_root'], 'images'), MultiStaticFileHandler,
|
||||
{'paths': [os.path.join(self.options['data_root'], 'images'),
|
||||
os.path.join(sickbeard.CACHE_DIR, 'images')]}),
|
||||
(r'%s/%s/(.*)(/?)' % (options['web_root'], 'css'), MultiStaticFileHandler,
|
||||
{'paths': [os.path.join(options['data_root'], 'css')]}),
|
||||
(r'%s/%s/(.*)(/?)' % (options['web_root'], 'js'), MultiStaticFileHandler,
|
||||
{'paths': [os.path.join(options['data_root'], 'js')]})
|
||||
(r'%s/%s/(.*)(/?)' % (self.options['web_root'], 'css'), MultiStaticFileHandler,
|
||||
{'paths': [os.path.join(self.options['data_root'], 'css')]}),
|
||||
(r'%s/%s/(.*)(/?)' % (self.options['web_root'], 'js'), MultiStaticFileHandler,
|
||||
{'paths': [os.path.join(self.options['data_root'], 'js')]})
|
||||
|
||||
])
|
||||
|
||||
global server
|
||||
|
||||
if enable_https:
|
||||
def run(self):
|
||||
if self.enable_https:
|
||||
protocol = "https"
|
||||
server = HTTPServer(app, no_keep_alive=True,
|
||||
ssl_options={"certfile": https_cert, "keyfile": https_key})
|
||||
self.server = HTTPServer(self.app, no_keep_alive=True,
|
||||
ssl_options={"certfile": self.https_cert, "keyfile": self.https_key})
|
||||
else:
|
||||
protocol = "http"
|
||||
server = HTTPServer(app, no_keep_alive=True)
|
||||
self.server = HTTPServer(self.app, no_keep_alive=True)
|
||||
|
||||
logger.log(u"Starting SickRage on " + protocol + "://" + str(options['host']) + ":" + str(
|
||||
options['port']) + "/")
|
||||
logger.log(u"Starting SickRage on " + protocol + "://" + str(self.options['host']) + ":" + str(
|
||||
self.options['port']) + "/")
|
||||
|
||||
server.listen(options['port'], options['host'])
|
||||
|
||||
def shutdown():
|
||||
|
||||
logger.log('Shutting down tornado IO loop')
|
||||
try:
|
||||
IOLoop.current().stop()
|
||||
except RuntimeError:
|
||||
pass
|
||||
self.server.listen(self.options['port'], self.options['host'])
|
||||
except:
|
||||
logger.log('Failed shutting down tornado IO loop: %s' % traceback.format_exc(), logger.ERROR)
|
||||
etype, evalue, etb = sys.exc_info()
|
||||
logger.log("Could not start webserver on %s. Excpeption: %s, Error: %s" % (self.options['port'], etype, evalue), logger.ERROR)
|
||||
return
|
||||
|
||||
try:
|
||||
self.io_loop.start()
|
||||
self.io_loop.close(True)
|
||||
|
||||
# stop all tasks
|
||||
sickbeard.halt()
|
||||
|
||||
# save all shows to DB
|
||||
sickbeard.saveAll()
|
||||
|
||||
except ValueError:
|
||||
# Ignore errors like "ValueError: I/O operation on closed kqueue fd". These might be thrown during a reload.
|
||||
pass
|
||||
|
||||
def shutDown(self):
|
||||
self.alive = False
|
||||
if self.server:
|
||||
self.server.stop()
|
||||
self.io_loop.stop()
|
Loading…
Reference in New Issue
Block a user