mirror of
https://github.com/SickGear/SickGear.git
synced 2025-01-05 17:43:37 +00:00
Merge remote-tracking branch 'origin/dev'
This commit is contained in:
commit
ae398031fd
27 changed files with 923 additions and 501 deletions
633
SickBeard.py
633
SickBeard.py
|
@ -19,10 +19,11 @@
|
|||
|
||||
# Check needed software dependencies to nudge users to fix their setup
|
||||
from __future__ import with_statement
|
||||
import functools
|
||||
|
||||
import time
|
||||
import sys
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
if sys.version_info < (2, 6):
|
||||
print "Sorry, requires Python 2.6 or 2.7."
|
||||
|
@ -60,368 +61,356 @@ 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)
|
||||
signal.signal(signal.SIGTERM, sickbeard.sig_handler)
|
||||
|
||||
throwaway = datetime.datetime.strptime('20110101', '%Y%m%d')
|
||||
|
||||
def loadShowsFromDB():
|
||||
"""
|
||||
Populates the showList with shows from the database
|
||||
"""
|
||||
class SickRage(object):
|
||||
|
||||
logger.log(u"Loading initial show list")
|
||||
def loadShowsFromDB(self):
|
||||
"""
|
||||
Populates the showList with shows from the database
|
||||
"""
|
||||
|
||||
myDB = db.DBConnection()
|
||||
sqlResults = myDB.select("SELECT * FROM tv_shows")
|
||||
logger.log(u"Loading initial show list")
|
||||
|
||||
sickbeard.showList = []
|
||||
for sqlShow in sqlResults:
|
||||
try:
|
||||
curShow = TVShow(int(sqlShow["indexer"]), int(sqlShow["indexer_id"]))
|
||||
sickbeard.showList.append(curShow)
|
||||
except Exception, e:
|
||||
logger.log(
|
||||
myDB = db.DBConnection()
|
||||
sqlResults = myDB.select("SELECT * FROM tv_shows")
|
||||
|
||||
sickbeard.showList = []
|
||||
for sqlShow in sqlResults:
|
||||
try:
|
||||
curShow = TVShow(int(sqlShow["indexer"]), int(sqlShow["indexer_id"]))
|
||||
sickbeard.showList.append(curShow)
|
||||
except Exception, e:
|
||||
logger.log(
|
||||
u"There was an error creating the show in " + sqlShow["location"] + ": " + str(e).decode('utf-8'),
|
||||
logger.ERROR)
|
||||
logger.log(traceback.format_exc(), logger.DEBUG)
|
||||
logger.ERROR)
|
||||
logger.log(traceback.format_exc(), logger.DEBUG)
|
||||
|
||||
# TODO: update the existing shows if the showlist has something in it
|
||||
|
||||
def daemonize():
|
||||
try:
|
||||
pid = os.fork()
|
||||
if pid > 0:
|
||||
sys.exit(0)
|
||||
except OSError:
|
||||
print "fork() failed"
|
||||
sys.exit(1)
|
||||
|
||||
os.chdir(sickbeard.PROG_DIR)
|
||||
os.setsid()
|
||||
# Make sure I can read my own files and shut out others
|
||||
prev= os.umask(0)
|
||||
os.umask(prev and int('077',8))
|
||||
|
||||
try:
|
||||
pid = os.fork()
|
||||
if pid > 0:
|
||||
sys.exit(0)
|
||||
except OSError:
|
||||
print "fork() failed"
|
||||
sys.exit(1)
|
||||
|
||||
# Write pid
|
||||
if sickbeard.CREATEPID:
|
||||
pid = str(os.getpid())
|
||||
logger.log(u"Writing PID: " + pid + " to " + str(sickbeard.PIDFILE))
|
||||
def restore(self, srcDir, dstDir):
|
||||
try:
|
||||
file(sickbeard.PIDFILE, 'w').write("%s\n" % pid)
|
||||
except IOError, e:
|
||||
logger.log_error_and_exit(
|
||||
u"Unable to write PID file: " + sickbeard.PIDFILE + " Error: " + str(e.strerror) + " [" + str(
|
||||
e.errno) + "]")
|
||||
for file in os.listdir(srcDir):
|
||||
srcFile = os.path.join(srcDir, file)
|
||||
dstFile = os.path.join(dstDir, file)
|
||||
bakFile = os.path.join(dstDir, file + '.bak')
|
||||
shutil.move(dstFile, bakFile)
|
||||
shutil.move(srcFile, dstFile)
|
||||
|
||||
dev_null = file('/dev/null', 'r')
|
||||
os.dup2(dev_null.fileno(), sys.stdin.fileno())
|
||||
os.rmdir(srcDir)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def restore(srcDir, dstDir):
|
||||
try:
|
||||
for file in os.listdir(srcDir):
|
||||
srcFile = os.path.join(srcDir, file)
|
||||
dstFile = os.path.join(dstDir, file)
|
||||
bakFile = os.path.join(dstDir, file + '.bak')
|
||||
shutil.move(dstFile, bakFile)
|
||||
shutil.move(srcFile, dstFile)
|
||||
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
|
||||
|
||||
os.rmdir(srcDir)
|
||||
return True
|
||||
except:
|
||||
return 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.SYS_ENCODING = None
|
||||
|
||||
def main():
|
||||
"""
|
||||
TV for me
|
||||
"""
|
||||
|
||||
# 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:
|
||||
locale.setlocale(locale.LC_ALL, "")
|
||||
sickbeard.SYS_ENCODING = locale.getpreferredencoding()
|
||||
except (locale.Error, IOError):
|
||||
pass
|
||||
|
||||
# For OSes that are poorly configured I'll just randomly force UTF-8
|
||||
if not sickbeard.SYS_ENCODING or sickbeard.SYS_ENCODING in ('ANSI_X3.4-1968', 'US-ASCII', 'ASCII'):
|
||||
sickbeard.SYS_ENCODING = 'UTF-8'
|
||||
|
||||
if not hasattr(sys, "setdefaultencoding"):
|
||||
reload(sys)
|
||||
|
||||
try:
|
||||
# pylint: disable=E1101
|
||||
# On non-unicode builds this will raise an AttributeError, if encoding type is not valid it throws a LookupError
|
||||
sys.setdefaultencoding(sickbeard.SYS_ENCODING)
|
||||
except:
|
||||
print 'Sorry, you MUST add the SickRage folder to the PYTHONPATH environment variable'
|
||||
print 'or find another way to force Python to use ' + sickbeard.SYS_ENCODING + ' for string encoding.'
|
||||
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)
|
||||
|
||||
# Rename the main thread
|
||||
threading.currentThread().name = "MAIN"
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], "qfdp::",
|
||||
['quiet', 'forceupdate', 'daemon', 'port=', 'pidfile=', 'nolaunch', 'config=',
|
||||
'datadir=']) # @UnusedVariable
|
||||
except getopt.GetoptError:
|
||||
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
|
||||
|
||||
# Should we update (from indexer) all shows in the DB right away?
|
||||
if o in ('-f', '--forceupdate'):
|
||||
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
|
||||
|
||||
# Override default/configured port
|
||||
if o in ('-p', '--port'):
|
||||
forcedPort = int(a)
|
||||
|
||||
# Run as a double forked daemon
|
||||
if o in ('-d', '--daemon'):
|
||||
sickbeard.DAEMON = True
|
||||
# When running as daemon disable consoleLogging and don't start browser
|
||||
consoleLogging = False
|
||||
noLaunch = True
|
||||
|
||||
if sys.platform == 'win32':
|
||||
sickbeard.DAEMON = False
|
||||
|
||||
# Specify folder to load the config file from
|
||||
if o in ('--config',):
|
||||
sickbeard.CONFIG_FILE = os.path.abspath(a)
|
||||
|
||||
# Specify folder to use as the data dir
|
||||
if o in ('--datadir',):
|
||||
sickbeard.DATA_DIR = os.path.abspath(a)
|
||||
|
||||
# Prevent resizing of the banner/posters even if PIL is installed
|
||||
if o in ('--noresize',):
|
||||
sickbeard.NO_RESIZE = True
|
||||
|
||||
# Write a pidfile if requested
|
||||
if o in ('--pidfile',):
|
||||
sickbeard.CREATEPID = True
|
||||
sickbeard.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.")
|
||||
|
||||
# The pidfile is only useful in daemon mode, make sure we can write the file properly
|
||||
if sickbeard.CREATEPID and not sickbeard.restarted:
|
||||
if sickbeard.DAEMON:
|
||||
pid_dir = os.path.dirname(sickbeard.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:
|
||||
sys.stdout.write("Not running in daemon mode. PID file creation disabled.\n")
|
||||
|
||||
sickbeard.CREATEPID = False
|
||||
|
||||
# If they don't specify a config file then put it in the data dir
|
||||
if not sickbeard.CONFIG_FILE:
|
||||
sickbeard.CONFIG_FILE = os.path.join(sickbeard.DATA_DIR, "config.ini")
|
||||
|
||||
# Make sure that we can create the data dir
|
||||
if not os.access(sickbeard.DATA_DIR, os.F_OK):
|
||||
try:
|
||||
os.makedirs(sickbeard.DATA_DIR, 0744)
|
||||
except os.error, e:
|
||||
raise SystemExit("Unable to create datadir '" + sickbeard.DATA_DIR + "'")
|
||||
locale.setlocale(locale.LC_ALL, "")
|
||||
sickbeard.SYS_ENCODING = locale.getpreferredencoding()
|
||||
except (locale.Error, IOError):
|
||||
pass
|
||||
|
||||
# Make sure we can write to the data dir
|
||||
if not os.access(sickbeard.DATA_DIR, os.W_OK):
|
||||
raise SystemExit("Datadir must be writeable '" + sickbeard.DATA_DIR + "'")
|
||||
# For OSes that are poorly configured I'll just randomly force UTF-8
|
||||
if not sickbeard.SYS_ENCODING or sickbeard.SYS_ENCODING in ('ANSI_X3.4-1968', 'US-ASCII', 'ASCII'):
|
||||
sickbeard.SYS_ENCODING = 'UTF-8'
|
||||
|
||||
# Make sure we can write to the config file
|
||||
if not os.access(sickbeard.CONFIG_FILE, os.W_OK):
|
||||
if os.path.isfile(sickbeard.CONFIG_FILE):
|
||||
raise SystemExit("Config file '" + sickbeard.CONFIG_FILE + "' must be writeable.")
|
||||
elif not os.access(os.path.dirname(sickbeard.CONFIG_FILE), os.W_OK):
|
||||
raise SystemExit("Config file root dir '" + os.path.dirname(sickbeard.CONFIG_FILE) + "' must be writeable.")
|
||||
if not hasattr(sys, "setdefaultencoding"):
|
||||
reload(sys)
|
||||
|
||||
# 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):
|
||||
logger.log(u"Restore successful...")
|
||||
try:
|
||||
# pylint: disable=E1101
|
||||
# On non-unicode builds this will raise an AttributeError, if encoding type is not valid it throws a LookupError
|
||||
sys.setdefaultencoding(sickbeard.SYS_ENCODING)
|
||||
except:
|
||||
print 'Sorry, you MUST add the SickRage folder to the PYTHONPATH environment variable'
|
||||
print 'or find another way to force Python to use ' + sickbeard.SYS_ENCODING + ' for string encoding.'
|
||||
sys.exit(1)
|
||||
|
||||
# Need console logging for SickBeard.py and SickBeard-console.exe
|
||||
self.consoleLogging = (not hasattr(sys, "frozen")) or (sickbeard.MY_NAME.lower().find('-console') > 0)
|
||||
|
||||
# Rename the main thread
|
||||
threading.currentThread().name = "MAIN"
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], "qfdp::",
|
||||
['quiet', 'forceupdate', 'daemon', 'port=', 'pidfile=', 'nolaunch', 'config=',
|
||||
'datadir=']) # @UnusedVariable
|
||||
except getopt.GetoptError:
|
||||
print "Available Options: --quiet, --forceupdate, --port, --daemon, --pidfile, --config, --datadir"
|
||||
sys.exit()
|
||||
|
||||
for o, a in opts:
|
||||
# For now we'll just silence the logging
|
||||
if o in ('-q', '--quiet'):
|
||||
self.consoleLogging = False
|
||||
|
||||
# Should we update (from indexer) all shows in the DB right away?
|
||||
if o in ('-f', '--forceupdate'):
|
||||
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',):
|
||||
self.noLaunch = True
|
||||
|
||||
# Override default/configured port
|
||||
if o in ('-p', '--port'):
|
||||
self.forcedPort = int(a)
|
||||
|
||||
# Run as a double forked daemon
|
||||
if o in ('-d', '--daemon'):
|
||||
self.runAsDaemon = True
|
||||
# When running as daemon disable consoleLogging and don't start browser
|
||||
self.consoleLogging = False
|
||||
self.noLaunch = True
|
||||
|
||||
if sys.platform == 'win32':
|
||||
self.runAsDaemon = False
|
||||
|
||||
# Specify folder to load the config file from
|
||||
if o in ('--config',):
|
||||
sickbeard.CONFIG_FILE = os.path.abspath(a)
|
||||
|
||||
# Specify folder to use as the data dir
|
||||
if o in ('--datadir',):
|
||||
sickbeard.DATA_DIR = os.path.abspath(a)
|
||||
|
||||
# Prevent resizing of the banner/posters even if PIL is installed
|
||||
if o in ('--noresize',):
|
||||
sickbeard.NO_RESIZE = True
|
||||
|
||||
# Write a pidfile if requested
|
||||
if o in ('--pidfile',):
|
||||
self.CREATEPID = True
|
||||
self.PIDFILE = str(a)
|
||||
|
||||
# If the pidfile already exists, sickbeard may still be running, so exit
|
||||
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 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 self.consoleLogging:
|
||||
sys.stdout.write("Not running in daemon mode. PID file creation disabled.\n")
|
||||
|
||||
self.CREATEPID = False
|
||||
|
||||
# If they don't specify a config file then put it in the data dir
|
||||
if not sickbeard.CONFIG_FILE:
|
||||
sickbeard.CONFIG_FILE = os.path.join(sickbeard.DATA_DIR, "config.ini")
|
||||
|
||||
# Make sure that we can create the data dir
|
||||
if not os.access(sickbeard.DATA_DIR, os.F_OK):
|
||||
try:
|
||||
os.makedirs(sickbeard.DATA_DIR, 0744)
|
||||
except os.error, e:
|
||||
raise SystemExit("Unable to create datadir '" + sickbeard.DATA_DIR + "'")
|
||||
|
||||
# Make sure we can write to the data dir
|
||||
if not os.access(sickbeard.DATA_DIR, os.W_OK):
|
||||
raise SystemExit("Datadir must be writeable '" + sickbeard.DATA_DIR + "'")
|
||||
|
||||
# Make sure we can write to the config file
|
||||
if not os.access(sickbeard.CONFIG_FILE, os.W_OK):
|
||||
if os.path.isfile(sickbeard.CONFIG_FILE):
|
||||
raise SystemExit("Config file '" + sickbeard.CONFIG_FILE + "' must be writeable.")
|
||||
elif not os.access(os.path.dirname(sickbeard.CONFIG_FILE), os.W_OK):
|
||||
raise SystemExit(
|
||||
"Config file root dir '" + os.path.dirname(sickbeard.CONFIG_FILE) + "' must be writeable.")
|
||||
|
||||
# Check if we need to perform a restore first
|
||||
restoreDir = os.path.join(sickbeard.DATA_DIR, 'restore')
|
||||
if os.path.exists(restoreDir):
|
||||
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)
|
||||
|
||||
# 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)
|
||||
|
||||
sickbeard.CFG = ConfigObj(sickbeard.CONFIG_FILE)
|
||||
|
||||
CUR_DB_VERSION = db.DBConnection().checkDBVersion()
|
||||
|
||||
if CUR_DB_VERSION > 0:
|
||||
if CUR_DB_VERSION < MIN_DB_VERSION:
|
||||
raise SystemExit("Your database version (" + str(
|
||||
CUR_DB_VERSION) + ") is too old to migrate from with this version of SickRage (" + str(
|
||||
MIN_DB_VERSION) + ").\n" + \
|
||||
"Upgrade using a previous version of SB first, or start with no database file to begin fresh.")
|
||||
if CUR_DB_VERSION > MAX_DB_VERSION:
|
||||
raise SystemExit("Your database version (" + str(
|
||||
CUR_DB_VERSION) + ") has been incremented past what this version of SickRage supports (" + str(
|
||||
MAX_DB_VERSION) + ").\n" + \
|
||||
"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=self.consoleLogging)
|
||||
|
||||
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:
|
||||
logger.log(u"Restore FAILED!", logger.ERROR)
|
||||
self.startPort = sickbeard.WEB_PORT
|
||||
|
||||
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)
|
||||
|
||||
sickbeard.CFG = ConfigObj(sickbeard.CONFIG_FILE)
|
||||
|
||||
CUR_DB_VERSION = db.DBConnection().checkDBVersion()
|
||||
|
||||
if CUR_DB_VERSION > 0:
|
||||
if CUR_DB_VERSION < MIN_DB_VERSION:
|
||||
raise SystemExit("Your database version (" + str(
|
||||
CUR_DB_VERSION) + ") is too old to migrate from with this version of SickRage (" + str(
|
||||
MIN_DB_VERSION) + ").\n" + \
|
||||
"Upgrade using a previous version of SB first, or start with no database file to begin fresh.")
|
||||
if CUR_DB_VERSION > MAX_DB_VERSION:
|
||||
raise SystemExit("Your database version (" + str(
|
||||
CUR_DB_VERSION) + ") has been incremented past what this version of SickRage supports (" + str(
|
||||
MAX_DB_VERSION) + ").\n" + \
|
||||
"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)
|
||||
|
||||
if sickbeard.DAEMON:
|
||||
daemonize()
|
||||
|
||||
# Use this PID for everything
|
||||
sickbeard.PID = os.getpid()
|
||||
|
||||
if forcedPort:
|
||||
logger.log(u"Forcing web server to port " + str(forcedPort))
|
||||
startPort = forcedPort
|
||||
else:
|
||||
startPort = sickbeard.WEB_PORT
|
||||
|
||||
if sickbeard.WEB_LOG:
|
||||
log_dir = sickbeard.LOG_DIR
|
||||
else:
|
||||
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
|
||||
else:
|
||||
if sickbeard.WEB_IPV6:
|
||||
webhost = '::'
|
||||
if sickbeard.WEB_LOG:
|
||||
self.log_dir = sickbeard.LOG_DIR
|
||||
else:
|
||||
webhost = '0.0.0.0'
|
||||
self.log_dir = None
|
||||
|
||||
options = {
|
||||
'port': int(startPort),
|
||||
'host': webhost,
|
||||
'data_root': os.path.join(sickbeard.PROG_DIR, 'gui', sickbeard.GUI_NAME),
|
||||
'web_root': sickbeard.WEB_ROOT,
|
||||
'log_dir': log_dir,
|
||||
'username': sickbeard.WEB_USERNAME,
|
||||
'password': sickbeard.WEB_PASSWORD,
|
||||
'enable_https': sickbeard.ENABLE_HTTPS,
|
||||
'handle_reverse_proxy': sickbeard.HANDLE_REVERSE_PROXY,
|
||||
'https_cert': sickbeard.HTTPS_CERT,
|
||||
'https_key': sickbeard.HTTPS_KEY,
|
||||
# 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':
|
||||
self.webhost = sickbeard.WEB_HOST
|
||||
else:
|
||||
if sickbeard.WEB_IPV6:
|
||||
self.webhost = '::'
|
||||
else:
|
||||
self.webhost = '0.0.0.0'
|
||||
|
||||
# web server options
|
||||
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': self.log_dir,
|
||||
'username': sickbeard.WEB_USERNAME,
|
||||
'password': sickbeard.WEB_PASSWORD,
|
||||
'enable_https': sickbeard.ENABLE_HTTPS,
|
||||
'handle_reverse_proxy': sickbeard.HANDLE_REVERSE_PROXY,
|
||||
'https_cert': sickbeard.HTTPS_CERT,
|
||||
'https_key': sickbeard.HTTPS_KEY,
|
||||
}
|
||||
|
||||
# init tornado
|
||||
try:
|
||||
webserveInit.initWebServer(options)
|
||||
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"Launching browser and exiting", logger.ERROR)
|
||||
sickbeard.launchBrowser(startPort)
|
||||
sys.exit()
|
||||
# start web server
|
||||
try:
|
||||
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?" % self.startPort,
|
||||
logger.ERROR)
|
||||
if sickbeard.LAUNCH_BROWSER and not self.runAsDaemon:
|
||||
logger.log(u"Launching browser and exiting", logger.ERROR)
|
||||
sickbeard.launchBrowser(self.startPort)
|
||||
os._exit(1)
|
||||
|
||||
# Build from the DB to start with
|
||||
loadShowsFromDB()
|
||||
if self.consoleLogging:
|
||||
print "Starting up SickRage " + SICKBEARD_VERSION + " from " + sickbeard.CONFIG_FILE
|
||||
|
||||
# Fire up all our threads
|
||||
sickbeard.start()
|
||||
# Build from the DB to start with
|
||||
self.loadShowsFromDB()
|
||||
|
||||
# Launch browser if we're supposed to
|
||||
if sickbeard.LAUNCH_BROWSER and not noLaunch:
|
||||
sickbeard.launchBrowser(startPort)
|
||||
# Fire up all our threads
|
||||
sickbeard.start()
|
||||
|
||||
# Start an update if we're supposed to
|
||||
if forceUpdate or sickbeard.UPDATE_SHOWS_ON_START:
|
||||
sickbeard.showUpdateScheduler.action.run(force=True) # @UndefinedVariable
|
||||
# Start an update if we're supposed to
|
||||
if self.forceUpdate or sickbeard.UPDATE_SHOWS_ON_START:
|
||||
sickbeard.showUpdateScheduler.action.run(force=True) # @UndefinedVariable
|
||||
|
||||
# If we restarted then unset the restarted flag
|
||||
if sickbeard.restarted:
|
||||
sickbeard.restarted = False
|
||||
if sickbeard.LAUNCH_BROWSER and not (self.noLaunch or self.runAsDaemon):
|
||||
sickbeard.launchBrowser(self.startPort)
|
||||
|
||||
# IOLoop
|
||||
io_loop = IOLoop.current()
|
||||
|
||||
# Open browser window
|
||||
if sickbeard.LAUNCH_BROWSER and not (noLaunch or sickbeard.DAEMON or sickbeard.restarted):
|
||||
io_loop.add_timeout(datetime.timedelta(seconds=5), functools.partial(sickbeard.launchBrowser, startPort))
|
||||
|
||||
# Start web server
|
||||
io_loop.start()
|
||||
|
||||
# Save and restart/shutdown
|
||||
sickbeard.saveAndShutdown()
|
||||
while(sickbeard.started):
|
||||
time.sleep(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
if sys.hexversion >= 0x020600F0:
|
||||
freeze_support()
|
||||
|
||||
while(True):
|
||||
main()
|
||||
sr = None
|
||||
try:
|
||||
# init sickrage
|
||||
sr = SickRage()
|
||||
|
||||
# check if restart was requested
|
||||
if not sickbeard.restarted:
|
||||
if sickbeard.CREATEPID:
|
||||
logger.log(u"Removing pidfile " + str(sickbeard.PIDFILE))
|
||||
sickbeard.remove_pid_file(sickbeard.PIDFILE)
|
||||
break
|
||||
# start sickrage
|
||||
sr.start()
|
||||
|
||||
# restart
|
||||
logger.log("Restarting SickRage, please stand by...")
|
||||
# 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)
|
92
gui/slick/css/trakt.css
Normal file
92
gui/slick/css/trakt.css
Normal file
|
@ -0,0 +1,92 @@
|
|||
.traktShowDiv {
|
||||
|
||||
clear: both;
|
||||
border-left: 1px solid #CCCCCC;
|
||||
border-right: 1px solid #CCCCCC;
|
||||
border-bottom: 1px solid #CCCCCC;
|
||||
margin: auto;
|
||||
padding: 0px;
|
||||
text-align: left;
|
||||
width: 750px;
|
||||
}
|
||||
|
||||
.traktShowDiv a, .traktShowDiv a:link, .traktShowDiv a:visited, .traktShowDiv a:hover {
|
||||
text-decoration: none;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.traktShowTitle a {
|
||||
color: #000000;
|
||||
float: left;
|
||||
padding-top: 3px;
|
||||
line-height: 1.2em;
|
||||
font-size: 1.1em;
|
||||
text-shadow: -1px -1px 0 #FFF);
|
||||
}
|
||||
|
||||
.traktShowTitleIcons {
|
||||
float: right;
|
||||
padding: 3px 5px;
|
||||
}
|
||||
|
||||
.traktShowDiv .title {
|
||||
font-weight: 900;
|
||||
color: #333;
|
||||
}
|
||||
.imgWrapper {
|
||||
background: url("../images/loading.gif") no-repeat scroll center center #FFFFFF;
|
||||
border: 3px solid #FFFFFF;
|
||||
box-shadow: 1px 1px 2px 0 #555555;
|
||||
float: left;
|
||||
height: 50px;
|
||||
overflow: hidden;
|
||||
text-indent: -3000px;
|
||||
width: 50px;
|
||||
}
|
||||
.imgWrapper .traktPosterThumb {
|
||||
float: left;
|
||||
min-height: 100%;
|
||||
min-width: 100%;
|
||||
width: 50px;
|
||||
height: auto;
|
||||
position: relative;
|
||||
border: none;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.traktPosterThumb {
|
||||
-ms-interpolation-mode: bicubic; /* make scaling look nicer for ie */
|
||||
vertical-align: top;
|
||||
height: auto;
|
||||
width: 160px;
|
||||
border-top: 1px solid #ccc;
|
||||
border-right: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.traktShowDiv th {
|
||||
color: #000;
|
||||
letter-spacing: 1px;
|
||||
text-align: left;
|
||||
background-color: #333333;
|
||||
}
|
||||
|
||||
.traktShowDiv th.nobg {
|
||||
background: #efefef;
|
||||
border-top: 1px solid #666;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.traktShowDiv td {
|
||||
border-top: 1px solid #d2ebe8;
|
||||
background: #fff;
|
||||
padding: 5px 10px 5px 10px;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.traktShowDiv td.trakts_show {
|
||||
width: 100%;
|
||||
height: 90%;
|
||||
border-top: 1px solid #ccc;
|
||||
vertical-align: top;
|
||||
background: #F5FAFA;
|
||||
color: #000;
|
||||
}
|
|
@ -26,15 +26,23 @@
|
|||
<p>For shows that you haven't downloaded yet, this option finds a show on theTVDB.com and TVRage.com, creates a directory for its episodes, and adds it to SickRage.</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<br/><br/>
|
||||
|
||||
<a href="$sbRoot/home/addShows/trendingShows/" id="btnNewShow" class="btn btn-large">
|
||||
<div class="button"><img src="$sbRoot/images/add-new32.png" height="32" width="32" alt="Add Trending Shows"/></div>
|
||||
<div class="buttontext">
|
||||
<h2>Add Trending Show</h2>
|
||||
<p>For shows that you haven't downloaded yet, this option lets you choose from a list of current trending shows with ratings to add, creates a directory for its episodes, and adds it to SickRage.</p>
|
||||
</div>
|
||||
</a>
|
||||
<br/><br/>
|
||||
|
||||
#if $sickbeard.TRAKT_USE_RECOMMENDED:
|
||||
<a href="$sbRoot/home/addShows/recommendedShows/" id="btnNewShow" class="btn btn-large">
|
||||
<div class="button"><img src="$sbRoot/images/add-new32.png" height="32" width="32" alt="Add Recommended Shows"/></div>
|
||||
<div class="buttontext">
|
||||
<h2>Add Recommended Shows</h2>
|
||||
<p>For shows that you haven't downloaded yet, this option recommends shows to add based on your Trakt.tv watch list, creates a directory for its episodes, and adds it to SickRage. *** Trakt.tv must be enabled ***</p>
|
||||
<h2>Add Recommended Show</h2>
|
||||
<p>For shows that you haven't downloaded yet, this option recommends shows to add based on your Trakt.tv show library, creates a directory for its episodes, and adds it to SickRage. *** Trakt.tv must be enabled ***</p>
|
||||
</div>
|
||||
</a>
|
||||
<br/><br/>
|
||||
|
|
58
gui/slick/interfaces/default/home_trendingShows.tmpl
Normal file
58
gui/slick/interfaces/default/home_trendingShows.tmpl
Normal file
|
@ -0,0 +1,58 @@
|
|||
#import sickbeard
|
||||
#import datetime
|
||||
#import re
|
||||
#from sickbeard.common import *
|
||||
#from sickbeard import sbdatetime
|
||||
|
||||
#set global $title="Trending Shows"
|
||||
#set global $header="Trending Shows"
|
||||
|
||||
#set global $sbPath=".."
|
||||
|
||||
#set global $topmenu="comingEpisodes"
|
||||
#import os.path
|
||||
#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/plotTooltip.js?$sbPID"></script>
|
||||
<link rel="stylesheet" type="text/css" href="$sbRoot/css/trakt.css?$sbPID" />
|
||||
|
||||
#if $varExists('header')
|
||||
<h1 class="header">$header</h1>
|
||||
#else
|
||||
<h1 class="title">$title</h1>
|
||||
#end if
|
||||
|
||||
<div class="traktShowDiv">
|
||||
<table width="100%" cellspacing="1" border="0" cellpadding="0">
|
||||
#for $i, $cur_show in $enumerate($trending_shows):
|
||||
<div id="listing_${cur_show["tvdb_id"]}">
|
||||
|
||||
#if not $i%4
|
||||
<tr>
|
||||
#end if
|
||||
|
||||
<th style="background-color: #efefef;" valign="top">
|
||||
<td class="trakt_show">
|
||||
<a href="${cur_show["url"]}"><img alt="" class="traktPosterThumb" src="${cur_show["images"]["poster"]}" /></a>
|
||||
<div class="clearfix">
|
||||
<h2>$cur_show["ratings"]["percentage"]% <img src="$sbRoot/images/like.png"></h2>
|
||||
<i>$cur_show["ratings"]["votes"] votes</i>
|
||||
|
||||
<div class="traktShowTitleIcons">
|
||||
<a href="$sbRoot/home/addTraktShow?indexer_id=${cur_show["tvdb_id"]}&showName=${cur_show["title"]}"><img alt="[add show]" height="16" width="16" src="$sbRoot/images/plus.png"></a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</th>
|
||||
</div>
|
||||
#end for
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
<!--
|
||||
window.setInterval( "location.reload(true)", 600000); // Refresh every 10 minutes
|
||||
//-->
|
||||
</script>
|
||||
|
||||
#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_bottom.tmpl")
|
|
@ -1,30 +1,55 @@
|
|||
if (sbHandleReverseProxy != "False" && sbHandleReverseProxy != 0)
|
||||
// Don't add the port to the url if using reverse proxy
|
||||
if (sbHttpsEnabled != "False" && sbHttpsEnabled != 0)
|
||||
var sb_base_url = 'https://'+sbHost+sbRoot;
|
||||
else
|
||||
var sb_base_url = 'http://'+sbHost+sbRoot;
|
||||
else
|
||||
if (sbHttpsEnabled != "False" && sbHttpsEnabled != 0)
|
||||
var sb_base_url = 'https://'+sbHost+':'+sbHttpPort+sbRoot;
|
||||
else
|
||||
var sb_base_url = 'http://'+sbHost+':'+sbHttpPort+sbRoot;
|
||||
if (sbHttpsEnabled != "False" && sbHttpsEnabled != 0) {
|
||||
var sb_base_url = 'https://' + sbHost + ':' + sbHttpPort + sbRoot;
|
||||
} else {
|
||||
var sb_base_url = 'http://' + sbHost + ':' + sbHttpPort + sbRoot;
|
||||
}
|
||||
|
||||
var base_url = window.location.protocol+'//'+window.location.host+sbRoot;
|
||||
var is_alive_url = sbRoot+'/home/is_alive';
|
||||
var base_url = window.location.protocol + '//' + window.location.host + sbRoot;
|
||||
var is_alive_url = sbRoot + '/home/is_alive/';
|
||||
var timeout_id;
|
||||
var restarted = '';
|
||||
var current_pid = '';
|
||||
var num_restart_waits = 0;
|
||||
|
||||
function restartHandler() {
|
||||
function is_alive() {
|
||||
timeout_id = 0;
|
||||
$.get(is_alive_url, function(data) {
|
||||
|
||||
// if it's still initalizing then just wait and try again
|
||||
if (data.msg == 'nope') {
|
||||
$('#shut_down_loading').hide();
|
||||
$('#shut_down_success').show();
|
||||
$('#restart_message').show();
|
||||
setTimeout('is_alive()', 1000);
|
||||
} else {
|
||||
// if this is before we've even shut down then just try again later
|
||||
if (current_pid == '' || data.msg == current_pid) {
|
||||
current_pid = data.msg;
|
||||
setTimeout(is_alive, 1000);
|
||||
|
||||
// if we're ready to go then redirect to new url
|
||||
} else {
|
||||
$('#restart_loading').hide();
|
||||
$('#restart_success').show();
|
||||
$('#refresh_message').show();
|
||||
window.location = sb_base_url + '/home/';
|
||||
}
|
||||
}
|
||||
}, 'jsonp');
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
is_alive();
|
||||
|
||||
$('#shut_down_message').ajaxError(function(e, jqxhr, settings, exception) {
|
||||
num_restart_waits += 1;
|
||||
|
||||
$('#shut_down_loading').hide();
|
||||
$('#shut_down_success').show();
|
||||
$('#restart_message').show();
|
||||
is_alive_url = sb_base_url+'/home/is_alive';
|
||||
is_alive_url = sb_base_url + '/home/is_alive/';
|
||||
|
||||
// if https is enabled or you are currently on https and the port or protocol changed just wait 5 seconds then redirect.
|
||||
// if https is enabled or you are currently on https and the port or protocol changed just wait 5 seconds then redirect.
|
||||
// This is because the ajax will fail if the cert is untrusted or the the http ajax requst from https will fail because of mixed content error.
|
||||
if ((sbHttpsEnabled != "False" && sbHttpsEnabled != 0) || window.location.protocol == "https:") {
|
||||
if (base_url != sb_base_url) {
|
||||
|
@ -34,16 +59,8 @@ function restartHandler() {
|
|||
$('#restart_success').show();
|
||||
$('#refresh_message').show();
|
||||
}, 3000);
|
||||
setTimeout("window.location = sb_base_url+'/home/'", 5000);
|
||||
setTimeout("window.location = sb_base_url + '/home/'", 5000);
|
||||
}
|
||||
} else {
|
||||
timeout_id = 1;
|
||||
setTimeout(function(){
|
||||
$('#restart_loading').hide();
|
||||
$('#restart_success').show();
|
||||
$('#refresh_message').show();
|
||||
}, 3000);
|
||||
setTimeout("window.location = sb_base_url+'/home/'", 5000);
|
||||
}
|
||||
|
||||
// if it is taking forever just give up
|
||||
|
@ -54,34 +71,9 @@ function restartHandler() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (timeout_id == 0)
|
||||
if (timeout_id == 0) {
|
||||
timeout_id = setTimeout('is_alive()', 1000);
|
||||
}
|
||||
|
||||
function is_alive() {
|
||||
timeout_id = 0;
|
||||
|
||||
$.get(is_alive_url, function(data) {
|
||||
|
||||
// if it's still initalizing then just wait and try again
|
||||
if (data.msg == 'nope') {
|
||||
$('#shut_down_loading').hide();
|
||||
$('#shut_down_success').show();
|
||||
$('#restart_message').show();
|
||||
setTimeout('is_alive()', 1000);
|
||||
} else if (data.restarted == 'True') {
|
||||
restartHandler();
|
||||
} else {
|
||||
// if this is before we've even shut down then just try again later
|
||||
if (restarted == '' || data.restarted == restarted) {
|
||||
restarted = data.restarted;
|
||||
setTimeout(is_alive, 1000);
|
||||
}
|
||||
}
|
||||
}, 'jsonp');
|
||||
}
|
||||
});
|
||||
|
||||
$(document).ready(function()
|
||||
{
|
||||
is_alive();
|
||||
});
|
||||
});
|
183
lib/daemon.py
Normal file
183
lib/daemon.py
Normal file
|
@ -0,0 +1,183 @@
|
|||
# Core modules
|
||||
import atexit
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import signal
|
||||
|
||||
|
||||
class Daemon(object):
|
||||
"""
|
||||
A generic daemon class.
|
||||
|
||||
Usage: subclass the Daemon class and override the run() method
|
||||
"""
|
||||
def __init__(self, pidfile, stdin=os.devnull,
|
||||
stdout=os.devnull, stderr=os.devnull,
|
||||
home_dir='.', umask=022, verbose=1):
|
||||
self.stdin = stdin
|
||||
self.stdout = stdout
|
||||
self.stderr = stderr
|
||||
self.pidfile = pidfile
|
||||
self.home_dir = home_dir
|
||||
self.verbose = verbose
|
||||
self.umask = umask
|
||||
self.daemon_alive = True
|
||||
|
||||
def daemonize(self):
|
||||
"""
|
||||
Do the UNIX double-fork magic, see Stevens' "Advanced
|
||||
Programming in the UNIX Environment" for details (ISBN 0201563177)
|
||||
http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
|
||||
"""
|
||||
try:
|
||||
pid = os.fork()
|
||||
if pid > 0:
|
||||
# Exit first parent
|
||||
os._exit(0)
|
||||
except OSError, e:
|
||||
sys.stderr.write(
|
||||
"fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
|
||||
sys.exit(1)
|
||||
|
||||
# Decouple from parent environment
|
||||
os.chdir(self.home_dir)
|
||||
os.setsid()
|
||||
os.umask(self.umask)
|
||||
|
||||
# Do second fork
|
||||
try:
|
||||
pid = os.fork()
|
||||
if pid > 0:
|
||||
# Exit from second parent
|
||||
os._exit(0)
|
||||
except OSError, e:
|
||||
sys.stderr.write(
|
||||
"fork #2 failed: %d (%s)\n" % (e.errno, e.strerror))
|
||||
sys.exit(1)
|
||||
|
||||
if sys.platform != 'darwin': # This block breaks on OS X
|
||||
# Redirect standard file descriptors
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
si = file(self.stdin, 'r')
|
||||
so = file(self.stdout, 'a+')
|
||||
if self.stderr:
|
||||
se = file(self.stderr, 'a+', 0)
|
||||
else:
|
||||
se = so
|
||||
os.dup2(si.fileno(), sys.stdin.fileno())
|
||||
os.dup2(so.fileno(), sys.stdout.fileno())
|
||||
os.dup2(se.fileno(), sys.stderr.fileno())
|
||||
|
||||
if self.verbose >= 1:
|
||||
print "Started"
|
||||
|
||||
# Write pidfile
|
||||
atexit.register(
|
||||
self.delpid) # Make sure pid file is removed if we quit
|
||||
pid = str(os.getpid())
|
||||
file(self.pidfile, 'w+').write("%s\n" % pid)
|
||||
|
||||
def delpid(self):
|
||||
os.remove(self.pidfile)
|
||||
|
||||
def start(self, *args, **kwargs):
|
||||
"""
|
||||
Start the daemon
|
||||
"""
|
||||
|
||||
if self.verbose >= 1:
|
||||
print "Starting..."
|
||||
|
||||
# Check for a pidfile to see if the daemon already runs
|
||||
try:
|
||||
pf = file(self.pidfile, 'r')
|
||||
pid = int(pf.read().strip())
|
||||
pf.close()
|
||||
except IOError:
|
||||
pid = None
|
||||
except SystemExit:
|
||||
pid = None
|
||||
|
||||
if pid:
|
||||
message = "pidfile %s already exists. Is it already running?\n"
|
||||
sys.stderr.write(message % self.pidfile)
|
||||
sys.exit(1)
|
||||
|
||||
# Start the daemon
|
||||
self.daemonize()
|
||||
self.run(*args, **kwargs)
|
||||
|
||||
def stop(self):
|
||||
"""
|
||||
Stop the daemon
|
||||
"""
|
||||
|
||||
if self.verbose >= 1:
|
||||
print "Stopping..."
|
||||
|
||||
# Get the pid from the pidfile
|
||||
pid = self.get_pid()
|
||||
|
||||
if not pid:
|
||||
message = "pidfile %s does not exist. Not running?\n"
|
||||
sys.stderr.write(message % self.pidfile)
|
||||
|
||||
# Just to be sure. A ValueError might occur if the PID file is
|
||||
# empty but does actually exist
|
||||
if os.path.exists(self.pidfile):
|
||||
os.remove(self.pidfile)
|
||||
|
||||
return # Not an error in a restart
|
||||
|
||||
# Try killing the daemon process
|
||||
try:
|
||||
i = 0
|
||||
while 1:
|
||||
os.kill(pid, signal.SIGTERM)
|
||||
time.sleep(0.1)
|
||||
i = i + 1
|
||||
if i % 10 == 0:
|
||||
os.kill(pid, signal.SIGHUP)
|
||||
except OSError, err:
|
||||
err = str(err)
|
||||
if err.find("No such process") > 0:
|
||||
if os.path.exists(self.pidfile):
|
||||
os.remove(self.pidfile)
|
||||
else:
|
||||
print str(err)
|
||||
sys.exit(1)
|
||||
|
||||
if self.verbose >= 1:
|
||||
print "Stopped"
|
||||
|
||||
def restart(self):
|
||||
"""
|
||||
Restart the daemon
|
||||
"""
|
||||
self.stop()
|
||||
self.start()
|
||||
|
||||
def get_pid(self):
|
||||
try:
|
||||
pf = file(self.pidfile, 'r')
|
||||
pid = int(pf.read().strip())
|
||||
pf.close()
|
||||
except IOError:
|
||||
pid = None
|
||||
except SystemExit:
|
||||
pid = None
|
||||
return pid
|
||||
|
||||
def is_running(self):
|
||||
pid = self.get_pid()
|
||||
print(pid)
|
||||
return pid and os.path.exists('/proc/%d' % pid)
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
You should override this method when you subclass Daemon.
|
||||
It will be called after the process has been
|
||||
daemonized by start() or restart().
|
||||
"""
|
|
@ -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,
|
||||
self.tname,
|
||||
self.pid))
|
||||
"%s%s.%s%s" % (self.hostname,
|
||||
self.tname,
|
||||
self.pid,
|
||||
hash(self.path)))
|
||||
self.timeout = timeout
|
||||
|
||||
def acquire(self, timeout=None):
|
||||
|
|
|
@ -7,7 +7,7 @@ try:
|
|||
except ImportError:
|
||||
from lib import simplejson as json
|
||||
|
||||
def TraktCall(method, api, username, password, data = {}):
|
||||
def TraktCall(method, api, username=None, password=None, data={}):
|
||||
"""
|
||||
A generic method for communicating with trakt. Uses the method and data provided along
|
||||
with the auth info to send the command.
|
||||
|
@ -26,19 +26,16 @@ def TraktCall(method, api, username, password, data = {}):
|
|||
return None
|
||||
|
||||
# if the username isn't given then it failed
|
||||
if not username:
|
||||
return None
|
||||
|
||||
password = sha1(password).hexdigest()
|
||||
if username and password:
|
||||
password = sha1(password).hexdigest()
|
||||
data["username"] = username
|
||||
data["password"] = password
|
||||
|
||||
# replace the API string with what we found
|
||||
method = method.replace("%API%", api)
|
||||
|
||||
data["username"] = username
|
||||
data["password"] = password
|
||||
|
||||
# take the URL params and make a json object out of them
|
||||
encoded_data = json.dumps(data);
|
||||
encoded_data = json.dumps(data)
|
||||
|
||||
# request the URL from trakt and parse the result as json
|
||||
try:
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
from __future__ import with_statement
|
||||
|
||||
import webbrowser
|
||||
import time
|
||||
import datetime
|
||||
import socket
|
||||
import os
|
||||
|
@ -29,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, \
|
||||
|
@ -104,7 +104,7 @@ CUR_COMMIT_HASH = None
|
|||
|
||||
INIT_LOCK = Lock()
|
||||
started = False
|
||||
restarted = False
|
||||
shutdown = False
|
||||
|
||||
ACTUAL_LOG_DIR = None
|
||||
LOG_DIR = None
|
||||
|
@ -432,7 +432,7 @@ IGNORE_WORDS = "german,french,core2hd,dutch,swedish,reenc,MrLss"
|
|||
CALENDAR_UNPROTECTED = False
|
||||
|
||||
TMDB_API_KEY = 'edc5f123313769de83a71e157758030b'
|
||||
|
||||
TRAKT_API_KEY = 'abd806c54516240c76e4ebc9c5ccf394'
|
||||
|
||||
__INITIALIZED__ = False
|
||||
def initialize(consoleLogging=True):
|
||||
|
@ -1270,7 +1270,7 @@ def halt():
|
|||
pass
|
||||
|
||||
__INITIALIZED__ = False
|
||||
|
||||
started = False
|
||||
|
||||
def remove_pid_file(PIDFILE):
|
||||
try:
|
||||
|
@ -1286,7 +1286,7 @@ def remove_pid_file(PIDFILE):
|
|||
def sig_handler(signum=None, frame=None):
|
||||
if type(signum) != type(None):
|
||||
logger.log(u"Signal %i caught, saving and exiting..." % int(signum))
|
||||
webserveInit.shutdown()
|
||||
saveAndShutdown()
|
||||
|
||||
def saveAll():
|
||||
global showList
|
||||
|
@ -1300,9 +1300,15 @@ def saveAll():
|
|||
logger.log(u"Saving config file to disk")
|
||||
save_config()
|
||||
|
||||
def saveAndShutdown():
|
||||
halt()
|
||||
saveAll()
|
||||
def saveAndShutdown(restart=False):
|
||||
global shutdown, started
|
||||
|
||||
# flag restart/shutdown
|
||||
if not restart:
|
||||
shutdown = True
|
||||
|
||||
# proceed with shutdown
|
||||
started = False
|
||||
|
||||
def invoke_command(to_call, *args, **kwargs):
|
||||
|
||||
|
@ -1317,23 +1323,17 @@ def invoke_command(to_call, *args, **kwargs):
|
|||
def invoke_restart(soft=True):
|
||||
invoke_command(restart, soft=soft)
|
||||
|
||||
|
||||
def invoke_shutdown():
|
||||
invoke_command(webserveInit.shutdown)
|
||||
|
||||
invoke_command(saveAndShutdown, False)
|
||||
|
||||
def restart(soft=True):
|
||||
global restarted
|
||||
|
||||
if soft:
|
||||
halt()
|
||||
saveAll()
|
||||
logger.log(u"Re-initializing all data")
|
||||
initialize()
|
||||
else:
|
||||
restarted=True
|
||||
time.sleep(5)
|
||||
webserveInit.shutdown()
|
||||
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)
|
||||
|
|
|
@ -147,9 +147,9 @@ class TraktChecker():
|
|||
"""
|
||||
Adds a new show with the default settings
|
||||
"""
|
||||
showObj = helpers.findCertainShow(sickbeard.showList, int(indexerid))
|
||||
if showObj != None:
|
||||
if helpers.findCertainShow(sickbeard.showList, int(indexerid)):
|
||||
return
|
||||
|
||||
logger.log(u"Adding show " + str(indexerid))
|
||||
root_dirs = sickbeard.ROOT_DIRS.split('|')
|
||||
location = root_dirs[int(root_dirs[0]) + 1]
|
||||
|
@ -161,6 +161,7 @@ class TraktChecker():
|
|||
return
|
||||
else:
|
||||
helpers.chmodAsParent(showPath)
|
||||
|
||||
sickbeard.showQueueScheduler.action.addShow(1, int(indexerid), showPath, status,
|
||||
int(sickbeard.QUALITY_DEFAULT),
|
||||
int(sickbeard.FLATTEN_FOLDERS_DEFAULT))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -48,7 +48,7 @@ from sickbeard import subtitles
|
|||
from sickbeard import network_timezones
|
||||
|
||||
from sickbeard.providers import newznab, rsstorrent
|
||||
from sickbeard.common import Quality, Overview, statusStrings, qualityPresetStrings, cpu_presets
|
||||
from sickbeard.common import Quality, Overview, statusStrings, qualityPresetStrings, cpu_presets, SKIPPED
|
||||
from sickbeard.common import SNATCHED, UNAIRED, IGNORED, ARCHIVED, WANTED, FAILED
|
||||
from sickbeard.common import SD, HD720p, HD1080p
|
||||
from sickbeard.exceptions import ex
|
||||
|
@ -82,6 +82,7 @@ from lib import adba
|
|||
from Cheetah.Template import Template
|
||||
from tornado.web import RequestHandler, HTTPError
|
||||
|
||||
|
||||
def authenticated(handler_class):
|
||||
def wrap_execute(handler_execute):
|
||||
def basicauth(handler, transforms, *args, **kwargs):
|
||||
|
@ -125,6 +126,7 @@ def authenticated(handler_class):
|
|||
handler_class._execute = wrap_execute(handler_class._execute)
|
||||
return handler_class
|
||||
|
||||
|
||||
class HTTPRedirect(Exception):
|
||||
"""Exception raised when the request should be redirected."""
|
||||
|
||||
|
@ -138,9 +140,11 @@ class HTTPRedirect(Exception):
|
|||
"""Use this exception as a request.handler (raise self)."""
|
||||
raise self
|
||||
|
||||
|
||||
def redirect(url, permanent=False, status=None):
|
||||
raise HTTPRedirect(url, permanent, status)
|
||||
|
||||
|
||||
@authenticated
|
||||
class MainHandler(RequestHandler):
|
||||
def http_error_401_handler(self):
|
||||
|
@ -216,7 +220,7 @@ class MainHandler(RequestHandler):
|
|||
def get(self, *args, **kwargs):
|
||||
try:
|
||||
self.finish(self._dispatch())
|
||||
except HTTPRedirect,inst:
|
||||
except HTTPRedirect, inst:
|
||||
self.redirect(inst.url, inst.permanent, inst.status)
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
|
@ -462,6 +466,7 @@ class MainHandler(RequestHandler):
|
|||
|
||||
browser = WebFileBrowser
|
||||
|
||||
|
||||
class PageTemplate(Template):
|
||||
def __init__(self, headers, *args, **KWs):
|
||||
KWs['file'] = os.path.join(sickbeard.PROG_DIR, "gui/" + sickbeard.GUI_NAME + "/interfaces/default/",
|
||||
|
@ -499,7 +504,7 @@ class PageTemplate(Template):
|
|||
{'title': 'Manage', 'key': 'manage'},
|
||||
{'title': 'Config', 'key': 'config'},
|
||||
{'title': logPageTitle, 'key': 'errorlogs'},
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
class IndexerWebUI(MainHandler):
|
||||
|
@ -518,6 +523,7 @@ class IndexerWebUI(MainHandler):
|
|||
def _munge(string):
|
||||
return unicode(string).encode('utf-8', 'xmlcharrefreplace')
|
||||
|
||||
|
||||
def _getEpisode(show, season=None, episode=None, absolute=None):
|
||||
if show is None:
|
||||
return "Invalid show parameters"
|
||||
|
@ -2643,7 +2649,7 @@ class NewHomeAddShows(MainHandler):
|
|||
'display_dir': '<b>' + ek.ek(os.path.dirname, cur_path) + os.sep + '</b>' + ek.ek(
|
||||
os.path.basename,
|
||||
cur_path),
|
||||
}
|
||||
}
|
||||
|
||||
# see if the folder is in XBMC already
|
||||
dirResults = myDB.select("SELECT * FROM tv_shows WHERE location = ?", [cur_path])
|
||||
|
@ -2745,10 +2751,10 @@ class NewHomeAddShows(MainHandler):
|
|||
logger.log(u"Could not connect to trakt service, aborting recommended list update", logger.ERROR)
|
||||
return
|
||||
|
||||
map(final_results.append, ([int(show['tvdb_id']), show['url'], show['title'], show['overview'],
|
||||
datetime.date.fromtimestamp(show['first_aired']).strftime('%Y%m%d')] for show in
|
||||
recommendedlist if
|
||||
not helpers.findCertainShow(sickbeard.showList, indexerid=int(show['tvdb_id']))))
|
||||
map(final_results.append,
|
||||
([int(show['tvdb_id']), show['url'], show['title'], show['overview'],
|
||||
datetime.date.fromtimestamp(int(show['first_aired']) / 1000.0).strftime('%Y%m%d')] for show in
|
||||
recommendedlist if not helpers.findCertainShow(sickbeard.showList, indexerid=int(show['tvdb_id']))))
|
||||
|
||||
return json.dumps({'results': final_results})
|
||||
|
||||
|
@ -2764,10 +2770,22 @@ class NewHomeAddShows(MainHandler):
|
|||
show_name = whichSeries.split('|')[2]
|
||||
|
||||
return self.addNewShow('|'.join([indexer_name, str(indexer), show_url, indexer_id, show_name, ""]),
|
||||
indexerLang, rootDir,
|
||||
defaultStatus,
|
||||
anyQualities, bestQualities, flatten_folders, subtitles, fullShowPath, other_shows,
|
||||
skipShow, providedIndexer, anime, scene)
|
||||
indexerLang, rootDir,
|
||||
defaultStatus,
|
||||
anyQualities, bestQualities, flatten_folders, subtitles, fullShowPath, other_shows,
|
||||
skipShow, providedIndexer, anime, scene)
|
||||
|
||||
def trendingShows(self, *args, **kwargs):
|
||||
"""
|
||||
Display the new show page which collects a tvdb id, folder, and extra options and
|
||||
posts them to addNewShow
|
||||
"""
|
||||
t = PageTemplate(headers=self.request.headers, file="home_trendingShows.tmpl")
|
||||
t.submenu = HomeMenu()
|
||||
|
||||
t.trending_shows = TraktCall("shows/trending.json/%API%/", sickbeard.TRAKT_API_KEY)
|
||||
|
||||
return _munge(t)
|
||||
|
||||
def existingShows(self, *args, **kwargs):
|
||||
"""
|
||||
|
@ -2778,6 +2796,33 @@ class NewHomeAddShows(MainHandler):
|
|||
|
||||
return _munge(t)
|
||||
|
||||
def addTraktShow(self, indexer_id, showName):
|
||||
if helpers.findCertainShow(sickbeard.showList, int(indexer_id)):
|
||||
return
|
||||
|
||||
root_dirs = sickbeard.ROOT_DIRS.split('|')
|
||||
location = root_dirs[int(root_dirs[0]) + 1]
|
||||
|
||||
show_dir = ek.ek(os.path.join, location, helpers.sanitizeFileName(showName))
|
||||
dir_exists = helpers.makeDir(show_dir)
|
||||
if not dir_exists:
|
||||
logger.log(u"Unable to create the folder " + show_dir + ", can't add the show", logger.ERROR)
|
||||
return
|
||||
else:
|
||||
helpers.chmodAsParent(show_dir)
|
||||
|
||||
sickbeard.showQueueScheduler.action.addShow(1, int(indexer_id), show_dir,
|
||||
default_status=sickbeard.STATUS_DEFAULT,
|
||||
quality=sickbeard.QUALITY_DEFAULT,
|
||||
flatten_folders=sickbeard.FLATTEN_FOLDERS_DEFAULT,
|
||||
subtitles=sickbeard.SUBTITLES_DEFAULT,
|
||||
anime=sickbeard.ANIME_DEFAULT,
|
||||
scene=sickbeard.SCENE_DEFAULT)
|
||||
|
||||
ui.notifications.message('Show added', 'Adding the specified show into ' + show_dir)
|
||||
|
||||
# done adding show
|
||||
redirect('/home/')
|
||||
|
||||
def addNewShow(self, whichSeries=None, indexerLang="en", rootDir=None, defaultStatus=None,
|
||||
anyQualities=None, bestQualities=None, flatten_folders=None, subtitles=None,
|
||||
|
@ -3054,9 +3099,9 @@ class Home(MainHandler):
|
|||
|
||||
if sickbeard.started:
|
||||
return callback + '(' + json.dumps(
|
||||
{"msg": str(sickbeard.PID), "restarted": str(sickbeard.restarted)}) + ');'
|
||||
{"msg": str(sickbeard.PID)}) + ');'
|
||||
else:
|
||||
return callback + '(' + json.dumps({"msg": "nope", "restarted": str(sickbeard.restarted)}) + ');'
|
||||
return callback + '(' + json.dumps({"msg": "nope"}) + ');'
|
||||
|
||||
|
||||
def index(self, *args, **kwargs):
|
||||
|
@ -3379,7 +3424,6 @@ class Home(MainHandler):
|
|||
|
||||
return _munge(t)
|
||||
|
||||
|
||||
def update(self, pid=None):
|
||||
|
||||
if str(pid) != str(sickbeard.PID):
|
||||
|
@ -3394,7 +3438,7 @@ class Home(MainHandler):
|
|||
return _munge(t)
|
||||
else:
|
||||
return self._genericMessage("Update Failed",
|
||||
"Update wasn't successful, not restarting. Check your log for more information.")
|
||||
"Update wasn't successful, not restarting. Check your log for more information.")
|
||||
|
||||
|
||||
def displayShow(self, show=None):
|
||||
|
@ -3999,7 +4043,6 @@ class Home(MainHandler):
|
|||
myDB = db.DBConnection()
|
||||
myDB.mass_action(sql_l)
|
||||
|
||||
|
||||
if int(status) == WANTED:
|
||||
msg = "Backlog was automatically started for the following seasons of <b>" + showObj.name + "</b>:<br />"
|
||||
for season in segment:
|
||||
|
@ -4294,9 +4337,9 @@ class Home(MainHandler):
|
|||
|
||||
return json.dumps({'result': 'failure'})
|
||||
|
||||
|
||||
class UI(MainHandler):
|
||||
def add_message(self):
|
||||
|
||||
ui.notifications.message('Test 1', 'This is test number 1')
|
||||
ui.notifications.error('Test 2', 'This is test number 2')
|
||||
|
||||
|
@ -4307,8 +4350,8 @@ class UI(MainHandler):
|
|||
cur_notification_num = 1
|
||||
for cur_notification in ui.notifications.get_notifications(self.request.remote_ip):
|
||||
messages['notification-' + str(cur_notification_num)] = {'title': cur_notification.title,
|
||||
'message': cur_notification.message,
|
||||
'type': cur_notification.type}
|
||||
'message': cur_notification.message,
|
||||
'type': cur_notification.type}
|
||||
cur_notification_num += 1
|
||||
|
||||
return json.dumps(messages)
|
|
@ -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,87 +34,106 @@ 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']
|
||||
# tornado setup
|
||||
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 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):
|
||||
logger.log(u"Unable to create CERT/KEY files, disabling HTTPS")
|
||||
if self.enable_https:
|
||||
# If either the HTTPS certificate or key do not exist, make some self-signed ones.
|
||||
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(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
|
||||
|
||||
if not (os.path.exists(https_cert) and os.path.exists(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
|
||||
self.app = Application([],
|
||||
debug=False,
|
||||
gzip=True,
|
||||
xheaders=sickbeard.HANDLE_REVERSE_PROXY,
|
||||
cookie_secret='61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo='
|
||||
)
|
||||
|
||||
# Load the app
|
||||
app = Application([],
|
||||
debug=False,
|
||||
gzip=True,
|
||||
xheaders=sickbeard.HANDLE_REVERSE_PROXY,
|
||||
cookie_secret='61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo='
|
||||
)
|
||||
# Main Handler
|
||||
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)
|
||||
])
|
||||
|
||||
# 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)
|
||||
])
|
||||
# Static Path Handler
|
||||
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/(.*)(/?)' % (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')]})
|
||||
|
||||
# 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'),
|
||||
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')]})
|
||||
])
|
||||
|
||||
])
|
||||
def run(self):
|
||||
if self.enable_https:
|
||||
protocol = "https"
|
||||
self.server = HTTPServer(self.app, no_keep_alive=True,
|
||||
ssl_options={"certfile": self.https_cert, "keyfile": self.https_key})
|
||||
else:
|
||||
protocol = "http"
|
||||
self.server = HTTPServer(self.app, no_keep_alive=True)
|
||||
|
||||
global server
|
||||
logger.log(u"Starting SickRage on " + protocol + "://" + str(self.options['host']) + ":" + str(
|
||||
self.options['port']) + "/")
|
||||
|
||||
if enable_https:
|
||||
protocol = "https"
|
||||
server = HTTPServer(app, no_keep_alive=True,
|
||||
ssl_options={"certfile": https_cert, "keyfile": https_key})
|
||||
else:
|
||||
protocol = "http"
|
||||
server = HTTPServer(app, no_keep_alive=True)
|
||||
try:
|
||||
self.server.listen(self.options['port'], self.options['host'])
|
||||
except:
|
||||
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
|
||||
|
||||
logger.log(u"Starting SickRage on " + protocol + "://" + str(options['host']) + ":" + str(
|
||||
options['port']) + "/")
|
||||
try:
|
||||
self.io_loop.start()
|
||||
self.io_loop.close(True)
|
||||
|
||||
if not sickbeard.restarted:
|
||||
server.listen(options['port'], options['host'])
|
||||
# stop all tasks
|
||||
sickbeard.halt()
|
||||
|
||||
def shutdown():
|
||||
global server
|
||||
# save all shows to DB
|
||||
sickbeard.saveAll()
|
||||
|
||||
logger.log('Shutting down tornado io loop')
|
||||
try:
|
||||
IOLoop.current().stop()
|
||||
except RuntimeError:
|
||||
pass
|
||||
except:
|
||||
logger.log('Failed shutting down tornado io loop: %s' % traceback.format_exc(), logger.ERROR)
|
||||
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 a new issue