Merge branch 'dev' of https://github.com/echel0n/SickRage into pushover-custom_api_key

This commit is contained in:
Mark Rawson 2014-06-18 21:38:44 +01:00
commit 03c9b303e0
9 changed files with 54297 additions and 23395 deletions

View file

@ -18,6 +18,8 @@
# along with SickRage. If not, see <http://www.gnu.org/licenses/>. # along with SickRage. If not, see <http://www.gnu.org/licenses/>.
# 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
import sys import sys
if sys.version_info < (2, 6): if sys.version_info < (2, 6):
@ -89,41 +91,33 @@ def loadShowsFromDB():
sickbeard.showList.append(curShow) sickbeard.showList.append(curShow)
except Exception, e: except Exception, e:
logger.log( logger.log(
u"There was an error creating the show in " + sqlShow["location"] + ": " + str(e).decode('utf-8'), u"There was an error creating the show in " + sqlShow["location"] + ": " + str(e).decode('utf-8'),
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 # TODO: update the existing shows if the showlist has something in it
def daemonize(): def daemonize():
"""
Fork off as a daemon
"""
# pylint: disable=E1101
# Make a non-session-leader child process
try: try:
pid = os.fork() # @UndefinedVariable - only available in UNIX pid = os.fork()
if pid != 0: if pid > 0:
os._exit(0) sys.exit(0)
except OSError, e: except OSError:
sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror)) print "fork() failed"
sys.exit(1) sys.exit(1)
os.setsid() # unix os.chdir(sickbeard.PROG_DIR)
os.setsid()
# Make sure I can read my own files and shut out others # Make sure I can read my own files and shut out others
prev = os.umask(0) prev= os.umask(0)
os.umask(prev and int('077', 8)) os.umask(prev and int('077',8))
# Make the child a session-leader by detaching from the terminal
try: try:
pid = os.fork() # @UndefinedVariable - only available in UNIX pid = os.fork()
if pid != 0: if pid > 0:
os._exit(0) sys.exit(0)
except OSError, e: except OSError:
sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror)) print "fork() failed"
sys.exit(1) sys.exit(1)
# Write pid # Write pid
@ -137,25 +131,14 @@ def daemonize():
u"Unable to write PID file: " + sickbeard.PIDFILE + " Error: " + str(e.strerror) + " [" + str( u"Unable to write PID file: " + sickbeard.PIDFILE + " Error: " + str(e.strerror) + " [" + str(
e.errno) + "]") e.errno) + "]")
# Redirect all output dev_null = file('/dev/null', 'r')
sys.stdout.flush() os.dup2(dev_null.fileno(), sys.stdin.fileno())
sys.stderr.flush()
devnull = getattr(os, 'devnull', '/dev/null')
stdin = file(devnull, 'r')
stdout = file(devnull, 'a+')
stderr = file(devnull, 'a+')
os.dup2(stdin.fileno(), sys.stdin.fileno())
os.dup2(stdout.fileno(), sys.stdout.fileno())
os.dup2(stderr.fileno(), sys.stderr.fileno())
def main(): def main():
""" """
TV for me TV for me
""" """
io_loop = IOLoop.current()
# 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)
@ -325,12 +308,6 @@ def main():
sickbeard.showList = [] sickbeard.showList = []
if sickbeard.DAEMON:
daemonize()
# Use this PID for everything
sickbeard.PID = os.getpid()
if forcedPort: if forcedPort:
logger.log(u"Forcing web server to port " + str(forcedPort)) logger.log(u"Forcing web server to port " + str(forcedPort))
startPort = forcedPort startPort = forcedPort
@ -385,6 +362,9 @@ def main():
if forceUpdate or sickbeard.UPDATE_SHOWS_ON_START: if forceUpdate or sickbeard.UPDATE_SHOWS_ON_START:
sickbeard.showUpdateScheduler.action.run(force=True) # @UndefinedVariable sickbeard.showUpdateScheduler.action.run(force=True) # @UndefinedVariable
# get ioloop
io_loop = IOLoop.current()
# init startup tasks # init startup tasks
io_loop.add_timeout(datetime.timedelta(seconds=5), startup) io_loop.add_timeout(datetime.timedelta(seconds=5), startup)
@ -393,6 +373,12 @@ def main():
if sickbeard.AUTO_UPDATE: if sickbeard.AUTO_UPDATE:
tornado.autoreload.start(io_loop) tornado.autoreload.start(io_loop)
if sickbeard.DAEMON:
daemonize()
# Use this PID for everything
sickbeard.PID = os.getpid()
# start IOLoop. # start IOLoop.
io_loop.start() io_loop.start()
sickbeard.saveAndShutdown() sickbeard.saveAndShutdown()

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -97,18 +97,8 @@ def change_LOG_DIR(log_dir, web_log):
return False return False
if sickbeard.WEB_LOG != web_log_value or log_dir_changed == True: if sickbeard.WEB_LOG != web_log_value or log_dir_changed == True:
sickbeard.WEB_LOG = web_log_value sickbeard.WEB_LOG = web_log_value
if sickbeard.WEB_LOG:
cherry_log = os.path.join(sickbeard.LOG_DIR, "cherrypy.log")
logger.log(u"Change cherry log file to " + cherry_log)
else:
cherry_log = None
logger.log(u"Disable cherry logging")
#cherrypy.config.update({'log.access_file': cherry_log})
return True return True

View file

@ -76,11 +76,13 @@ class SBRotatingLogHandler(object):
sub_logger = logging.getLogger('subliminal') sub_logger = logging.getLogger('subliminal')
imdb_logger = logging.getLogger('imdbpy') imdb_logger = logging.getLogger('imdbpy')
tornado_logger = logging.getLogger('tornado') tornado_logger = logging.getLogger('tornado')
feedcache_logger = logging.getLogger('feedcache')
sb_logger.removeHandler(handler) sb_logger.removeHandler(handler)
sub_logger.removeHandler(handler) sub_logger.removeHandler(handler)
imdb_logger.removeHandler(handler) imdb_logger.removeHandler(handler)
tornado_logger.removeHandler(handler) tornado_logger.removeHandler(handler)
feedcache_logger.removeHandler(handler)
handler.flush() handler.flush()
handler.close() handler.close()
@ -116,7 +118,9 @@ class SBRotatingLogHandler(object):
'%H:%M:%S'), '%H:%M:%S'),
'imdbpy': logging.Formatter('%(asctime)s %(levelname)s::IMDBPY :: %(message)s', '%H:%M:%S'), 'imdbpy': logging.Formatter('%(asctime)s %(levelname)s::IMDBPY :: %(message)s', '%H:%M:%S'),
'tornado.general': logging.Formatter('%(asctime)s %(levelname)s::TORNADO :: %(message)s', '%H:%M:%S'), 'tornado.general': logging.Formatter('%(asctime)s %(levelname)s::TORNADO :: %(message)s', '%H:%M:%S'),
'tornado.application': logging.Formatter('%(asctime)s %(levelname)s::TORNADO :: %(message)s', '%H:%M:%S') 'tornado.application': logging.Formatter('%(asctime)s %(levelname)s::TORNADO :: %(message)s', '%H:%M:%S'),
'feedcache.cache': logging.Formatter('%(asctime)s %(levelname)s::FEEDCACHE :: %(message)s',
'%H:%M:%S')
}, },
logging.Formatter('%(message)s'), )) logging.Formatter('%(message)s'), ))
@ -126,15 +130,18 @@ class SBRotatingLogHandler(object):
logging.getLogger('tornado.application').addHandler(console) logging.getLogger('tornado.application').addHandler(console)
logging.getLogger('subliminal').addHandler(console) logging.getLogger('subliminal').addHandler(console)
logging.getLogger('imdbpy').addHandler(console) logging.getLogger('imdbpy').addHandler(console)
logging.getLogger('feedcache').addHandler(console)
self.log_file_path = os.path.join(sickbeard.LOG_DIR, self.log_file) self.log_file_path = os.path.join(sickbeard.LOG_DIR, self.log_file)
self.cur_handler = self._config_handler() self.cur_handler = self._config_handler()
logging.getLogger('sickbeard').addHandler(self.cur_handler) logging.getLogger('sickbeard').addHandler(self.cur_handler)
logging.getLogger('tornado.access').addHandler(NullHandler())
logging.getLogger('tornado.general').addHandler(self.cur_handler) logging.getLogger('tornado.general').addHandler(self.cur_handler)
logging.getLogger('tornado.application').addHandler(self.cur_handler) logging.getLogger('tornado.application').addHandler(self.cur_handler)
logging.getLogger('subliminal').addHandler(self.cur_handler) logging.getLogger('subliminal').addHandler(self.cur_handler)
logging.getLogger('imdbpy').addHandler(self.cur_handler) logging.getLogger('imdbpy').addHandler(self.cur_handler)
logging.getLogger('feedcache').addHandler(self.cur_handler)
logging.getLogger('sickbeard').setLevel(DB) logging.getLogger('sickbeard').setLevel(DB)
@ -146,20 +153,20 @@ class SBRotatingLogHandler(object):
logging.getLogger('tornado.application').setLevel(log_level) logging.getLogger('tornado.application').setLevel(log_level)
logging.getLogger('subliminal').setLevel(log_level) logging.getLogger('subliminal').setLevel(log_level)
logging.getLogger('imdbpy').setLevel(log_level) logging.getLogger('imdbpy').setLevel(log_level)
logging.getLogger('feedcache').setLevel(log_level)
logging.getLogger('tornado.access').addHandler(NullHandler())
# already logging in new log folder, close the old handler # already logging in new log folder, close the old handler
if old_handler: if old_handler:
self.close_log(old_handler) self.close_log(old_handler)
# old_handler.flush() # old_handler.flush()
# old_handler.close() # old_handler.close()
# sb_logger = logging.getLogger('sickbeard') # sb_logger = logging.getLogger('sickbeard')
# sub_logger = logging.getLogger('subliminal') # sub_logger = logging.getLogger('subliminal')
# imdb_logger = logging.getLogger('imdbpy') # imdb_logger = logging.getLogger('imdbpy')
# sb_logger.removeHandler(old_handler) # sb_logger.removeHandler(old_handler)
# subli_logger.removeHandler(old_handler) # subli_logger.removeHandler(old_handler)
# imdb_logger.removeHandler(old_handler) # imdb_logger.removeHandler(old_handler)
def _config_handler(self): def _config_handler(self):
""" """
@ -174,7 +181,9 @@ class SBRotatingLogHandler(object):
'%Y-%m-%d %H:%M:%S'), '%Y-%m-%d %H:%M:%S'),
'imdbpy': logging.Formatter('%(asctime)s %(levelname)-8s IMDBPY :: %(message)s', '%Y-%m-%d %H:%M:%S'), 'imdbpy': logging.Formatter('%(asctime)s %(levelname)-8s IMDBPY :: %(message)s', '%Y-%m-%d %H:%M:%S'),
'tornado.general': logging.Formatter('%(asctime)s %(levelname)-8s TORNADO :: %(message)s', '%Y-%m-%d %H:%M:%S'), 'tornado.general': logging.Formatter('%(asctime)s %(levelname)-8s TORNADO :: %(message)s', '%Y-%m-%d %H:%M:%S'),
'tornado.application': logging.Formatter('%(asctime)s %(levelname)-8s TORNADO :: %(message)s', '%Y-%m-%d %H:%M:%S') 'tornado.application': logging.Formatter('%(asctime)s %(levelname)-8s TORNADO :: %(message)s', '%Y-%m-%d %H:%M:%S'),
'feedcache.cache': logging.Formatter('%(asctime)s %(levelname)-8s FEEDCACHE :: %(message)s',
'%Y-%m-%d %H:%M:%S')
}, },
logging.Formatter('%(message)s'), )) logging.Formatter('%(message)s'), ))
@ -208,6 +217,7 @@ class SBRotatingLogHandler(object):
sub_logger = logging.getLogger('subliminal') sub_logger = logging.getLogger('subliminal')
imdb_logger = logging.getLogger('imdbpy') imdb_logger = logging.getLogger('imdbpy')
tornado_logger = logging.getLogger('tornado') tornado_logger = logging.getLogger('tornado')
feedcache_logger = logging.getLogger('feedcache')
# delete the old handler # delete the old handler
if self.cur_handler: if self.cur_handler:
@ -233,6 +243,7 @@ class SBRotatingLogHandler(object):
sub_logger.addHandler(new_file_handler) sub_logger.addHandler(new_file_handler)
imdb_logger.addHandler(new_file_handler) imdb_logger.addHandler(new_file_handler)
tornado_logger.addHandler(new_file_handler) tornado_logger.addHandler(new_file_handler)
feedcache_logger.addHandler(new_file_handler)
def log(self, toLog, logLevel=MESSAGE): def log(self, toLog, logLevel=MESSAGE):
@ -257,6 +268,7 @@ class SBRotatingLogHandler(object):
sub_logger = logging.getLogger('subliminal') sub_logger = logging.getLogger('subliminal')
imdb_logger = logging.getLogger('imdbpy') imdb_logger = logging.getLogger('imdbpy')
tornado_logger = logging.getLogger('tornado') tornado_logger = logging.getLogger('tornado')
feedcache_logger = logging.getLogger('feedcache')
try: try:
if logLevel == DEBUG: if logLevel == DEBUG:

View file

@ -160,7 +160,6 @@ class Api(webserve.IndexHandler):
return webserve._munge(t) return webserve._munge(t)
def _out_as_json(self, dict): def _out_as_json(self, dict):
""" set cherrypy response to json """
self.set_header("Content-Type", "application/json") self.set_header("Content-Type", "application/json")
try: try:
out = json.dumps(dict, indent=self.intent, sort_keys=True) out = json.dumps(dict, indent=self.intent, sort_keys=True)
@ -297,10 +296,11 @@ def filter_params(cmd, args, kwargs):
return curArgs, curKwargs return curArgs, curKwargs
class ApiCall(Api): class ApiCall(webserve.IndexHandler):
_help = {"desc": "No help message available. Please tell the devs that a help msg is missing for this cmd"} _help = {"desc": "No help message available. Please tell the devs that a help msg is missing for this cmd"}
def __init__(self, args, kwargs): def __init__(self, args, kwargs):
# missing # missing
try: try:
if self._missing: if self._missing:
@ -2104,7 +2104,7 @@ class CMD_ShowGetPoster(ApiCall):
def run(self): def run(self):
""" get the poster for a show in sickbeard """ """ get the poster for a show in sickbeard """
return {'outputType': 'image', 'image': webserve.IndexHandler.showPoster(self.indexerid, 'poster')} return {'outputType': 'image', 'image': self.showPoster(self.indexerid, 'poster')}
class CMD_ShowGetBanner(ApiCall): class CMD_ShowGetBanner(ApiCall):
@ -2122,7 +2122,7 @@ class CMD_ShowGetBanner(ApiCall):
def run(self): def run(self):
""" get the banner for a show in sickbeard """ """ get the banner for a show in sickbeard """
return {'outputType': 'image', 'image': webserve.IndexHandler.showPoster(self.indexerid, 'banner')} return {'outputType': 'image', 'image': self.showPoster(self.indexerid, 'banner')}
class CMD_ShowPause(ApiCall): class CMD_ShowPause(ApiCall):

View file

