Commit c0b6be87 authored by echel0n's avatar echel0n
Browse files

Added websockets support for ui messages

parent 096b055d
# Changelog
- * a264515 - 2018-08-24: Improvements made to tornado web handler code
- * 7e11f4e - 2018-08-24: Added websockets support for ui messages
- * 096b055 - 2018-08-24: Improvements made to tornado web handler code
- * a7f3541 - 2018-08-23: Release v9.3.58
- * 0366d15 - 2018-08-23: Changed the way TheTVDB cache's its shows, improved performance of app
- * faefa9d - 2018-08-23: Fixed issues with custom web roots and redirects Checking for updates now redirects back to referrer url
......
......@@ -80,7 +80,7 @@ class Core(object):
def __init__(self):
self.started = False
self.daemon = None
self.io_loop = IOLoop().current()
self.io_loop = IOLoop()
self.pid = os.getpid()
self.showlist = []
......
......@@ -35,7 +35,7 @@ class Notifications(object):
self._messages = []
self._errors = []
def message(self, title, message=None):
def message(self, title, message=""):
"""
Add a regular notification to the queue
......@@ -45,7 +45,7 @@ class Notifications(object):
self._messages.append(Notification(title, message, MESSAGE))
def error(self, title, message=None):
def error(self, title, message=""):
"""
Add an error notification to the queue
......
......@@ -31,7 +31,7 @@ import sickrage
from sickrage.core.helpers import create_https_certificates, launch_browser
from sickrage.core.webserver.api import ApiHandler, KeyHandler
from sickrage.core.webserver.routes import Route
from sickrage.core.webserver.views import CalendarHandler, LoginHandler, LogoutHandler
from sickrage.core.webserver.views import CalendarHandler, LoginHandler, LogoutHandler, WebSocketUIHandler
class StaticImageHandler(StaticFileHandler):
......@@ -81,7 +81,8 @@ class WebServer(object):
# web root
if sickrage.app.config.web_root:
sickrage.app.config.web_root = sickrage.app.config.web_root = ('/' + sickrage.app.config.web_root.lstrip('/').strip('/'))
sickrage.app.config.web_root = sickrage.app.config.web_root = (
'/' + sickrage.app.config.web_root.lstrip('/').strip('/'))
# api root
self.api_root = r'%s/api/%s' % (sickrage.app.config.web_root, sickrage.app.config.api_key)
......@@ -105,73 +106,82 @@ class WebServer(object):
# Load the app
self.app = Application(
[
# api
(r'%s(/?.*)' % self.api_root, ApiHandler),
debug=True,
autoreload=False,
gzip=sickrage.app.config.web_use_gzip,
cookie_secret=sickrage.app.config.web_cookie_secret,
login_url='%s/login/' % sickrage.app.config.web_root)
# redirect to home
(r"(%s)" % sickrage.app.config.web_root, RedirectHandler,
{"url": "%s/home" % sickrage.app.config.web_root}),
# Websocket handler
self.app.add_handlers(".*$", [
(r'%s/ws/ui' % sickrage.app.config.web_root, WebSocketUIHandler)
])
# api key
(r'%s/getkey(/?.*)' % sickrage.app.config.web_root, KeyHandler),
# Static File Handlers
self.app.add_handlers('.*$', [
# api
(r'%s(/?.*)' % self.api_root, ApiHandler),
# api builder
(r'%s/api/builder' % sickrage.app.config.web_root, RedirectHandler,
{"url": sickrage.app.config.web_root + '/apibuilder/'}),
# redirect to home
(r"(%s)" % sickrage.app.config.web_root, RedirectHandler,
{"url": "%s/home" % sickrage.app.config.web_root}),
# login
(r'%s/login(/?)' % sickrage.app.config.web_root, LoginHandler),
# api key
(r'%s/getkey(/?.*)' % sickrage.app.config.web_root, KeyHandler),
# logout
(r'%s/logout(/?)' % sickrage.app.config.web_root, LogoutHandler),
# api builder
(r'%s/api/builder' % sickrage.app.config.web_root, RedirectHandler,
{"url": sickrage.app.config.web_root + '/apibuilder/'}),
# calendar
(r'%s/calendar' % sickrage.app.config.web_root, CalendarHandler),
# login
(r'%s/login(/?)' % sickrage.app.config.web_root, LoginHandler),
# favicon
(r'%s/(favicon\.ico)' % sickrage.app.config.web_root, StaticNoCacheFileHandler,
{"path": os.path.join(sickrage.app.config.gui_static_dir, 'images/favicon.ico')}),
# logout
(r'%s/logout(/?)' % sickrage.app.config.web_root, LogoutHandler),
# images
(r'%s/images/(.*)' % sickrage.app.config.web_root, StaticImageHandler,
{"path": os.path.join(sickrage.app.config.gui_static_dir, 'images')}),
# calendar
(r'%s/calendar' % sickrage.app.config.web_root, CalendarHandler),
# css
(r'%s/css/(.*)' % sickrage.app.config.web_root, StaticNoCacheFileHandler,
{"path": os.path.join(sickrage.app.config.gui_static_dir, 'css')}),
# favicon
(r'%s/(favicon\.ico)' % sickrage.app.config.web_root, StaticNoCacheFileHandler,
{"path": os.path.join(sickrage.app.config.gui_static_dir, 'images/favicon.ico')}),
# scss
(r'%s/scss/(.*)' % sickrage.app.config.web_root, StaticNoCacheFileHandler,
{"path": os.path.join(sickrage.app.config.gui_static_dir, 'scss')}),
# images
(r'%s/images/(.*)' % sickrage.app.config.web_root, StaticImageHandler,
{"path": os.path.join(sickrage.app.config.gui_static_dir, 'images')}),
# fonts
(r'%s/fonts/(.*)' % sickrage.app.config.web_root, StaticNoCacheFileHandler,
{"path": os.path.join(sickrage.app.config.gui_static_dir, 'fonts')}),
# css
(r'%s/css/(.*)' % sickrage.app.config.web_root, StaticNoCacheFileHandler,
{"path": os.path.join(sickrage.app.config.gui_static_dir, 'css')}),
# javascript
(r'%s/js/(.*)' % sickrage.app.config.web_root, StaticNoCacheFileHandler,
{"path": os.path.join(sickrage.app.config.gui_static_dir, 'js')}),
# scss
(r'%s/scss/(.*)' % sickrage.app.config.web_root, StaticNoCacheFileHandler,
{"path": os.path.join(sickrage.app.config.gui_static_dir, 'scss')}),
# videos
(r'%s/videos/(.*)' % sickrage.app.config.web_root, StaticNoCacheFileHandler,
{"path": self.video_root}),
] + Route.get_routes(sickrage.app.config.web_root),
debug=True,
autoreload=False,
gzip=sickrage.app.config.web_use_gzip,
cookie_secret=sickrage.app.config.web_cookie_secret,
login_url='%s/login/' % sickrage.app.config.web_root)
# fonts
(r'%s/fonts/(.*)' % sickrage.app.config.web_root, StaticNoCacheFileHandler,
{"path": os.path.join(sickrage.app.config.gui_static_dir, 'fonts')}),
# javascript
(r'%s/js/(.*)' % sickrage.app.config.web_root, StaticNoCacheFileHandler,
{"path": os.path.join(sickrage.app.config.gui_static_dir, 'js')}),
# videos
(r'%s/videos/(.*)' % sickrage.app.config.web_root, StaticNoCacheFileHandler,
{"path": self.video_root}),
])
self.server = HTTPServer(self.app, no_keep_alive=True, xheaders=sickrage.app.config.handle_reverse_proxy)
# Web Handlers
self.app.add_handlers('.*$', Route.get_routes(sickrage.app.config.web_root))
self.server = HTTPServer(self.app, xheaders=sickrage.app.config.handle_reverse_proxy)
if sickrage.app.config.enable_https: self.server.ssl_options = {
"certfile": sickrage.app.config.https_cert,
"keyfile": sickrage.app.config.https_key
}
try:
self.server.listen(sickrage.app.config.web_port, None)
self.server.listen(sickrage.app.config.web_port)
sickrage.app.log.info(
"SiCKRAGE :: STARTED")
......@@ -183,7 +193,8 @@ class WebServer(object):
"SiCKRAGE :: DATABASE:[v{}]".format(sickrage.app.main_db.version))
sickrage.app.log.info(
"SiCKRAGE :: URL:[{}://{}:{}{}]".format(('http', 'https')[sickrage.app.config.enable_https],
sickrage.app.config.web_host, sickrage.app.config.web_port, sickrage.app.config.web_root))
sickrage.app.config.web_host, sickrage.app.config.web_port,
sickrage.app.config.web_root))
# launch browser window
if all([not sickrage.app.no_launch,
......
......@@ -41,6 +41,7 @@ from tornado.escape import json_encode, recursive_unicode, json_decode
from tornado.gen import coroutine
from tornado.process import cpu_count
from tornado.web import RequestHandler, authenticated
from tornado.websocket import WebSocketHandler, WebSocketClosedError
import sickrage
import sickrage.subtitles
......@@ -231,7 +232,6 @@ class WebHandler(BaseHandler):
result = yield self.route()
if result: self.write(result)
@coroutine
@authenticated
def post(self, *args, **kwargs):
......@@ -298,6 +298,42 @@ class LogoutHandler(BaseHandler):
return self.redirect('/login/')
class WebSocketUIHandler(WebSocketHandler):
"""WebSocket handler to send and receive data to and from a web client."""
clients = set()
def check_origin(self, origin):
"""Allow alternate origins."""
return True
def open(self, *args, **kwargs):
"""Client connected to the WebSocket."""
self.clients.add(self)
# If we have pending messages send them to the new client
for cur_notification in sickrage.app.alerts.get_notifications(self.request.remote_ip):
try:
self.write_message(json_encode({'event': 'notification',
'data': {'title': cur_notification.title,
'message': cur_notification.message,
'type': cur_notification.type}}))
except WebSocketClosedError:
pass
def on_message(self, message):
"""Received a message from the client."""
sickrage.app.log.debug('WebSocket received message from {}: {}'.format(self.request.remote_ip, message))
def on_close(self):
"""Client disconnected from the WebSocket."""
self.clients.remove(self)
def __repr__(self):
"""Client representation."""
return '<{} Client: {}>'.format(type(self).__name__, self.request.remote_ip)
class CalendarHandler(BaseHandler):
def prepare(self, *args, **kwargs):
if sickrage.app.config.calendar_unprotected:
......@@ -531,32 +567,6 @@ class WebRoot(WebHandler):
sickrage.app.quicksearch_cache.get_shows(term) + sickrage.app.quicksearch_cache.get_episodes(term))
@Route('/ui(/?.*)')
class UI(WebHandler):
def __init__(self, *args, **kwargs):
super(UI, self).__init__(*args, **kwargs)
self.set_header('Content-Type', 'application/json')
@staticmethod
def add_message():
sickrage.app.alerts.message('Test 1', 'This is test number 1')
sickrage.app.alerts.error('Test 2', 'This is test number 2')
return "ok"
def get_messages(self):
messages = {}
cur_notification_num = 0
for cur_notification in sickrage.app.alerts.get_notifications(self.request.remote_ip):
cur_notification_num += 1
messages['notification-{}'.format(cur_notification_num)] = {
'title': cur_notification.title,
'message': cur_notification.message or "",
'type': cur_notification.type
}
if messages: return json_encode(messages)
@Route('/browser(/?.*)')
class WebFileBrowser(WebHandler):
def __init__(self, *args, **kwargs):
......
......@@ -67,7 +67,6 @@
<link rel="stylesheet" type="text/css" href="${srWebRoot}/css/core.min.css"/>
<%block name="css" />
</head>
<body data-controller="${controller}" data-action="${action}">
${mainModals()}
......
......@@ -32,19 +32,18 @@ var gt = function (msgid) {
$(document).ready(function ($) {
var SICKRAGE = {
check_notifications: function () {
var message_url = SICKRAGE.srWebRoot + '/ui/get_messages';
if ('visible' === document.visibilityState) {
$.getJSON(message_url, function (data) {
$.each(data, function (name, data) {
SICKRAGE.notify(data.type, data.title, data.message);
});
});
}
setTimeout(function () {
"use strict";
SICKRAGE.check_notifications();
}, 3000);
ws_notifications: function () {
const proto = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
var ws = new WebSocket(proto + '//' + window.location.hostname + ':' + window.location.port + SICKRAGE.srWebRoot + '/ws/ui');
ws.onmessage = function (evt) {
var msg = JSON.parse(evt.data);
// Add handling for different kinds of events. For ex: {"event": "notification", "data": {"title": ..}}
if (msg.event === 'notification') {
SICKRAGE.notify(msg.data.type, msg.data.title, msg.data.message);
}
};
},
notify: function (type, title, message) {
......@@ -232,6 +231,8 @@ $(document).ready(function ($) {
SICKRAGE.loadingHTML = '<i class="fas fa-spinner fa-spin fa-fw"></i>';
SICKRAGE.anonURL = SICKRAGE.getMeta('anonURL');
SICKRAGE.ws_notifications();
// add locale translation
$.get(`${SICKRAGE.srWebRoot}/messages.po`, function (data) {
if (data) {
......@@ -485,7 +486,6 @@ $(document).ready(function ($) {
SICKRAGE.browser.init();
SICKRAGE.root_dirs.init();
SICKRAGE.quality_chooser.init();
SICKRAGE.check_notifications();
$("#changelog").on('click', function (event) {
event.preventDefault();
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment