mirror of
https://github.com/SickGear/SickGear.git
synced 2025-01-08 11:03:38 +00:00
Merge remote-tracking branch 'origin/dev'
This commit is contained in:
commit
ae398031fd
27 changed files with 923 additions and 501 deletions
255
SickBeard.py
255
SickBeard.py
|
@ -19,10 +19,11 @@
|
||||||
|
|
||||||
# Check needed software dependencies to nudge users to fix their setup
|
# Check needed software dependencies to nudge users to fix their setup
|
||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
import functools
|
|
||||||
|
|
||||||
|
import time
|
||||||
import sys
|
import sys
|
||||||
import shutil
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
|
||||||
if sys.version_info < (2, 6):
|
if sys.version_info < (2, 6):
|
||||||
print "Sorry, requires Python 2.6 or 2.7."
|
print "Sorry, requires Python 2.6 or 2.7."
|
||||||
|
@ -60,21 +61,22 @@ import sickbeard
|
||||||
from sickbeard import db
|
from sickbeard import db
|
||||||
from sickbeard.tv import TVShow
|
from sickbeard.tv import TVShow
|
||||||
from sickbeard import logger
|
from sickbeard import logger
|
||||||
from sickbeard import webserveInit
|
from sickbeard.webserveInit import SRWebServer
|
||||||
from sickbeard.version import SICKBEARD_VERSION
|
from sickbeard.version import SICKBEARD_VERSION
|
||||||
from sickbeard.databases.mainDB import MIN_DB_VERSION
|
from sickbeard.databases.mainDB import MIN_DB_VERSION
|
||||||
from sickbeard.databases.mainDB import MAX_DB_VERSION
|
from sickbeard.databases.mainDB import MAX_DB_VERSION
|
||||||
|
|
||||||
from lib.configobj import ConfigObj
|
from lib.configobj import ConfigObj
|
||||||
|
from daemon import Daemon
|
||||||
from tornado.ioloop import IOLoop
|
|
||||||
|
|
||||||
signal.signal(signal.SIGINT, sickbeard.sig_handler)
|
signal.signal(signal.SIGINT, sickbeard.sig_handler)
|
||||||
signal.signal(signal.SIGTERM, sickbeard.sig_handler)
|
signal.signal(signal.SIGTERM, sickbeard.sig_handler)
|
||||||
|
|
||||||
throwaway = datetime.datetime.strptime('20110101', '%Y%m%d')
|
throwaway = datetime.datetime.strptime('20110101', '%Y%m%d')
|
||||||
|
|
||||||
def loadShowsFromDB():
|
class SickRage(object):
|
||||||
|
|
||||||
|
def loadShowsFromDB(self):
|
||||||
"""
|
"""
|
||||||
Populates the showList with shows from the database
|
Populates the showList with shows from the database
|
||||||
"""
|
"""
|
||||||
|
@ -95,46 +97,7 @@ def loadShowsFromDB():
|
||||||
logger.ERROR)
|
logger.ERROR)
|
||||||
logger.log(traceback.format_exc(), logger.DEBUG)
|
logger.log(traceback.format_exc(), logger.DEBUG)
|
||||||
|
|
||||||
# TODO: update the existing shows if the showlist has something in it
|
def restore(self, srcDir, dstDir):
|
||||||
|
|
||||||
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))
|
|
||||||
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) + "]")
|
|
||||||
|
|
||||||
dev_null = file('/dev/null', 'r')
|
|
||||||
os.dup2(dev_null.fileno(), sys.stdin.fileno())
|
|
||||||
|
|
||||||
def restore(srcDir, dstDir):
|
|
||||||
try:
|
try:
|
||||||
for file in os.listdir(srcDir):
|
for file in os.listdir(srcDir):
|
||||||
srcFile = os.path.join(srcDir, file)
|
srcFile = os.path.join(srcDir, file)
|
||||||
|
@ -148,20 +111,23 @@ def restore(srcDir, dstDir):
|
||||||
except:
|
except:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def main():
|
def __init__(self):
|
||||||
"""
|
self.daemon = None
|
||||||
TV for me
|
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
|
# do some preliminary stuff
|
||||||
sickbeard.MY_FULLNAME = os.path.normpath(os.path.abspath(__file__))
|
sickbeard.MY_FULLNAME = os.path.normpath(os.path.abspath(__file__))
|
||||||
sickbeard.MY_NAME = os.path.basename(sickbeard.MY_FULLNAME)
|
sickbeard.MY_NAME = os.path.basename(sickbeard.MY_FULLNAME)
|
||||||
sickbeard.PROG_DIR = os.path.dirname(sickbeard.MY_FULLNAME)
|
sickbeard.PROG_DIR = os.path.dirname(sickbeard.MY_FULLNAME)
|
||||||
sickbeard.DATA_DIR = sickbeard.PROG_DIR
|
sickbeard.DATA_DIR = sickbeard.PROG_DIR
|
||||||
sickbeard.MY_ARGS = sys.argv[1:]
|
sickbeard.MY_ARGS = sys.argv[1:]
|
||||||
sickbeard.DAEMON = False
|
|
||||||
sickbeard.CREATEPID = False
|
|
||||||
|
|
||||||
sickbeard.SYS_ENCODING = None
|
sickbeard.SYS_ENCODING = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -187,7 +153,7 @@ def main():
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Need console logging for SickBeard.py and SickBeard-console.exe
|
# Need console logging for SickBeard.py and SickBeard-console.exe
|
||||||
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
|
# Rename the main thread
|
||||||
threading.currentThread().name = "MAIN"
|
threading.currentThread().name = "MAIN"
|
||||||
|
@ -200,38 +166,34 @@ def main():
|
||||||
print "Available Options: --quiet, --forceupdate, --port, --daemon, --pidfile, --config, --datadir"
|
print "Available Options: --quiet, --forceupdate, --port, --daemon, --pidfile, --config, --datadir"
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
forceUpdate = False
|
|
||||||
forcedPort = None
|
|
||||||
noLaunch = False
|
|
||||||
|
|
||||||
for o, a in opts:
|
for o, a in opts:
|
||||||
# For now we'll just silence the logging
|
# For now we'll just silence the logging
|
||||||
if o in ('-q', '--quiet'):
|
if o in ('-q', '--quiet'):
|
||||||
consoleLogging = False
|
self.consoleLogging = False
|
||||||
|
|
||||||
# Should we update (from indexer) all shows in the DB right away?
|
# Should we update (from indexer) all shows in the DB right away?
|
||||||
if o in ('-f', '--forceupdate'):
|
if o in ('-f', '--forceupdate'):
|
||||||
forceUpdate = True
|
self.forceUpdate = True
|
||||||
|
|
||||||
# Suppress launching web browser
|
# Suppress launching web browser
|
||||||
# Needed for OSes without default browser assigned
|
# Needed for OSes without default browser assigned
|
||||||
# Prevent duplicate browser window when restarting in the app
|
# Prevent duplicate browser window when restarting in the app
|
||||||
if o in ('--nolaunch',):
|
if o in ('--nolaunch',):
|
||||||
noLaunch = True
|
self.noLaunch = True
|
||||||
|
|
||||||
# Override default/configured port
|
# Override default/configured port
|
||||||
if o in ('-p', '--port'):
|
if o in ('-p', '--port'):
|
||||||
forcedPort = int(a)
|
self.forcedPort = int(a)
|
||||||
|
|
||||||
# Run as a double forked daemon
|
# Run as a double forked daemon
|
||||||
if o in ('-d', '--daemon'):
|
if o in ('-d', '--daemon'):
|
||||||
sickbeard.DAEMON = True
|
self.runAsDaemon = True
|
||||||
# When running as daemon disable consoleLogging and don't start browser
|
# When running as daemon disable consoleLogging and don't start browser
|
||||||
consoleLogging = False
|
self.consoleLogging = False
|
||||||
noLaunch = True
|
self.noLaunch = True
|
||||||
|
|
||||||
if sys.platform == 'win32':
|
if sys.platform == 'win32':
|
||||||
sickbeard.DAEMON = False
|
self.runAsDaemon = False
|
||||||
|
|
||||||
# Specify folder to load the config file from
|
# Specify folder to load the config file from
|
||||||
if o in ('--config',):
|
if o in ('--config',):
|
||||||
|
@ -247,27 +209,27 @@ def main():
|
||||||
|
|
||||||
# Write a pidfile if requested
|
# Write a pidfile if requested
|
||||||
if o in ('--pidfile',):
|
if o in ('--pidfile',):
|
||||||
sickbeard.CREATEPID = True
|
self.CREATEPID = True
|
||||||
sickbeard.PIDFILE = str(a)
|
self.PIDFILE = str(a)
|
||||||
|
|
||||||
# If the pidfile already exists, sickbeard may still be running, so exit
|
# If the pidfile already exists, sickbeard may still be running, so exit
|
||||||
if os.path.exists(sickbeard.PIDFILE):
|
if os.path.exists(self.PIDFILE):
|
||||||
sys.exit("PID file: " + sickbeard.PIDFILE + " already exists. Exiting.")
|
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
|
# The pidfile is only useful in daemon mode, make sure we can write the file properly
|
||||||
if sickbeard.CREATEPID and not sickbeard.restarted:
|
if self.CREATEPID:
|
||||||
if sickbeard.DAEMON:
|
if self.runAsDaemon:
|
||||||
pid_dir = os.path.dirname(sickbeard.PIDFILE)
|
pid_dir = os.path.dirname(self.PIDFILE)
|
||||||
if not os.access(pid_dir, os.F_OK):
|
if not os.access(pid_dir, os.F_OK):
|
||||||
sys.exit("PID dir: " + pid_dir + " doesn't exist. Exiting.")
|
sys.exit("PID dir: " + pid_dir + " doesn't exist. Exiting.")
|
||||||
if not os.access(pid_dir, os.W_OK):
|
if not os.access(pid_dir, os.W_OK):
|
||||||
sys.exit("PID dir: " + pid_dir + " must be writable (write permissions). Exiting.")
|
sys.exit("PID dir: " + pid_dir + " must be writable (write permissions). Exiting.")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if consoleLogging:
|
if self.consoleLogging:
|
||||||
sys.stdout.write("Not running in daemon mode. PID file creation disabled.\n")
|
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 they don't specify a config file then put it in the data dir
|
||||||
if not sickbeard.CONFIG_FILE:
|
if not sickbeard.CONFIG_FILE:
|
||||||
|
@ -289,21 +251,19 @@ def main():
|
||||||
if os.path.isfile(sickbeard.CONFIG_FILE):
|
if os.path.isfile(sickbeard.CONFIG_FILE):
|
||||||
raise SystemExit("Config file '" + sickbeard.CONFIG_FILE + "' must be writeable.")
|
raise SystemExit("Config file '" + sickbeard.CONFIG_FILE + "' must be writeable.")
|
||||||
elif not os.access(os.path.dirname(sickbeard.CONFIG_FILE), os.W_OK):
|
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.")
|
raise SystemExit(
|
||||||
|
"Config file root dir '" + os.path.dirname(sickbeard.CONFIG_FILE) + "' must be writeable.")
|
||||||
|
|
||||||
# Check if we need to perform a restore first
|
# Check if we need to perform a restore first
|
||||||
restoreDir = os.path.join(sickbeard.DATA_DIR, 'restore')
|
restoreDir = os.path.join(sickbeard.DATA_DIR, 'restore')
|
||||||
if os.path.exists(restoreDir):
|
if os.path.exists(restoreDir):
|
||||||
if restore(restoreDir, sickbeard.DATA_DIR):
|
if self.restore(restoreDir, sickbeard.DATA_DIR):
|
||||||
logger.log(u"Restore successful...")
|
logger.log(u"Restore successful...")
|
||||||
else:
|
else:
|
||||||
logger.log(u"Restore FAILED!", logger.ERROR)
|
logger.log(u"Restore FAILED!", logger.ERROR)
|
||||||
|
|
||||||
os.chdir(sickbeard.DATA_DIR)
|
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
|
# Load the config and publish it to the sickbeard package
|
||||||
if not os.path.isfile(sickbeard.CONFIG_FILE):
|
if not os.path.isfile(sickbeard.CONFIG_FILE):
|
||||||
logger.log(u"Unable to find '" + sickbeard.CONFIG_FILE + "' , all settings will be default!", logger.ERROR)
|
logger.log(u"Unable to find '" + sickbeard.CONFIG_FILE + "' , all settings will be default!", logger.ERROR)
|
||||||
|
@ -325,41 +285,43 @@ def main():
|
||||||
"If you have used other forks of SB, your database may be unusable due to their modifications.")
|
"If you have used other forks of SB, your database may be unusable due to their modifications.")
|
||||||
|
|
||||||
# Initialize the config and our threads
|
# Initialize the config and our threads
|
||||||
sickbeard.initialize(consoleLogging=consoleLogging)
|
sickbeard.initialize(consoleLogging=self.consoleLogging)
|
||||||
|
|
||||||
if sickbeard.DAEMON:
|
if self.runAsDaemon:
|
||||||
daemonize()
|
self.daemon = Daemon(self.PIDFILE or os.path.join(sickbeard.DATA_DIR, 'sickbeard.pid'))
|
||||||
|
self.daemon.daemonize()
|
||||||
|
|
||||||
# Use this PID for everything
|
# Get PID
|
||||||
sickbeard.PID = os.getpid()
|
sickbeard.PID = os.getpid()
|
||||||
|
|
||||||
if forcedPort:
|
if self.forcedPort:
|
||||||
logger.log(u"Forcing web server to port " + str(forcedPort))
|
logger.log(u"Forcing web server to port " + str(self.forcedPort))
|
||||||
startPort = forcedPort
|
self.startPort = self.forcedPort
|
||||||
else:
|
else:
|
||||||
startPort = sickbeard.WEB_PORT
|
self.startPort = sickbeard.WEB_PORT
|
||||||
|
|
||||||
if sickbeard.WEB_LOG:
|
if sickbeard.WEB_LOG:
|
||||||
log_dir = sickbeard.LOG_DIR
|
self.log_dir = sickbeard.LOG_DIR
|
||||||
else:
|
else:
|
||||||
log_dir = None
|
self.log_dir = None
|
||||||
|
|
||||||
# sickbeard.WEB_HOST is available as a configuration value in various
|
# sickbeard.WEB_HOST is available as a configuration value in various
|
||||||
# places but is not configurable. It is supported here for historic reasons.
|
# places but is not configurable. It is supported here for historic reasons.
|
||||||
if sickbeard.WEB_HOST and sickbeard.WEB_HOST != '0.0.0.0':
|
if sickbeard.WEB_HOST and sickbeard.WEB_HOST != '0.0.0.0':
|
||||||
webhost = sickbeard.WEB_HOST
|
self.webhost = sickbeard.WEB_HOST
|
||||||
else:
|
else:
|
||||||
if sickbeard.WEB_IPV6:
|
if sickbeard.WEB_IPV6:
|
||||||
webhost = '::'
|
self.webhost = '::'
|
||||||
else:
|
else:
|
||||||
webhost = '0.0.0.0'
|
self.webhost = '0.0.0.0'
|
||||||
|
|
||||||
options = {
|
# web server options
|
||||||
'port': int(startPort),
|
self.web_options = {
|
||||||
'host': webhost,
|
'port': int(self.startPort),
|
||||||
|
'host': self.webhost,
|
||||||
'data_root': os.path.join(sickbeard.PROG_DIR, 'gui', sickbeard.GUI_NAME),
|
'data_root': os.path.join(sickbeard.PROG_DIR, 'gui', sickbeard.GUI_NAME),
|
||||||
'web_root': sickbeard.WEB_ROOT,
|
'web_root': sickbeard.WEB_ROOT,
|
||||||
'log_dir': log_dir,
|
'log_dir': self.log_dir,
|
||||||
'username': sickbeard.WEB_USERNAME,
|
'username': sickbeard.WEB_USERNAME,
|
||||||
'password': sickbeard.WEB_PASSWORD,
|
'password': sickbeard.WEB_PASSWORD,
|
||||||
'enable_https': sickbeard.ENABLE_HTTPS,
|
'enable_https': sickbeard.ENABLE_HTTPS,
|
||||||
|
@ -368,60 +330,87 @@ def main():
|
||||||
'https_key': sickbeard.HTTPS_KEY,
|
'https_key': sickbeard.HTTPS_KEY,
|
||||||
}
|
}
|
||||||
|
|
||||||
# init tornado
|
# start web server
|
||||||
try:
|
try:
|
||||||
webserveInit.initWebServer(options)
|
self.webserver = SRWebServer(self.web_options)
|
||||||
|
self.webserver.start()
|
||||||
except IOError:
|
except IOError:
|
||||||
logger.log(u"Unable to start web server, is something else running on port %d?" % startPort, logger.ERROR)
|
logger.log(u"Unable to start web server, is something else running on port %d?" % self.startPort,
|
||||||
if sickbeard.LAUNCH_BROWSER and not sickbeard.DAEMON:
|
logger.ERROR)
|
||||||
|
if sickbeard.LAUNCH_BROWSER and not self.runAsDaemon:
|
||||||
logger.log(u"Launching browser and exiting", logger.ERROR)
|
logger.log(u"Launching browser and exiting", logger.ERROR)
|
||||||
sickbeard.launchBrowser(startPort)
|
sickbeard.launchBrowser(self.startPort)
|
||||||
sys.exit()
|
os._exit(1)
|
||||||
|
|
||||||
|
if self.consoleLogging:
|
||||||
|
print "Starting up SickRage " + SICKBEARD_VERSION + " from " + sickbeard.CONFIG_FILE
|
||||||
|
|
||||||
# Build from the DB to start with
|
# Build from the DB to start with
|
||||||
loadShowsFromDB()
|
self.loadShowsFromDB()
|
||||||
|
|
||||||
# Fire up all our threads
|
# Fire up all our threads
|
||||||
sickbeard.start()
|
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
|
# 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
|
sickbeard.showUpdateScheduler.action.run(force=True) # @UndefinedVariable
|
||||||
|
|
||||||
# If we restarted then unset the restarted flag
|
if sickbeard.LAUNCH_BROWSER and not (self.noLaunch or self.runAsDaemon):
|
||||||
if sickbeard.restarted:
|
sickbeard.launchBrowser(self.startPort)
|
||||||
sickbeard.restarted = False
|
|
||||||
|
|
||||||
# IOLoop
|
while(sickbeard.started):
|
||||||
io_loop = IOLoop.current()
|
time.sleep(1)
|
||||||
|
|
||||||
# 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()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
if sys.hexversion >= 0x020600F0:
|
if sys.hexversion >= 0x020600F0:
|
||||||
freeze_support()
|
freeze_support()
|
||||||
|
|
||||||
while(True):
|
sr = None
|
||||||
main()
|
try:
|
||||||
|
# init sickrage
|
||||||
|
sr = SickRage()
|
||||||
|
|
||||||
# check if restart was requested
|
# start sickrage
|
||||||
if not sickbeard.restarted:
|
sr.start()
|
||||||
if sickbeard.CREATEPID:
|
|
||||||
logger.log(u"Removing pidfile " + str(sickbeard.PIDFILE))
|
|
||||||
sickbeard.remove_pid_file(sickbeard.PIDFILE)
|
|
||||||
break
|
|
||||||
|
|
||||||
# restart
|
# shutdown web server
|
||||||
logger.log("Restarting SickRage, please stand by...")
|
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>
|
<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>
|
</div>
|
||||||
</a>
|
</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/>
|
<br/><br/>
|
||||||
|
|
||||||
#if $sickbeard.TRAKT_USE_RECOMMENDED:
|
#if $sickbeard.TRAKT_USE_RECOMMENDED:
|
||||||
<a href="$sbRoot/home/addShows/recommendedShows/" id="btnNewShow" class="btn btn-large">
|
<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="button"><img src="$sbRoot/images/add-new32.png" height="32" width="32" alt="Add Recommended Shows"/></div>
|
||||||
<div class="buttontext">
|
<div class="buttontext">
|
||||||
<h2>Add Recommended Shows</h2>
|
<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 watch list, creates a directory for its episodes, and adds it to SickRage. *** Trakt.tv must be enabled ***</p>
|
<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>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<br/><br/>
|
<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,28 +1,53 @@
|
||||||
if (sbHandleReverseProxy != "False" && sbHandleReverseProxy != 0)
|
if (sbHttpsEnabled != "False" && sbHttpsEnabled != 0) {
|
||||||
// Don't add the port to the url if using reverse proxy
|
var sb_base_url = 'https://' + sbHost + ':' + sbHttpPort + sbRoot;
|
||||||
if (sbHttpsEnabled != "False" && sbHttpsEnabled != 0)
|
} else {
|
||||||
var sb_base_url = 'https://'+sbHost+sbRoot;
|
var sb_base_url = 'http://' + sbHost + ':' + sbHttpPort + 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;
|
|
||||||
|
|
||||||
var base_url = window.location.protocol+'//'+window.location.host+sbRoot;
|
var base_url = window.location.protocol + '//' + window.location.host + sbRoot;
|
||||||
var is_alive_url = sbRoot+'/home/is_alive';
|
var is_alive_url = sbRoot + '/home/is_alive/';
|
||||||
var timeout_id;
|
var timeout_id;
|
||||||
var restarted = '';
|
var current_pid = '';
|
||||||
var num_restart_waits = 0;
|
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;
|
num_restart_waits += 1;
|
||||||
|
|
||||||
$('#shut_down_loading').hide();
|
$('#shut_down_loading').hide();
|
||||||
$('#shut_down_success').show();
|
$('#shut_down_success').show();
|
||||||
$('#restart_message').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.
|
// 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.
|
||||||
|
@ -34,16 +59,8 @@ function restartHandler() {
|
||||||
$('#restart_success').show();
|
$('#restart_success').show();
|
||||||
$('#refresh_message').show();
|
$('#refresh_message').show();
|
||||||
}, 3000);
|
}, 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
|
// if it is taking forever just give up
|
||||||
|
@ -54,34 +71,9 @@ function restartHandler() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timeout_id == 0)
|
if (timeout_id == 0) {
|
||||||
timeout_id = setTimeout('is_alive()', 1000);
|
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:
|
else:
|
||||||
self.tname = ""
|
self.tname = ""
|
||||||
dirname = os.path.dirname(self.lock_file)
|
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,
|
self.unique_name = os.path.join(dirname,
|
||||||
"%s%s.%s" % (self.hostname,
|
"%s%s.%s%s" % (self.hostname,
|
||||||
self.tname,
|
self.tname,
|
||||||
self.pid))
|
self.pid,
|
||||||
|
hash(self.path)))
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
|
|
||||||
def acquire(self, timeout=None):
|
def acquire(self, timeout=None):
|
||||||
|
|
|
@ -7,7 +7,7 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from lib import simplejson as json
|
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
|
A generic method for communicating with trakt. Uses the method and data provided along
|
||||||
with the auth info to send the command.
|
with the auth info to send the command.
|
||||||
|
@ -26,19 +26,16 @@ def TraktCall(method, api, username, password, data = {}):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# if the username isn't given then it failed
|
# if the username isn't given then it failed
|
||||||
if not username:
|
if username and password:
|
||||||
return None
|
|
||||||
|
|
||||||
password = sha1(password).hexdigest()
|
password = sha1(password).hexdigest()
|
||||||
|
data["username"] = username
|
||||||
|
data["password"] = password
|
||||||
|
|
||||||
# replace the API string with what we found
|
# replace the API string with what we found
|
||||||
method = method.replace("%API%", api)
|
method = method.replace("%API%", api)
|
||||||
|
|
||||||
data["username"] = username
|
|
||||||
data["password"] = password
|
|
||||||
|
|
||||||
# take the URL params and make a json object out of them
|
# 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
|
# request the URL from trakt and parse the result as json
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
|
|
||||||
import webbrowser
|
import webbrowser
|
||||||
import time
|
|
||||||
import datetime
|
import datetime
|
||||||
import socket
|
import socket
|
||||||
import os
|
import os
|
||||||
|
@ -29,6 +28,7 @@ from urllib2 import getproxies
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
|
|
||||||
# apparently py2exe won't build these unless they're imported somewhere
|
# apparently py2exe won't build these unless they're imported somewhere
|
||||||
|
import sys
|
||||||
from sickbeard import providers, metadata, config, webserveInit
|
from sickbeard import providers, metadata, config, webserveInit
|
||||||
from sickbeard.providers.generic import GenericProvider
|
from sickbeard.providers.generic import GenericProvider
|
||||||
from providers import ezrss, tvtorrents, btn, newznab, womble, thepiratebay, torrentleech, kat, iptorrents, \
|
from providers import ezrss, tvtorrents, btn, newznab, womble, thepiratebay, torrentleech, kat, iptorrents, \
|
||||||
|
@ -104,7 +104,7 @@ CUR_COMMIT_HASH = None
|
||||||
|
|
||||||
INIT_LOCK = Lock()
|
INIT_LOCK = Lock()
|
||||||
started = False
|
started = False
|
||||||
restarted = False
|
shutdown = False
|
||||||
|
|
||||||
ACTUAL_LOG_DIR = None
|
ACTUAL_LOG_DIR = None
|
||||||
LOG_DIR = None
|
LOG_DIR = None
|
||||||
|
@ -432,7 +432,7 @@ IGNORE_WORDS = "german,french,core2hd,dutch,swedish,reenc,MrLss"
|
||||||
CALENDAR_UNPROTECTED = False
|
CALENDAR_UNPROTECTED = False
|
||||||
|
|
||||||
TMDB_API_KEY = 'edc5f123313769de83a71e157758030b'
|
TMDB_API_KEY = 'edc5f123313769de83a71e157758030b'
|
||||||
|
TRAKT_API_KEY = 'abd806c54516240c76e4ebc9c5ccf394'
|
||||||
|
|
||||||
__INITIALIZED__ = False
|
__INITIALIZED__ = False
|
||||||
def initialize(consoleLogging=True):
|
def initialize(consoleLogging=True):
|
||||||
|
@ -1270,7 +1270,7 @@ def halt():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
__INITIALIZED__ = False
|
__INITIALIZED__ = False
|
||||||
|
started = False
|
||||||
|
|
||||||
def remove_pid_file(PIDFILE):
|
def remove_pid_file(PIDFILE):
|
||||||
try:
|
try:
|
||||||
|
@ -1286,7 +1286,7 @@ def remove_pid_file(PIDFILE):
|
||||||
def sig_handler(signum=None, frame=None):
|
def sig_handler(signum=None, frame=None):
|
||||||
if type(signum) != type(None):
|
if type(signum) != type(None):
|
||||||
logger.log(u"Signal %i caught, saving and exiting..." % int(signum))
|
logger.log(u"Signal %i caught, saving and exiting..." % int(signum))
|
||||||
webserveInit.shutdown()
|
saveAndShutdown()
|
||||||
|
|
||||||
def saveAll():
|
def saveAll():
|
||||||
global showList
|
global showList
|
||||||
|
@ -1300,9 +1300,15 @@ def saveAll():
|
||||||
logger.log(u"Saving config file to disk")
|
logger.log(u"Saving config file to disk")
|
||||||
save_config()
|
save_config()
|
||||||
|
|
||||||
def saveAndShutdown():
|
def saveAndShutdown(restart=False):
|
||||||
halt()
|
global shutdown, started
|
||||||
saveAll()
|
|
||||||
|
# flag restart/shutdown
|
||||||
|
if not restart:
|
||||||
|
shutdown = True
|
||||||
|
|
||||||
|
# proceed with shutdown
|
||||||
|
started = False
|
||||||
|
|
||||||
def invoke_command(to_call, *args, **kwargs):
|
def invoke_command(to_call, *args, **kwargs):
|
||||||
|
|
||||||
|
@ -1317,23 +1323,17 @@ def invoke_command(to_call, *args, **kwargs):
|
||||||
def invoke_restart(soft=True):
|
def invoke_restart(soft=True):
|
||||||
invoke_command(restart, soft=soft)
|
invoke_command(restart, soft=soft)
|
||||||
|
|
||||||
|
|
||||||
def invoke_shutdown():
|
def invoke_shutdown():
|
||||||
invoke_command(webserveInit.shutdown)
|
invoke_command(saveAndShutdown, False)
|
||||||
|
|
||||||
|
|
||||||
def restart(soft=True):
|
def restart(soft=True):
|
||||||
global restarted
|
|
||||||
|
|
||||||
if soft:
|
if soft:
|
||||||
halt()
|
halt()
|
||||||
saveAll()
|
saveAll()
|
||||||
logger.log(u"Re-initializing all data")
|
logger.log(u"Re-initializing all data")
|
||||||
initialize()
|
initialize()
|
||||||
else:
|
else:
|
||||||
restarted=True
|
saveAndShutdown(True)
|
||||||
time.sleep(5)
|
|
||||||
webserveInit.shutdown()
|
|
||||||
|
|
||||||
|
|
||||||
def save_config():
|
def save_config():
|
||||||
|
|
|
@ -349,6 +349,8 @@ class BTNCache(tvcache.TVCache):
|
||||||
if ci is not None:
|
if ci is not None:
|
||||||
cl.append(ci)
|
cl.append(ci)
|
||||||
|
|
||||||
|
time.sleep(.2)
|
||||||
|
|
||||||
if cl:
|
if cl:
|
||||||
myDB = self._getDB()
|
myDB = self._getDB()
|
||||||
myDB.mass_action(cl)
|
myDB.mass_action(cl)
|
||||||
|
|
|
@ -260,6 +260,8 @@ class HDBitsCache(tvcache.TVCache):
|
||||||
if ci is not None:
|
if ci is not None:
|
||||||
ql.append(ci)
|
ql.append(ci)
|
||||||
|
|
||||||
|
time.sleep(.2)
|
||||||
|
|
||||||
if ql:
|
if ql:
|
||||||
myDB = self._getDB()
|
myDB = self._getDB()
|
||||||
myDB.mass_action(ql)
|
myDB.mass_action(ql)
|
||||||
|
|
|
@ -382,6 +382,8 @@ class HDTorrentsCache(tvcache.TVCache):
|
||||||
if ci is not None:
|
if ci is not None:
|
||||||
cl.append(ci)
|
cl.append(ci)
|
||||||
|
|
||||||
|
time.sleep(.2)
|
||||||
|
|
||||||
if cl:
|
if cl:
|
||||||
myDB = self._getDB()
|
myDB = self._getDB()
|
||||||
myDB.mass_action(cl)
|
myDB.mass_action(cl)
|
||||||
|
|
|
@ -323,6 +323,8 @@ class IPTorrentsCache(tvcache.TVCache):
|
||||||
if ci is not None:
|
if ci is not None:
|
||||||
cl.append(ci)
|
cl.append(ci)
|
||||||
|
|
||||||
|
time.sleep(.2)
|
||||||
|
|
||||||
if cl:
|
if cl:
|
||||||
myDB = self._getDB()
|
myDB = self._getDB()
|
||||||
myDB.mass_action(cl)
|
myDB.mass_action(cl)
|
||||||
|
|
|
@ -460,6 +460,8 @@ class KATCache(tvcache.TVCache):
|
||||||
if ci is not None:
|
if ci is not None:
|
||||||
cl.append(ci)
|
cl.append(ci)
|
||||||
|
|
||||||
|
time.sleep(.2)
|
||||||
|
|
||||||
if cl:
|
if cl:
|
||||||
myDB = self._getDB()
|
myDB = self._getDB()
|
||||||
myDB.mass_action(cl)
|
myDB.mass_action(cl)
|
||||||
|
|
|
@ -347,6 +347,8 @@ class NewznabCache(tvcache.TVCache):
|
||||||
if ci is not None:
|
if ci is not None:
|
||||||
ql.append(ci)
|
ql.append(ci)
|
||||||
|
|
||||||
|
time.sleep(.2)
|
||||||
|
|
||||||
if ql:
|
if ql:
|
||||||
myDB = self._getDB()
|
myDB = self._getDB()
|
||||||
myDB.mass_action(ql)
|
myDB.mass_action(ql)
|
||||||
|
|
|
@ -372,6 +372,8 @@ class NextGenCache(tvcache.TVCache):
|
||||||
if ci is not None:
|
if ci is not None:
|
||||||
cl.append(ci)
|
cl.append(ci)
|
||||||
|
|
||||||
|
time.sleep(.2)
|
||||||
|
|
||||||
if cl:
|
if cl:
|
||||||
myDB = self._getDB()
|
myDB = self._getDB()
|
||||||
myDB.mass_action(cl)
|
myDB.mass_action(cl)
|
||||||
|
|
|
@ -345,6 +345,8 @@ class PublicHDCache(tvcache.TVCache):
|
||||||
if ci is not None:
|
if ci is not None:
|
||||||
ql.append(ci)
|
ql.append(ci)
|
||||||
|
|
||||||
|
time.sleep(.2)
|
||||||
|
|
||||||
if ql:
|
if ql:
|
||||||
myDB = self._getDB()
|
myDB = self._getDB()
|
||||||
myDB.mass_action(ql)
|
myDB.mass_action(ql)
|
||||||
|
|
|
@ -367,6 +367,8 @@ class SCCCache(tvcache.TVCache):
|
||||||
if ci is not None:
|
if ci is not None:
|
||||||
cl.append(ci)
|
cl.append(ci)
|
||||||
|
|
||||||
|
time.sleep(.2)
|
||||||
|
|
||||||
if cl:
|
if cl:
|
||||||
myDB = self._getDB()
|
myDB = self._getDB()
|
||||||
myDB.mass_action(cl)
|
myDB.mass_action(cl)
|
||||||
|
|
|
@ -307,6 +307,8 @@ class SpeedCDCache(tvcache.TVCache):
|
||||||
if ci is not None:
|
if ci is not None:
|
||||||
ql.append(ci)
|
ql.append(ci)
|
||||||
|
|
||||||
|
time.sleep(.2)
|
||||||
|
|
||||||
if ql:
|
if ql:
|
||||||
myDB = self._getDB()
|
myDB = self._getDB()
|
||||||
myDB.mass_action(ql)
|
myDB.mass_action(ql)
|
||||||
|
|
|
@ -440,6 +440,8 @@ class ThePirateBayCache(tvcache.TVCache):
|
||||||
if ci is not None:
|
if ci is not None:
|
||||||
cl.append(ci)
|
cl.append(ci)
|
||||||
|
|
||||||
|
time.sleep(.2)
|
||||||
|
|
||||||
if cl:
|
if cl:
|
||||||
myDB = self._getDB()
|
myDB = self._getDB()
|
||||||
myDB.mass_action(cl)
|
myDB.mass_action(cl)
|
||||||
|
|
|
@ -331,6 +331,8 @@ class TorrentDayCache(tvcache.TVCache):
|
||||||
if ci is not None:
|
if ci is not None:
|
||||||
cl.append(ci)
|
cl.append(ci)
|
||||||
|
|
||||||
|
time.sleep(.2)
|
||||||
|
|
||||||
if cl:
|
if cl:
|
||||||
myDB = self._getDB()
|
myDB = self._getDB()
|
||||||
myDB.mass_action(cl)
|
myDB.mass_action(cl)
|
||||||
|
|
|
@ -326,6 +326,8 @@ class TorrentLeechCache(tvcache.TVCache):
|
||||||
if ci is not None:
|
if ci is not None:
|
||||||
cl.append(ci)
|
cl.append(ci)
|
||||||
|
|
||||||
|
time.sleep(.2)
|
||||||
|
|
||||||
if cl:
|
if cl:
|
||||||
myDB = self._getDB()
|
myDB = self._getDB()
|
||||||
myDB.mass_action(cl)
|
myDB.mass_action(cl)
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with SickRage. If not, see <http://www.gnu.org/licenses/>.
|
# along with SickRage. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
import time
|
||||||
|
|
||||||
import sickbeard
|
import sickbeard
|
||||||
import generic
|
import generic
|
||||||
|
@ -73,6 +74,8 @@ class WombleCache(tvcache.TVCache):
|
||||||
if ci is not None:
|
if ci is not None:
|
||||||
cl.append(ci)
|
cl.append(ci)
|
||||||
|
|
||||||
|
time.sleep(.2)
|
||||||
|
|
||||||
if cl:
|
if cl:
|
||||||
myDB = self._getDB()
|
myDB = self._getDB()
|
||||||
myDB.mass_action(cl)
|
myDB.mass_action(cl)
|
||||||
|
|
|
@ -147,9 +147,9 @@ class TraktChecker():
|
||||||
"""
|
"""
|
||||||
Adds a new show with the default settings
|
Adds a new show with the default settings
|
||||||
"""
|
"""
|
||||||
showObj = helpers.findCertainShow(sickbeard.showList, int(indexerid))
|
if helpers.findCertainShow(sickbeard.showList, int(indexerid)):
|
||||||
if showObj != None:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
logger.log(u"Adding show " + str(indexerid))
|
logger.log(u"Adding show " + str(indexerid))
|
||||||
root_dirs = sickbeard.ROOT_DIRS.split('|')
|
root_dirs = sickbeard.ROOT_DIRS.split('|')
|
||||||
location = root_dirs[int(root_dirs[0]) + 1]
|
location = root_dirs[int(root_dirs[0]) + 1]
|
||||||
|
@ -161,6 +161,7 @@ class TraktChecker():
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
helpers.chmodAsParent(showPath)
|
helpers.chmodAsParent(showPath)
|
||||||
|
|
||||||
sickbeard.showQueueScheduler.action.addShow(1, int(indexerid), showPath, status,
|
sickbeard.showQueueScheduler.action.addShow(1, int(indexerid), showPath, status,
|
||||||
int(sickbeard.QUALITY_DEFAULT),
|
int(sickbeard.QUALITY_DEFAULT),
|
||||||
int(sickbeard.FLATTEN_FOLDERS_DEFAULT))
|
int(sickbeard.FLATTEN_FOLDERS_DEFAULT))
|
||||||
|
|
|
@ -128,6 +128,8 @@ class TVCache():
|
||||||
if ci is not None:
|
if ci is not None:
|
||||||
cl.append(ci)
|
cl.append(ci)
|
||||||
|
|
||||||
|
time.sleep(.2)
|
||||||
|
|
||||||
if cl:
|
if cl:
|
||||||
myDB = self._getDB()
|
myDB = self._getDB()
|
||||||
myDB.mass_action(cl)
|
myDB.mass_action(cl)
|
||||||
|
|
|
@ -48,7 +48,7 @@ from sickbeard import subtitles
|
||||||
from sickbeard import network_timezones
|
from sickbeard import network_timezones
|
||||||
|
|
||||||
from sickbeard.providers import newznab, rsstorrent
|
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 SNATCHED, UNAIRED, IGNORED, ARCHIVED, WANTED, FAILED
|
||||||
from sickbeard.common import SD, HD720p, HD1080p
|
from sickbeard.common import SD, HD720p, HD1080p
|
||||||
from sickbeard.exceptions import ex
|
from sickbeard.exceptions import ex
|
||||||
|
@ -82,6 +82,7 @@ from lib import adba
|
||||||
from Cheetah.Template import Template
|
from Cheetah.Template import Template
|
||||||
from tornado.web import RequestHandler, HTTPError
|
from tornado.web import RequestHandler, HTTPError
|
||||||
|
|
||||||
|
|
||||||
def authenticated(handler_class):
|
def authenticated(handler_class):
|
||||||
def wrap_execute(handler_execute):
|
def wrap_execute(handler_execute):
|
||||||
def basicauth(handler, transforms, *args, **kwargs):
|
def basicauth(handler, transforms, *args, **kwargs):
|
||||||
|
@ -125,6 +126,7 @@ def authenticated(handler_class):
|
||||||
handler_class._execute = wrap_execute(handler_class._execute)
|
handler_class._execute = wrap_execute(handler_class._execute)
|
||||||
return handler_class
|
return handler_class
|
||||||
|
|
||||||
|
|
||||||
class HTTPRedirect(Exception):
|
class HTTPRedirect(Exception):
|
||||||
"""Exception raised when the request should be redirected."""
|
"""Exception raised when the request should be redirected."""
|
||||||
|
|
||||||
|
@ -138,9 +140,11 @@ class HTTPRedirect(Exception):
|
||||||
"""Use this exception as a request.handler (raise self)."""
|
"""Use this exception as a request.handler (raise self)."""
|
||||||
raise self
|
raise self
|
||||||
|
|
||||||
|
|
||||||
def redirect(url, permanent=False, status=None):
|
def redirect(url, permanent=False, status=None):
|
||||||
raise HTTPRedirect(url, permanent, status)
|
raise HTTPRedirect(url, permanent, status)
|
||||||
|
|
||||||
|
|
||||||
@authenticated
|
@authenticated
|
||||||
class MainHandler(RequestHandler):
|
class MainHandler(RequestHandler):
|
||||||
def http_error_401_handler(self):
|
def http_error_401_handler(self):
|
||||||
|
@ -216,7 +220,7 @@ class MainHandler(RequestHandler):
|
||||||
def get(self, *args, **kwargs):
|
def get(self, *args, **kwargs):
|
||||||
try:
|
try:
|
||||||
self.finish(self._dispatch())
|
self.finish(self._dispatch())
|
||||||
except HTTPRedirect,inst:
|
except HTTPRedirect, inst:
|
||||||
self.redirect(inst.url, inst.permanent, inst.status)
|
self.redirect(inst.url, inst.permanent, inst.status)
|
||||||
|
|
||||||
def post(self, *args, **kwargs):
|
def post(self, *args, **kwargs):
|
||||||
|
@ -462,6 +466,7 @@ class MainHandler(RequestHandler):
|
||||||
|
|
||||||
browser = WebFileBrowser
|
browser = WebFileBrowser
|
||||||
|
|
||||||
|
|
||||||
class PageTemplate(Template):
|
class PageTemplate(Template):
|
||||||
def __init__(self, headers, *args, **KWs):
|
def __init__(self, headers, *args, **KWs):
|
||||||
KWs['file'] = os.path.join(sickbeard.PROG_DIR, "gui/" + sickbeard.GUI_NAME + "/interfaces/default/",
|
KWs['file'] = os.path.join(sickbeard.PROG_DIR, "gui/" + sickbeard.GUI_NAME + "/interfaces/default/",
|
||||||
|
@ -518,6 +523,7 @@ class IndexerWebUI(MainHandler):
|
||||||
def _munge(string):
|
def _munge(string):
|
||||||
return unicode(string).encode('utf-8', 'xmlcharrefreplace')
|
return unicode(string).encode('utf-8', 'xmlcharrefreplace')
|
||||||
|
|
||||||
|
|
||||||
def _getEpisode(show, season=None, episode=None, absolute=None):
|
def _getEpisode(show, season=None, episode=None, absolute=None):
|
||||||
if show is None:
|
if show is None:
|
||||||
return "Invalid show parameters"
|
return "Invalid show parameters"
|
||||||
|
@ -2745,10 +2751,10 @@ class NewHomeAddShows(MainHandler):
|
||||||
logger.log(u"Could not connect to trakt service, aborting recommended list update", logger.ERROR)
|
logger.log(u"Could not connect to trakt service, aborting recommended list update", logger.ERROR)
|
||||||
return
|
return
|
||||||
|
|
||||||
map(final_results.append, ([int(show['tvdb_id']), show['url'], show['title'], show['overview'],
|
map(final_results.append,
|
||||||
datetime.date.fromtimestamp(show['first_aired']).strftime('%Y%m%d')] for show in
|
([int(show['tvdb_id']), show['url'], show['title'], show['overview'],
|
||||||
recommendedlist if
|
datetime.date.fromtimestamp(int(show['first_aired']) / 1000.0).strftime('%Y%m%d')] for show in
|
||||||
not helpers.findCertainShow(sickbeard.showList, indexerid=int(show['tvdb_id']))))
|
recommendedlist if not helpers.findCertainShow(sickbeard.showList, indexerid=int(show['tvdb_id']))))
|
||||||
|
|
||||||
return json.dumps({'results': final_results})
|
return json.dumps({'results': final_results})
|
||||||
|
|
||||||
|
@ -2769,6 +2775,18 @@ class NewHomeAddShows(MainHandler):
|
||||||
anyQualities, bestQualities, flatten_folders, subtitles, fullShowPath, other_shows,
|
anyQualities, bestQualities, flatten_folders, subtitles, fullShowPath, other_shows,
|
||||||
skipShow, providedIndexer, anime, scene)
|
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):
|
def existingShows(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Prints out the page to add existing shows from a root dir
|
Prints out the page to add existing shows from a root dir
|
||||||
|
@ -2778,6 +2796,33 @@ class NewHomeAddShows(MainHandler):
|
||||||
|
|
||||||
return _munge(t)
|
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,
|
def addNewShow(self, whichSeries=None, indexerLang="en", rootDir=None, defaultStatus=None,
|
||||||
anyQualities=None, bestQualities=None, flatten_folders=None, subtitles=None,
|
anyQualities=None, bestQualities=None, flatten_folders=None, subtitles=None,
|
||||||
|
@ -3054,9 +3099,9 @@ class Home(MainHandler):
|
||||||
|
|
||||||
if sickbeard.started:
|
if sickbeard.started:
|
||||||
return callback + '(' + json.dumps(
|
return callback + '(' + json.dumps(
|
||||||
{"msg": str(sickbeard.PID), "restarted": str(sickbeard.restarted)}) + ');'
|
{"msg": str(sickbeard.PID)}) + ');'
|
||||||
else:
|
else:
|
||||||
return callback + '(' + json.dumps({"msg": "nope", "restarted": str(sickbeard.restarted)}) + ');'
|
return callback + '(' + json.dumps({"msg": "nope"}) + ');'
|
||||||
|
|
||||||
|
|
||||||
def index(self, *args, **kwargs):
|
def index(self, *args, **kwargs):
|
||||||
|
@ -3379,7 +3424,6 @@ class Home(MainHandler):
|
||||||
|
|
||||||
return _munge(t)
|
return _munge(t)
|
||||||
|
|
||||||
|
|
||||||
def update(self, pid=None):
|
def update(self, pid=None):
|
||||||
|
|
||||||
if str(pid) != str(sickbeard.PID):
|
if str(pid) != str(sickbeard.PID):
|
||||||
|
@ -3999,7 +4043,6 @@ class Home(MainHandler):
|
||||||
myDB = db.DBConnection()
|
myDB = db.DBConnection()
|
||||||
myDB.mass_action(sql_l)
|
myDB.mass_action(sql_l)
|
||||||
|
|
||||||
|
|
||||||
if int(status) == WANTED:
|
if int(status) == WANTED:
|
||||||
msg = "Backlog was automatically started for the following seasons of <b>" + showObj.name + "</b>:<br />"
|
msg = "Backlog was automatically started for the following seasons of <b>" + showObj.name + "</b>:<br />"
|
||||||
for season in segment:
|
for season in segment:
|
||||||
|
@ -4294,9 +4337,9 @@ class Home(MainHandler):
|
||||||
|
|
||||||
return json.dumps({'result': 'failure'})
|
return json.dumps({'result': 'failure'})
|
||||||
|
|
||||||
|
|
||||||
class UI(MainHandler):
|
class UI(MainHandler):
|
||||||
def add_message(self):
|
def add_message(self):
|
||||||
|
|
||||||
ui.notifications.message('Test 1', 'This is test number 1')
|
ui.notifications.message('Test 1', 'This is test number 1')
|
||||||
ui.notifications.error('Test 2', 'This is test number 2')
|
ui.notifications.error('Test 2', 'This is test number 2')
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
import os
|
import os
|
||||||
import traceback
|
import socket
|
||||||
|
import time
|
||||||
|
import threading
|
||||||
|
import sys
|
||||||
import sickbeard
|
import sickbeard
|
||||||
import webserve
|
import webserve
|
||||||
import webapi
|
import webapi
|
||||||
|
@ -10,9 +13,6 @@ from tornado.web import Application, StaticFileHandler, RedirectHandler, HTTPErr
|
||||||
from tornado.httpserver import HTTPServer
|
from tornado.httpserver import HTTPServer
|
||||||
from tornado.ioloop import IOLoop
|
from tornado.ioloop import IOLoop
|
||||||
|
|
||||||
server = None
|
|
||||||
|
|
||||||
|
|
||||||
class MultiStaticFileHandler(StaticFileHandler):
|
class MultiStaticFileHandler(StaticFileHandler):
|
||||||
def initialize(self, paths, default_filename=None):
|
def initialize(self, paths, default_filename=None):
|
||||||
self.paths = paths
|
self.paths = paths
|
||||||
|
@ -34,37 +34,44 @@ class MultiStaticFileHandler(StaticFileHandler):
|
||||||
# Oops file not found anywhere!
|
# Oops file not found anywhere!
|
||||||
raise HTTPError(404)
|
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={}):
|
self.options = options
|
||||||
options.setdefault('port', 8081)
|
self.options.setdefault('port', 8081)
|
||||||
options.setdefault('host', '0.0.0.0')
|
self.options.setdefault('host', '0.0.0.0')
|
||||||
options.setdefault('log_dir', None)
|
self.options.setdefault('log_dir', None)
|
||||||
options.setdefault('username', '')
|
self.options.setdefault('username', '')
|
||||||
options.setdefault('password', '')
|
self.options.setdefault('password', '')
|
||||||
options.setdefault('web_root', '/')
|
self.options.setdefault('web_root', '/')
|
||||||
assert isinstance(options['port'], int)
|
assert isinstance(self.options['port'], int)
|
||||||
assert 'data_root' in options
|
assert 'data_root' in self.options
|
||||||
|
|
||||||
# tornado setup
|
# tornado setup
|
||||||
enable_https = options['enable_https']
|
self.enable_https = self.options['enable_https']
|
||||||
https_cert = options['https_cert']
|
self.https_cert = self.options['https_cert']
|
||||||
https_key = options['https_key']
|
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 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 (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(https_cert, https_key):
|
if not create_https_certificates(self.https_cert, self.https_key):
|
||||||
logger.log(u"Unable to create CERT/KEY files, disabling HTTPS")
|
logger.log(u"Unable to create CERT/KEY files, disabling HTTPS")
|
||||||
sickbeard.ENABLE_HTTPS = False
|
sickbeard.ENABLE_HTTPS = False
|
||||||
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)
|
logger.log(u"Disabled HTTPS because of missing CERT and KEY files", logger.WARNING)
|
||||||
sickbeard.ENABLE_HTTPS = False
|
sickbeard.ENABLE_HTTPS = False
|
||||||
enable_https = False
|
enable_https = False
|
||||||
|
|
||||||
# Load the app
|
# Load the app
|
||||||
app = Application([],
|
self.app = Application([],
|
||||||
debug=False,
|
debug=False,
|
||||||
gzip=True,
|
gzip=True,
|
||||||
xheaders=sickbeard.HANDLE_REVERSE_PROXY,
|
xheaders=sickbeard.HANDLE_REVERSE_PROXY,
|
||||||
|
@ -72,49 +79,61 @@ def initWebServer(options={}):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Main Handler
|
# Main Handler
|
||||||
app.add_handlers(".*$", [
|
self.app.add_handlers(".*$", [
|
||||||
(r"%s" % options['web_root'], RedirectHandler, {'url': '%s/home/' % options['web_root']}),
|
(r"%s" % self.options['web_root'], RedirectHandler, {'url': '%s/home/' % self.options['web_root']}),
|
||||||
(r'%s/api/(.*)(/?)' % options['web_root'], webapi.Api),
|
(r'%s/api/(.*)(/?)' % self.options['web_root'], webapi.Api),
|
||||||
(r'%s/(.*)(/?)' % options['web_root'], webserve.MainHandler)
|
(r'%s/(.*)(/?)' % self.options['web_root'], webserve.MainHandler)
|
||||||
])
|
])
|
||||||
|
|
||||||
# Static Path Handler
|
# Static Path Handler
|
||||||
app.add_handlers(".*$", [
|
self.app.add_handlers(".*$", [
|
||||||
(r'%s/(favicon\.ico)' % options['web_root'], MultiStaticFileHandler,
|
(r'%s/(favicon\.ico)' % self.options['web_root'], MultiStaticFileHandler,
|
||||||
{'paths': [os.path.join(options['data_root'], 'images/ico/favicon.ico')]}),
|
{'paths': [os.path.join(self.options['data_root'], 'images/ico/favicon.ico')]}),
|
||||||
(r'%s/%s/(.*)(/?)' % (options['web_root'], 'images'), MultiStaticFileHandler,
|
(r'%s/%s/(.*)(/?)' % (self.options['web_root'], 'images'), MultiStaticFileHandler,
|
||||||
{'paths': [os.path.join(options['data_root'], 'images'),
|
{'paths': [os.path.join(self.options['data_root'], 'images'),
|
||||||
os.path.join(sickbeard.CACHE_DIR, 'images')]}),
|
os.path.join(sickbeard.CACHE_DIR, 'images')]}),
|
||||||
(r'%s/%s/(.*)(/?)' % (options['web_root'], 'css'), MultiStaticFileHandler,
|
(r'%s/%s/(.*)(/?)' % (self.options['web_root'], 'css'), MultiStaticFileHandler,
|
||||||
{'paths': [os.path.join(options['data_root'], 'css')]}),
|
{'paths': [os.path.join(self.options['data_root'], 'css')]}),
|
||||||
(r'%s/%s/(.*)(/?)' % (options['web_root'], 'js'), MultiStaticFileHandler,
|
(r'%s/%s/(.*)(/?)' % (self.options['web_root'], 'js'), MultiStaticFileHandler,
|
||||||
{'paths': [os.path.join(options['data_root'], 'js')]})
|
{'paths': [os.path.join(self.options['data_root'], 'js')]})
|
||||||
|
|
||||||
])
|
])
|
||||||
|
|
||||||
global server
|
def run(self):
|
||||||
|
if self.enable_https:
|
||||||
if enable_https:
|
|
||||||
protocol = "https"
|
protocol = "https"
|
||||||
server = HTTPServer(app, no_keep_alive=True,
|
self.server = HTTPServer(self.app, no_keep_alive=True,
|
||||||
ssl_options={"certfile": https_cert, "keyfile": https_key})
|
ssl_options={"certfile": self.https_cert, "keyfile": self.https_key})
|
||||||
else:
|
else:
|
||||||
protocol = "http"
|
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(
|
logger.log(u"Starting SickRage on " + protocol + "://" + str(self.options['host']) + ":" + str(
|
||||||
options['port']) + "/")
|
self.options['port']) + "/")
|
||||||
|
|
||||||
if not sickbeard.restarted:
|
|
||||||
server.listen(options['port'], options['host'])
|
|
||||||
|
|
||||||
def shutdown():
|
|
||||||
global server
|
|
||||||
|
|
||||||
logger.log('Shutting down tornado io loop')
|
|
||||||
try:
|
try:
|
||||||
IOLoop.current().stop()
|
self.server.listen(self.options['port'], self.options['host'])
|
||||||
except RuntimeError:
|
|
||||||
pass
|
|
||||||
except:
|
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 a new issue