@ -80,26 +80,16 @@ except ImportError:
from lib import adba from lib import adba
from Cheetah.Template import Template from Cheetah.Template import Template
from tornado import gen, autoreload from tornado import gen
from tornado.web import RequestHandler, HTTPError, asynchronous from tornado.web import RequestHandler, HTTPError, asynchronous
from tornado.ioloop import IOLoop
# def _handle_reverse_proxy():
# if sickbeard.HANDLE_REVERSE_PROXY:
# cherrypy.lib.cptools.proxy()
# cherrypy.tools.handle_reverse_proxy = cherrypy.Tool('before_handler', _handle_reverse_proxy)
req_headers = None req_headers = None
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):
def _request_basic_auth(handler): def _request_basic_auth(handler):
handler.set_status(401) handler.set_status(401)
handler.set_header('WWW-Authenticate', 'Basic realm=Restricted') handler.set_header('WWW-Authenticate', 'Basic realm="SickRage"')
handler._transforms = [] handler._transforms = []
handler.finish() handler.finish()
return False return False
@ -107,6 +97,9 @@ def authenticated(handler_class):
try: try:
if not (sickbeard.WEB_USERNAME and sickbeard.WEB_PASSWORD): if not (sickbeard.WEB_USERNAME and sickbeard.WEB_PASSWORD):
return True return True
elif handler.request.uri.startswith('/calendar') or (
handler.request.uri.startswith('/api') and '/api/builder' not in handler.request.uri):
return True
auth_hdr = handler.request.headers.get('Authorization') auth_hdr = handler.request.headers.get('Authorization')
@ -135,13 +128,8 @@ def authenticated(handler_class):
return handler_class return handler_class
class RedirectHandler(RequestHandler):
def get(self, path, **kwargs):
self.redirect(path, permanent=True)
@authenticated @authenticated
class IndexHandler(RedirectHandler): class IndexHandler(RequestHandler):
def __init__(self, application, request, **kwargs): def __init__(self, application, request, **kwargs):
super(IndexHandler, self).__init__(application, request, **kwargs) super(IndexHandler, self).__init__(application, request, **kwargs)
global req_headers global req_headers
@ -149,22 +137,35 @@ class IndexHandler(RedirectHandler):
sickbeard.REMOTE_IP = self.request.remote_ip sickbeard.REMOTE_IP = self.request.remote_ip
req_headers = self.request.headers req_headers = self.request.headers
def delist_arguments(self, args): def http_error_401_handler(self):
""" """ Custom handler for 401 error """
Takes a dictionary, 'args' and de-lists any single-item lists then return r'''<!DOCTYPE html>
returns the resulting dictionary. <html>
<head>
<title>%s</title>
</head>
<body>
<br/>
<font color="#0000FF">Error %s: You need to provide a valid username and password.</font>
</body>
</html>
''' % ('Access denied', 401)
In other words, {'foo': ['bar']} would become {'foo': 'bar'} def http_error_404_handler(self):
""" """ Custom handler for 404 error, redirect back to main page """
for arg, value in args.items(): self.redirect('/home/')
if len(value) == 1:
args[arg] = value[0] def write_error(self, status_code, **kwargs):
return args if status_code == 404:
self.redirect('/home/')
elif status_code == 401:
self.finish(self.http_error_401_handler())
else:
super(IndexHandler, self).write_error(status_code, **kwargs)
def _dispatch(self): def _dispatch(self):
args = None path = self.request.uri.replace(sickbeard.WEB_ROOT, '').split('?')[0]
path = self.request.uri.split('?')[0]
method = path.strip('/').split('/')[-1] method = path.strip('/').split('/')[-1]
if path.startswith('/api'): if path.startswith('/api'):
@ -185,8 +186,10 @@ class IndexHandler(RedirectHandler):
if klass and not method.startswith('_'): if klass and not method.startswith('_'):
# Sanitize argument lists: # Sanitize argument lists:
if self.request.arguments: args = self.request.arguments
args = self.delist_arguments(self.request.arguments) for arg, value in args.items():
if len(value) == 1:
args[arg] = value[0]
# Regular method handler for classes # Regular method handler for classes
func = getattr(klass, method, None) func = getattr(klass, method, None)
@ -199,30 +202,29 @@ class IndexHandler(RedirectHandler):
func = getattr(klass, 'index', None) func = getattr(klass, 'index', None)
if func: if func:
if args: return func(**args)
return func(**args)
else:
return func()
raise HTTPError(404) raise HTTPError(404)
def redirect(self, url, permanent=False, status=None):
self._transforms = []
super(IndexHandler, self).redirect(sickbeard.WEB_ROOT + url, permanent, status)
@asynchronous @asynchronous
@gen.engine
def get(self, *args, **kwargs): def get(self, *args, **kwargs):
try: response = yield gen.Task(self.getresponse, self._dispatch)
resp = self._dispatch() self.finish(response)
if resp:
self.finish(resp)
except Exception as e:
logger.log(ex(e), logger.ERROR)
logger.log(u"Traceback: " + traceback.format_exc(), logger.DEBUG)
@asynchronous @asynchronous
@gen.engine
def post(self, *args, **kwargs): def post(self, *args, **kwargs):
try: response = yield gen.Task(self.getresponse, self._dispatch)
self.finish(self._dispatch()) self.finish(response)
except Exception as e:
logger.log(ex(e), logger.ERROR) def getresponse(self, func, callback):
logger.log(u"Traceback: " + traceback.format_exc(), logger.DEBUG) response = func()
callback(response)
def robots_txt(self, *args, **kwargs): def robots_txt(self, *args, **kwargs):
""" Keep web crawlers out """ """ Keep web crawlers out """
@ -264,7 +266,7 @@ class IndexHandler(RedirectHandler):
sickbeard.HOME_LAYOUT = layout sickbeard.HOME_LAYOUT = layout
self.redirect("/home/") return self.redirect("/home/")
def setHistoryLayout(self, layout): def setHistoryLayout(self, layout):
@ -273,13 +275,13 @@ class IndexHandler(RedirectHandler):
sickbeard.HISTORY_LAYOUT = layout sickbeard.HISTORY_LAYOUT = layout
self.redirect("/history/") return self.redirect("/history/")
def toggleDisplayShowSpecials(self, show): def toggleDisplayShowSpecials(self, show):
sickbeard.DISPLAY_SHOW_SPECIALS = not sickbeard.DISPLAY_SHOW_SPECIALS sickbeard.DISPLAY_SHOW_SPECIALS = not sickbeard.DISPLAY_SHOW_SPECIALS
self.redirect("/home/displayShow?show=" + show) return self.redirect("/home/displayShow?show=" + show)
def setComingEpsLayout(self, layout): def setComingEpsLayout(self, layout):
if layout not in ('poster', 'banner', 'list'): if layout not in ('poster', 'banner', 'list'):
@ -287,13 +289,13 @@ class IndexHandler(RedirectHandler):
sickbeard.COMING_EPS_LAYOUT = layout sickbeard.COMING_EPS_LAYOUT = layout
self.redirect("/comingEpisodes/") return self.redirect("/comingEpisodes/")
def toggleComingEpsDisplayPaused(self, *args, **kwargs): def toggleComingEpsDisplayPaused(self, *args, **kwargs):
sickbeard.COMING_EPS_DISPLAY_PAUSED = not sickbeard.COMING_EPS_DISPLAY_PAUSED sickbeard.COMING_EPS_DISPLAY_PAUSED = not sickbeard.COMING_EPS_DISPLAY_PAUSED
self.redirect("/comingEpisodes/") return self.redirect("/comingEpisodes/")
def setComingEpsSort(self, sort): def setComingEpsSort(self, sort):
if sort not in ('date', 'network', 'show'): if sort not in ('date', 'network', 'show'):
@ -301,7 +303,7 @@ class IndexHandler(RedirectHandler):
sickbeard.COMING_EPS_SORT = sort sickbeard.COMING_EPS_SORT = sort
self.redirect("/comingEpisodes/") return self.redirect("/comingEpisodes/")
def comingEpisodes(self, layout="None"): def comingEpisodes(self, layout="None"):
@ -454,7 +456,6 @@ class IndexHandler(RedirectHandler):
browser = WebFileBrowser browser = WebFileBrowser
class PageTemplate(Template): class PageTemplate(Template):
def __init__(self, *args, **KWs): def __init__(self, *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/",
@ -506,7 +507,7 @@ class IndexerWebUI(IndexHandler):
showDirList = "" showDirList = ""
for curShowDir in self.config['_showDir']: for curShowDir in self.config['_showDir']:
showDirList += "showDir=" + curShowDir + "&" showDirList += "showDir=" + curShowDir + "&"
self.redirect("/home/addShows/addShow?" + showDirList + "seriesList=" + searchList) return self.redirect("/home/addShows/addShow?" + showDirList + "seriesList=" + searchList)
def _munge(string): def _munge(string):
@ -585,7 +586,7 @@ class ManageSearches(IndexHandler):
logger.log(u"Backlog search forced") logger.log(u"Backlog search forced")
ui.notifications.message('Backlog search started') ui.notifications.message('Backlog search started')
self.redirect("/manage/manageSearches/") return self.redirect("/manage/manageSearches/")
def forceSearch(self, *args, **kwargs): def forceSearch(self, *args, **kwargs):
@ -596,7 +597,7 @@ class ManageSearches(IndexHandler):
logger.log(u"Daily search forced") logger.log(u"Daily search forced")
ui.notifications.message('Daily search started') ui.notifications.message('Daily search started')
self.redirect("/manage/manageSearches/") return self.redirect("/manage/manageSearches/")
def forceFindPropers(self, *args, **kwargs): def forceFindPropers(self, *args, **kwargs):
@ -607,7 +608,7 @@ class ManageSearches(IndexHandler):
logger.log(u"Find propers search forced") logger.log(u"Find propers search forced")
ui.notifications.message('Find propers search started') ui.notifications.message('Find propers search started')
self.redirect("/manage/manageSearches/") return self.redirect("/manage/manageSearches/")
def pauseBacklog(self, paused=None): def pauseBacklog(self, paused=None):
@ -616,7 +617,7 @@ class ManageSearches(IndexHandler):
else: else:
sickbeard.searchQueueScheduler.action.unpause_backlog() # @UndefinedVariable sickbeard.searchQueueScheduler.action.unpause_backlog() # @UndefinedVariable
self.redirect("/manage/manageSearches/") return self.redirect("/manage/manageSearches/")
class Manage(IndexHandler): class Manage(IndexHandler):
@ -729,7 +730,7 @@ class Manage(IndexHandler):
Home.setStatus(cur_indexer_id, '|'.join(to_change[cur_indexer_id]), newStatus, direct=True) Home.setStatus(cur_indexer_id, '|'.join(to_change[cur_indexer_id]), newStatus, direct=True)
self.redirect('/manage/episodeStatuses/') return self.redirect('/manage/episodeStatuses/')
def showSubtitleMissed(self, indexer_id, whichSubs): def showSubtitleMissed(self, indexer_id, whichSubs):
@ -837,7 +838,7 @@ class Manage(IndexHandler):
show = sickbeard.helpers.findCertainShow(sickbeard.showList, int(cur_indexer_id)) show = sickbeard.helpers.findCertainShow(sickbeard.showList, int(cur_indexer_id))
subtitles = show.getEpisode(int(season), int(episode)).downloadSubtitles() subtitles = show.getEpisode(int(season), int(episode)).downloadSubtitles()
self.redirect('/manage/subtitleMissed/') return self.redirect('/manage/subtitleMissed/')
def backlogShow(self, indexer_id): def backlogShow(self, indexer_id):
@ -847,7 +848,7 @@ class Manage(IndexHandler):
if show_obj: if show_obj:
sickbeard.backlogSearchScheduler.action.searchBacklog([show_obj]) # @UndefinedVariable sickbeard.backlogSearchScheduler.action.searchBacklog([show_obj]) # @UndefinedVariable
self.redirect("/manage/backlogOverview/") return self.redirect("/manage/backlogOverview/")
def backlogOverview(self, *args, **kwargs): def backlogOverview(self, *args, **kwargs):
@ -897,7 +898,7 @@ class Manage(IndexHandler):
t.submenu = ManageMenu() t.submenu = ManageMenu()
if not toEdit: if not toEdit:
self.redirect("/manage/") return self.redirect("/manage/")
showIDs = toEdit.split("|") showIDs = toEdit.split("|")
showList = [] showList = []
@ -1062,7 +1063,7 @@ class Manage(IndexHandler):
ui.notifications.error('%d error%s while saving changes:' % (len(errors), "" if len(errors) == 1 else "s"), ui.notifications.error('%d error%s while saving changes:' % (len(errors), "" if len(errors) == 1 else "s"),
" ".join(errors)) " ".join(errors))
self.redirect("/manage/") return self.redirect("/manage/")
def massUpdate(self, toUpdate=None, toRefresh=None, toRename=None, toDelete=None, toMetadata=None, toSubtitle=None): def massUpdate(self, toUpdate=None, toRefresh=None, toRename=None, toDelete=None, toMetadata=None, toSubtitle=None):
@ -1171,7 +1172,7 @@ class Manage(IndexHandler):
ui.notifications.message("The following actions were queued:", ui.notifications.message("The following actions were queued:",
messageDetail) messageDetail)
self.redirect("/manage/") return self.redirect("/manage/")
def manageTorrents(self, *args, **kwargs): def manageTorrents(self, *args, **kwargs):
@ -1303,7 +1304,7 @@ class History(IndexHandler):
myDB.action("DELETE FROM history WHERE 1=1") myDB.action("DELETE FROM history WHERE 1=1")
ui.notifications.message('History cleared') ui.notifications.message('History cleared')
self.redirect("/history/") return self.redirect("/history/")
def trimHistory(self, *args, **kwargs): def trimHistory(self, *args, **kwargs):
@ -1313,7 +1314,7 @@ class History(IndexHandler):
(datetime.datetime.today() - datetime.timedelta(days=30)).strftime(history.dateFormat))) (datetime.datetime.today() - datetime.timedelta(days=30)).strftime(history.dateFormat)))
ui.notifications.message('Removed history entries greater than 30 days old') ui.notifications.message('Removed history entries greater than 30 days old')
self.redirect("/history/") return self.redirect("/history/")
ConfigMenu = [ ConfigMenu = [
@ -2461,7 +2462,7 @@ class HomePostProcess(IndexHandler):
if sickbeard.versionCheckScheduler.action.check_for_new_version(force=True): if sickbeard.versionCheckScheduler.action.check_for_new_version(force=True):
logger.log(u"Forcing version check") logger.log(u"Forcing version check")
self.redirect("/home/") return self.redirect("/home/")
def processEpisode(self, dir=None, nzbName=None, jobName=None, quiet=None, process_method=None, force=None, def processEpisode(self, dir=None, nzbName=None, jobName=None, quiet=None, process_method=None, force=None,
is_priority=None, failed="0", type="auto"): is_priority=None, failed="0", type="auto"):
@ -2482,7 +2483,7 @@ class HomePostProcess(IndexHandler):
is_priority = False is_priority = False
if not dir: if not dir:
self.redirect("/home/postprocess/") return self.redirect("/home/postprocess/")
else: else:
result = processTV.processDir(dir, nzbName, process_method=process_method, force=force, result = processTV.processDir(dir, nzbName, process_method=process_method, force=force,
is_priority=is_priority, failed=failed, type=type) is_priority=is_priority, failed=failed, type=type)
@ -2703,7 +2704,7 @@ class NewHomeAddShows(IndexHandler):
def finishAddShow(): def finishAddShow():
# if there are no extra shows then go home # if there are no extra shows then go home
if not other_shows: if not other_shows:
self.redirect('/home/') return self.redirect('/home/')
# peel off the next one # peel off the next one
next_show_dir = other_shows[0] next_show_dir = other_shows[0]
@ -2728,7 +2729,7 @@ class NewHomeAddShows(IndexHandler):
logger.log("Unable to add show due to show selection. Not anough arguments: %s" % (repr(series_pieces)), logger.log("Unable to add show due to show selection. Not anough arguments: %s" % (repr(series_pieces)),
logger.ERROR) logger.ERROR)
ui.notifications.error("Unknown error. Unable to add show due to problem with show selection.") ui.notifications.error("Unknown error. Unable to add show due to problem with show selection.")
self.redirect('/home/addShows/existingShows/') return self.redirect('/home/addShows/existingShows/')
indexer = int(series_pieces[1]) indexer = int(series_pieces[1])
indexer_id = int(series_pieces[3]) indexer_id = int(series_pieces[3])
show_name = series_pieces[4] show_name = series_pieces[4]
@ -2750,7 +2751,7 @@ class NewHomeAddShows(IndexHandler):
# blanket policy - if the dir exists you should have used "add existing show" numbnuts # blanket policy - if the dir exists you should have used "add existing show" numbnuts
if ek.ek(os.path.isdir, show_dir) and not fullShowPath: if ek.ek(os.path.isdir, show_dir) and not fullShowPath:
ui.notifications.error("Unable to add show", "Folder " + show_dir + " exists already") ui.notifications.error("Unable to add show", "Folder " + show_dir + " exists already")
self.redirect('/home/addShows/existingShows/') return self.redirect('/home/addShows/existingShows/')
# don't create show dir if config says not to # don't create show dir if config says not to
if sickbeard.ADD_SHOWS_WO_DIR: if sickbeard.ADD_SHOWS_WO_DIR:
@ -2761,7 +2762,7 @@ class NewHomeAddShows(IndexHandler):
logger.log(u"Unable to create the folder " + show_dir + ", can't add the show", logger.ERROR) logger.log(u"Unable to create the folder " + show_dir + ", can't add the show", logger.ERROR)
ui.notifications.error("Unable to add show", ui.notifications.error("Unable to add show",
"Unable to create the folder " + show_dir + ", can't add the show") "Unable to create the folder " + show_dir + ", can't add the show")
self.redirect("/home/") return self.redirect("/home/")
else: else:
helpers.chmodAsParent(show_dir) helpers.chmodAsParent(show_dir)
@ -2866,7 +2867,7 @@ class NewHomeAddShows(IndexHandler):
# if we're done then go home # if we're done then go home
if not dirs_only: if not dirs_only:
self.redirect('/home/') return self.redirect('/home/')
# for the remaining shows we need to prompt for each one, so forward this on to the newShow page # for the remaining shows we need to prompt for each one, so forward this on to the newShow page
return self.newShow(dirs_only[0], dirs_only[1:]) return self.newShow(dirs_only[0], dirs_only[1:])
@ -2889,7 +2890,7 @@ class ErrorLogs(IndexHandler):
def clearerrors(self, *args, **kwargs): def clearerrors(self, *args, **kwargs):
classes.ErrorViewer.clear() classes.ErrorViewer.clear()
self.redirect("/errorlogs/") return self.redirect("/errorlogs/")
def viewlog(self, minLevel=logger.MESSAGE, maxLines=500): def viewlog(self, minLevel=logger.MESSAGE, maxLines=500):
@ -3260,7 +3261,7 @@ class Home(IndexHandler):
def shutdown(self, pid=None): def shutdown(self, pid=None):
if str(pid) != str(sickbeard.PID): if str(pid) != str(sickbeard.PID):
self.redirect("/home/") return self.redirect("/home/")
threading.Timer(2, sickbeard.invoke_shutdown).start() threading.Timer(2, sickbeard.invoke_shutdown).start()
@ -3272,7 +3273,7 @@ class Home(IndexHandler):
def restart(self, pid=None): def restart(self, pid=None):
if str(pid) != str(sickbeard.PID): if str(pid) != str(sickbeard.PID):
self.redirect("/home/") return self.redirect("/home/")
t = PageTemplate(file="restart.tmpl") t = PageTemplate(file="restart.tmpl")
t.submenu = HomeMenu() t.submenu = HomeMenu()
@ -3286,16 +3287,14 @@ class Home(IndexHandler):
def update(self, pid=None): def update(self, pid=None):
if str(pid) != str(sickbeard.PID): if str(pid) != str(sickbeard.PID):
self.redirect("/home/") return self.redirect("/home/")
# auto-reload
tornado.autoreload.start(IOLoop.current())
updated = sickbeard.versionCheckScheduler.action.update() # @UndefinedVariable updated = sickbeard.versionCheckScheduler.action.update() # @UndefinedVariable
if updated: if updated:
# do a hard restart # do a hard restart
#threading.Timer(2, sickbeard.invoke_restart, [False]).start() if not sickbeard.AUTO_UPDATE:
threading.Timer(2, sickbeard.invoke_restart, [False]).start()
t = PageTemplate(file="restart_bare.tmpl") t = PageTemplate(file="restart_bare.tmpl")
return _munge(t) return _munge(t)
else: else:
@ -3709,7 +3708,7 @@ class Home(IndexHandler):
ui.notifications.error('%d error%s while saving changes:' % (len(errors), "" if len(errors) == 1 else "s"), ui.notifications.error('%d error%s while saving changes:' % (len(errors), "" if len(errors) == 1 else "s"),
'<ul>' + '\n'.join(['<li>%s</li>' % error for error in errors]) + "</ul>") '<ul>' + '\n'.join(['<li>%s</li>' % error for error in errors]) + "</ul>")
self.redirect("/home/displayShow?show=" + show) return self.redirect("/home/displayShow?show=" + show)
def deleteShow(self, show=None): def deleteShow(self, show=None):
@ -3729,7 +3728,7 @@ class Home(IndexHandler):
showObj.deleteShow() showObj.deleteShow()
ui.notifications.message('<b>%s</b> has been deleted' % showObj.name) ui.notifications.message('<b>%s</b> has been deleted' % showObj.name)
self.redirect("/home/") return self.redirect("/home/")
def refreshShow(self, show=None): def refreshShow(self, show=None):
@ -3751,7 +3750,7 @@ class Home(IndexHandler):
time.sleep(cpu_presets[sickbeard.CPU_PRESET]) time.sleep(cpu_presets[sickbeard.CPU_PRESET])
self.redirect("/home/displayShow?show=" + str(showObj.indexerid)) return self.redirect("/home/displayShow?show=" + str(showObj.indexerid))
def updateShow(self, show=None, force=0): def updateShow(self, show=None, force=0):
@ -3774,7 +3773,7 @@ class Home(IndexHandler):
# just give it some time # just give it some time
time.sleep(cpu_presets[sickbeard.CPU_PRESET]) time.sleep(cpu_presets[sickbeard.CPU_PRESET])
self.redirect("/home/displayShow?show=" + str(showObj.indexerid)) return self.redirect("/home/displayShow?show=" + str(showObj.indexerid))
def subtitleShow(self, show=None, force=0): def subtitleShow(self, show=None, force=0):
@ -3792,7 +3791,7 @@ class Home(IndexHandler):
time.sleep(cpu_presets[sickbeard.CPU_PRESET]) time.sleep(cpu_presets[sickbeard.CPU_PRESET])
self.redirect("/home/displayShow?show=" + str(showObj.indexerid)) return self.redirect("/home/displayShow?show=" + str(showObj.indexerid))
def updateXBMC(self, showName=None): def updateXBMC(self, showName=None):
@ -3808,7 +3807,7 @@ class Home(IndexHandler):
ui.notifications.message("Library update command sent to XBMC host(s): " + host) ui.notifications.message("Library update command sent to XBMC host(s): " + host)
else: else:
ui.notifications.error("Unable to contact one or more XBMC host(s): " + host) ui.notifications.error("Unable to contact one or more XBMC host(s): " + host)
self.redirect('/home/') return self.redirect('/home/')
def updatePLEX(self, *args, **kwargs): def updatePLEX(self, *args, **kwargs):
@ -3817,7 +3816,7 @@ class Home(IndexHandler):
"Library update command sent to Plex Media Server host: " + sickbeard.PLEX_SERVER_HOST) "Library update command sent to Plex Media Server host: " + sickbeard.PLEX_SERVER_HOST)
else: else:
ui.notifications.error("Unable to contact Plex Media Server host: " + sickbeard.PLEX_SERVER_HOST) ui.notifications.error("Unable to contact Plex Media Server host: " + sickbeard.PLEX_SERVER_HOST)
self.redirect('/home/') return self.redirect('/home/')
def setStatus(self, show=None, eps=None, status=None, direct=False): def setStatus(self, show=None, eps=None, status=None, direct=False):
@ -3931,7 +3930,7 @@ class Home(IndexHandler):
if direct: if direct:
return json.dumps({'result': 'success'}) return json.dumps({'result': 'success'})
else: else:
self.redirect("/home/displayShow?show=" + show) return self.redirect("/home/displayShow?show=" + show)
def testRename(self, show=None): def testRename(self, show=None):
@ -3998,7 +3997,7 @@ class Home(IndexHandler):
return _genericMessage("Error", "Can't rename episodes when the show dir is missing.") return _genericMessage("Error", "Can't rename episodes when the show dir is missing.")
if eps is None: if eps is None:
self.redirect("/home/displayShow?show=" + show) return self.redirect("/home/displayShow?show=" + show)
with db.DBConnection() as myDB: with db.DBConnection() as myDB:
for curEp in eps.split('|'): for curEp in eps.split('|'):
@ -4025,7 +4024,7 @@ class Home(IndexHandler):
root_ep_obj.rename() root_ep_obj.rename()
self.redirect("/home/displayShow?show=" + show) return self.redirect("/home/displayShow?show=" + show)
def searchEpisode(self, show=None, season=None, episode=None): def searchEpisode(self, show=None, season=None, episode=None):

View file

@ -16,6 +16,7 @@ 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
self.default_filename = default_filename
def get(self, path, include_body=True): def get(self, path, include_body=True):
for p in self.paths: for p in self.paths:
@ -44,41 +45,6 @@ def initWebServer(options={}):
assert isinstance(options['port'], int) assert isinstance(options['port'], int)
assert 'data_root' in options assert 'data_root' in options
def http_error_401_hander(status, message, traceback, version):
""" Custom handler for 401 error """
if status != "401 Unauthorized":
logger.log(u"Tornado caught an error: %s %s" % (status, message), logger.ERROR)
logger.log(traceback, logger.DEBUG)
return r'''<!DOCTYPE html>
<html>
<head>
<title>%s</title>
</head>
<body>
<br/>
<font color="#0000FF">Error %s: You need to provide a valid username and password.</font>
</body>
</html>
''' % ('Access denied', status)
def http_error_404_hander(status, message, traceback, version):
""" Custom handler for 404 error, redirect back to main page """
return r'''<!DOCTYPE html>
<html>
<head>
<title>404</title>
<script type="text/javascript" charset="utf-8">
<!--
location.href = "%s/home/"
//-->
</script>
</head>
<body>
<br/>
</body>
</html>
''' % options['web_root']
# tornado setup # tornado setup
enable_https = options['enable_https'] enable_https = options['enable_https']
https_cert = options['https_cert'] https_cert = options['https_cert']
@ -101,25 +67,24 @@ def initWebServer(options={}):
app = Application([], app = Application([],
debug=sickbeard.DEBUG, debug=sickbeard.DEBUG,
gzip=True, gzip=True,
xheaders=True, xheaders=sickbeard.HANDLE_REVERSE_PROXY,
cookie_secret='61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=' cookie_secret='61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo='
) )
# Index Handler # Main Handler
app.add_handlers(".*$", [ app.add_handlers(".*$", [
(r"/", RedirectHandler, {'url': '/home/'}), (r"/", RedirectHandler, {'url': '%s/home/' % options['web_root']}),
(r'/api/(.*)(/?)', webapi.Api), (r'%s/api/(.*)(/?)' % options['web_root'], webapi.Api),
(r'%s(.*)(/?)' % options['web_root'], webserve.IndexHandler) (r'%s/(.*)(/?)' % options['web_root'], webserve.IndexHandler)
]) ])
# Static Path Handler # Static Path Handler
app.add_handlers(".*$", [ app.add_handlers(".*$", [
(r'/(favicon\.ico)', MultiStaticFileHandler, (r'%s/(favicon\.ico)' % options['web_root'], MultiStaticFileHandler,
{'paths': '%s/%s' % (options['web_root'], 'images/ico/favicon.ico')}), {'paths': [os.path.join(options['data_root'], 'images/ico/favicon.ico')]}),
(r'%s/%s/(.*)(/?)' % (options['web_root'], 'images'), MultiStaticFileHandler, (r'%s/%s/(.*)(/?)' % (options['web_root'], 'images'), MultiStaticFileHandler,
{'paths': [os.path.join(options['data_root'], 'images'), {'paths': [os.path.join(options['data_root'], 'images'),
os.path.join(sickbeard.CACHE_DIR, 'images'), os.path.join(sickbeard.CACHE_DIR, 'images')]}),
os.path.join(sickbeard.CACHE_DIR, 'images', 'thumbnails')]}),
(r'%s/%s/(.*)(/?)' % (options['web_root'], 'css'), MultiStaticFileHandler, (r'%s/%s/(.*)(/?)' % (options['web_root'], 'css'), MultiStaticFileHandler,
{'paths': [os.path.join(options['data_root'], 'css')]}), {'paths': [os.path.join(options['data_root'], 'css')]}),
(r'%s/%s/(.*)(/?)' % (options['web_root'], 'js'), MultiStaticFileHandler, (r'%s/%s/(.*)(/?)' % (options['web_root'], 'js'), MultiStaticFileHandler,

View file

@ -630,6 +630,7 @@ class RequestHandler(object):
self.set_status(status) self.set_status(status)
self.set_header("Location", urlparse.urljoin(utf8(self.request.uri), self.set_header("Location", urlparse.urljoin(utf8(self.request.uri),
utf8(url))) utf8(url)))
self.finish() self.finish()
def write(self, chunk): def write(self, chunk):