forked from openlp/openlp
head r2249
This commit is contained in:
commit
3c4e67545b
@ -779,8 +779,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
"""
|
||||
We need to make sure, that the SlidePreview's size is correct.
|
||||
"""
|
||||
self.preview_controller.previewSizeChanged()
|
||||
self.live_controller.previewSizeChanged()
|
||||
self.preview_controller.preview_size_changed()
|
||||
self.live_controller.preview_size_changed()
|
||||
|
||||
def on_settings_shortcuts_item_clicked(self):
|
||||
"""
|
||||
@ -989,8 +989,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
self.application.set_busy_cursor()
|
||||
self.image_manager.update_display()
|
||||
self.renderer.update_display()
|
||||
self.preview_controller.screenSizeChanged()
|
||||
self.live_controller.screenSizeChanged()
|
||||
self.preview_controller.screen_size_changed()
|
||||
self.live_controller.screen_size_changed()
|
||||
self.setFocus()
|
||||
self.activateWindow()
|
||||
self.application.set_normal_cursor()
|
||||
|
@ -89,7 +89,7 @@ class SlideController(DisplayController):
|
||||
Set up the Slide Controller.
|
||||
"""
|
||||
DisplayController.__init__(self, parent, is_live)
|
||||
Registry().register_function(u'bootstrap_post_set_up', self.screenSizeChanged)
|
||||
Registry().register_function(u'bootstrap_post_set_up', self.screen_size_changed)
|
||||
self.screens = ScreenList()
|
||||
try:
|
||||
self.ratio = float(self.screens.current[u'size'].width()) / float(self.screens.current[u'size'].height())
|
||||
@ -121,6 +121,8 @@ class SlideController(DisplayController):
|
||||
self.update_slide_limits()
|
||||
self.panel = QtGui.QWidget(parent.controlSplitter)
|
||||
self.slideList = {}
|
||||
self.slide_count = 0
|
||||
self.slide_image = None
|
||||
# Layout for holding panel
|
||||
self.panel_layout = QtGui.QVBoxLayout(self.panel)
|
||||
self.panel_layout.setSpacing(0)
|
||||
@ -321,18 +323,18 @@ class SlideController(DisplayController):
|
||||
self.slide_layout.insertWidget(0, self.preview_display)
|
||||
self.preview_display.hide()
|
||||
# Actual preview screen
|
||||
self.slidePreview = QtGui.QLabel(self)
|
||||
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.slidePreview.sizePolicy().hasHeightForWidth())
|
||||
self.slidePreview.setSizePolicy(sizePolicy)
|
||||
self.slidePreview.setFrameShape(QtGui.QFrame.Box)
|
||||
self.slidePreview.setFrameShadow(QtGui.QFrame.Plain)
|
||||
self.slidePreview.setLineWidth(1)
|
||||
self.slidePreview.setScaledContents(True)
|
||||
self.slidePreview.setObjectName(u'slidePreview')
|
||||
self.slide_layout.insertWidget(0, self.slidePreview)
|
||||
self.slide_preview = QtGui.QLabel(self)
|
||||
size_policy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
|
||||
size_policy.setHorizontalStretch(0)
|
||||
size_policy.setVerticalStretch(0)
|
||||
size_policy.setHeightForWidth(self.slide_preview.sizePolicy().hasHeightForWidth())
|
||||
self.slide_preview.setSizePolicy(size_policy)
|
||||
self.slide_preview.setFrameShape(QtGui.QFrame.Box)
|
||||
self.slide_preview.setFrameShadow(QtGui.QFrame.Plain)
|
||||
self.slide_preview.setLineWidth(1)
|
||||
self.slide_preview.setScaledContents(True)
|
||||
self.slide_preview.setObjectName(u'slide_preview')
|
||||
self.slide_layout.insertWidget(0, self.slide_preview)
|
||||
self.grid.addLayout(self.slide_layout, 0, 0, 1, 1)
|
||||
if self.is_live:
|
||||
self.current_shortcut = u''
|
||||
@ -517,10 +519,9 @@ class SlideController(DisplayController):
|
||||
self.service_manager.next_item()
|
||||
self.keypress_loop = False
|
||||
|
||||
def screenSizeChanged(self):
|
||||
def screen_size_changed(self):
|
||||
"""
|
||||
Settings dialog has changed the screen size of adjust output and
|
||||
screen previews.
|
||||
Settings dialog has changed the screen size of adjust output and screen previews.
|
||||
"""
|
||||
# rebuild display as screen size changed
|
||||
if self.display:
|
||||
@ -536,14 +537,14 @@ class SlideController(DisplayController):
|
||||
except ZeroDivisionError:
|
||||
self.ratio = 1
|
||||
self.media_controller.setup_display(self.display, False)
|
||||
self.previewSizeChanged()
|
||||
self.preview_size_changed()
|
||||
self.preview_display.setup()
|
||||
service_item = ServiceItem()
|
||||
self.preview_display.web_view.setHtml(build_html(service_item, self.preview_display.screen, None, self.is_live,
|
||||
plugins=self.plugin_manager.plugins))
|
||||
self.media_controller.setup_display(self.preview_display, True)
|
||||
if self.service_item:
|
||||
self.refreshServiceItem()
|
||||
self.refresh_service_item()
|
||||
|
||||
def __addActionsToWidget(self, widget):
|
||||
"""
|
||||
@ -554,7 +555,7 @@ class SlideController(DisplayController):
|
||||
self.previousService, self.nextService,
|
||||
self.escapeItem])
|
||||
|
||||
def previewSizeChanged(self):
|
||||
def preview_size_changed(self):
|
||||
"""
|
||||
Takes care of the SlidePreview's size. Is called when one of the the
|
||||
splitters is moved or when the screen size is changed. Note, that this
|
||||
@ -563,14 +564,14 @@ class SlideController(DisplayController):
|
||||
if self.ratio < float(self.preview_frame.width()) / float(self.preview_frame.height()):
|
||||
# We have to take the height as limit.
|
||||
max_height = self.preview_frame.height() - self.grid.margin() * 2
|
||||
self.slidePreview.setFixedSize(QtCore.QSize(max_height * self.ratio, max_height))
|
||||
self.slide_preview.setFixedSize(QtCore.QSize(max_height * self.ratio, max_height))
|
||||
self.preview_display.setFixedSize(QtCore.QSize(max_height * self.ratio, max_height))
|
||||
self.preview_display.screen = {
|
||||
u'size': self.preview_display.geometry()}
|
||||
else:
|
||||
# We have to take the width as limit.
|
||||
max_width = self.preview_frame.width() - self.grid.margin() * 2
|
||||
self.slidePreview.setFixedSize(QtCore.QSize(max_width, max_width / self.ratio))
|
||||
self.slide_preview.setFixedSize(QtCore.QSize(max_width, max_width / self.ratio))
|
||||
self.preview_display.setFixedSize(QtCore.QSize(max_width, max_width / self.ratio))
|
||||
self.preview_display.screen = {
|
||||
u'size': self.preview_display.geometry()}
|
||||
@ -624,17 +625,16 @@ class SlideController(DisplayController):
|
||||
"""
|
||||
self.slide_limits = Settings().value(self.main_window.advanced_settings_section + u'/slide limits')
|
||||
|
||||
def enableToolBar(self, item):
|
||||
def enable_tool_bar(self, item):
|
||||
"""
|
||||
Allows the toolbars to be reconfigured based on Controller Type
|
||||
and ServiceItem Type
|
||||
Allows the toolbars to be reconfigured based on Controller Type and ServiceItem Type
|
||||
"""
|
||||
if self.is_live:
|
||||
self.enableLiveToolBar(item)
|
||||
self.enable_live_tool_bar(item)
|
||||
else:
|
||||
self.enablePreviewToolBar(item)
|
||||
self.enable_preview_tool_bar(item)
|
||||
|
||||
def enableLiveToolBar(self, item):
|
||||
def enable_live_tool_bar(self, item):
|
||||
"""
|
||||
Allows the live toolbar to be customised
|
||||
"""
|
||||
@ -663,7 +663,7 @@ class SlideController(DisplayController):
|
||||
# See bug #791050
|
||||
self.toolbar.show()
|
||||
|
||||
def enablePreviewToolBar(self, item):
|
||||
def enable_preview_tool_bar(self, item):
|
||||
"""
|
||||
Allows the Preview toolbar to be customised
|
||||
"""
|
||||
@ -682,15 +682,15 @@ class SlideController(DisplayController):
|
||||
# See bug #791050
|
||||
self.toolbar.show()
|
||||
|
||||
def refreshServiceItem(self):
|
||||
def refresh_service_item(self):
|
||||
"""
|
||||
Method to update the service item if the screen has changed
|
||||
"""
|
||||
log.debug(u'refreshServiceItem live = %s' % self.is_live)
|
||||
log.debug(u'refresh_service_item live = %s' % self.is_live)
|
||||
if self.service_item.is_text() or self.service_item.is_image():
|
||||
item = self.service_item
|
||||
item.render()
|
||||
self._processItem(item, self.selected_row)
|
||||
self._process_item(item, self.selected_row)
|
||||
|
||||
def add_service_item(self, item):
|
||||
"""
|
||||
@ -703,14 +703,14 @@ class SlideController(DisplayController):
|
||||
if self.song_edit:
|
||||
slideno = self.selected_row
|
||||
self.song_edit = False
|
||||
self._processItem(item, slideno)
|
||||
self._process_item(item, slideno)
|
||||
|
||||
def replaceServiceManagerItem(self, item):
|
||||
"""
|
||||
Replacement item following a remote edit
|
||||
"""
|
||||
if item == self.service_item:
|
||||
self._processItem(item, self.preview_list_widget.currentRow())
|
||||
self._process_item(item, self.preview_list_widget.currentRow())
|
||||
|
||||
def addServiceManagerItem(self, item, slideno):
|
||||
"""
|
||||
@ -729,7 +729,7 @@ class SlideController(DisplayController):
|
||||
self.__checkUpdateSelectedSlide(slidenum)
|
||||
self.slideSelected()
|
||||
else:
|
||||
self._processItem(item, slidenum)
|
||||
self._process_item(item, slidenum)
|
||||
if self.is_live and item.auto_play_slides_loop and item.timed_slide_interval > 0:
|
||||
self.play_slides_loop.setChecked(item.auto_play_slides_loop)
|
||||
self.delay_spin_box.setValue(int(item.timed_slide_interval))
|
||||
@ -739,7 +739,7 @@ class SlideController(DisplayController):
|
||||
self.delay_spin_box.setValue(int(item.timed_slide_interval))
|
||||
self.onPlaySlidesOnce()
|
||||
|
||||
def _processItem(self, service_item, slideno):
|
||||
def _process_item(self, service_item, slideno):
|
||||
"""
|
||||
Loads a ServiceItem into the system from ServiceManager
|
||||
Display the slide number passed
|
||||
@ -827,10 +827,9 @@ class SlideController(DisplayController):
|
||||
self.preview_list_widget.setVerticalHeaderLabels(text)
|
||||
if self.service_item.is_text():
|
||||
self.preview_list_widget.resizeRowsToContents()
|
||||
self.preview_list_widget.setColumnWidth(0,
|
||||
self.preview_list_widget.viewport().size().width())
|
||||
self.preview_list_widget.setColumnWidth(0, self.preview_list_widget.viewport().size().width())
|
||||
self.__updatePreviewSelection(slideno)
|
||||
self.enableToolBar(service_item)
|
||||
self.enable_tool_bar(service_item)
|
||||
# Pass to display for viewing.
|
||||
# Postpone image build, we need to do this later to avoid the theme
|
||||
# flashing on the screen
|
||||
@ -1050,27 +1049,28 @@ class SlideController(DisplayController):
|
||||
|
||||
def updatePreview(self):
|
||||
"""
|
||||
This updates the preview frame, for example after changing a slide or
|
||||
using *Blank to Theme*.
|
||||
This updates the preview frame, for example after changing a slide or using *Blank to Theme*.
|
||||
"""
|
||||
log.debug(u'updatePreview %s ' % self.screens.current[u'primary'])
|
||||
if not self.screens.current[u'primary'] and self.service_item and \
|
||||
self.service_item.is_capable(ItemCapabilities.ProvidesOwnDisplay):
|
||||
# Grab now, but try again in a couple of seconds if slide change
|
||||
# is slow
|
||||
QtCore.QTimer.singleShot(0.5, self.grabMainDisplay)
|
||||
QtCore.QTimer.singleShot(2.5, self.grabMainDisplay)
|
||||
# Grab now, but try again in a couple of seconds if slide change is slow
|
||||
QtCore.QTimer.singleShot(0.5, self.grab_maindisplay)
|
||||
QtCore.QTimer.singleShot(2.5, self.grab_maindisplay)
|
||||
else:
|
||||
self.slidePreview.setPixmap(self.display.preview())
|
||||
self.slide_image = self.display.preview()
|
||||
self.slide_preview.setPixmap(self.slide_image)
|
||||
self.slide_count += 1
|
||||
|
||||
def grabMainDisplay(self):
|
||||
def grab_maindisplay(self):
|
||||
"""
|
||||
Creates an image of the current screen and updates the preview frame.
|
||||
"""
|
||||
winid = QtGui.QApplication.desktop().winId()
|
||||
win_id = QtGui.QApplication.desktop().winId()
|
||||
rect = self.screens.current[u'size']
|
||||
winimg = QtGui.QPixmap.grabWindow(winid, rect.x(), rect.y(), rect.width(), rect.height())
|
||||
self.slidePreview.setPixmap(winimg)
|
||||
win_image = QtGui.QPixmap.grabWindow(win_id, rect.x(), rect.y(), rect.width(), rect.height())
|
||||
self.slide_preview.setPixmap(win_image)
|
||||
self.slide_image = win_image
|
||||
|
||||
def on_slide_selected_next_action(self, checked):
|
||||
"""
|
||||
@ -1276,7 +1276,7 @@ class SlideController(DisplayController):
|
||||
self.media_controller.video(self.controller_type, item, self.hide_mode())
|
||||
if not self.is_live:
|
||||
self.preview_display.show()
|
||||
self.slidePreview.hide()
|
||||
self.slide_preview.hide()
|
||||
|
||||
def onMediaClose(self):
|
||||
"""
|
||||
@ -1285,7 +1285,7 @@ class SlideController(DisplayController):
|
||||
log.debug(u'SlideController onMediaClose')
|
||||
self.media_controller.media_reset(self)
|
||||
self.preview_display.hide()
|
||||
self.slidePreview.show()
|
||||
self.slide_preview.show()
|
||||
|
||||
def _resetBlank(self):
|
||||
"""
|
||||
|
39
openlp/plugins/remotes/html/live.css
Normal file
39
openlp/plugins/remotes/html/live.css
Normal file
@ -0,0 +1,39 @@
|
||||
/******************************************************************************
|
||||
* OpenLP - Open Source Lyrics Projection *
|
||||
* --------------------------------------------------------------------------- *
|
||||
* Copyright (c) 2008-2013 Raoul Snyman *
|
||||
* Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan *
|
||||
* Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, *
|
||||
* Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. *
|
||||
* Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, *
|
||||
* Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, *
|
||||
* Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, *
|
||||
* Frode Woldsund, Martin Zibricky *
|
||||
* --------------------------------------------------------------------------- *
|
||||
* This program is free software; you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU General Public License as published by the Free *
|
||||
* Software Foundation; version 2 of the License. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT *
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for *
|
||||
* more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License along *
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59 *
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA *
|
||||
******************************************************************************/
|
||||
body {
|
||||
background-color: black;
|
||||
font-family: sans-serif;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.size {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
vertical-align: middle;
|
||||
height: 100%;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
}
|
41
openlp/plugins/remotes/html/live.html
Normal file
41
openlp/plugins/remotes/html/live.html
Normal file
@ -0,0 +1,41 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>${live_title}</title>
|
||||
<link rel="stylesheet" href="/files/live.css" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/files/images/favicon.ico">
|
||||
<script type="text/javascript" src="/files/jquery.js"></script>
|
||||
<script type="text/javascript" src="/files/live.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<img id="image" class="size"/>
|
||||
</body>
|
||||
</html>
|
52
openlp/plugins/remotes/html/live.js
Normal file
52
openlp/plugins/remotes/html/live.js
Normal file
@ -0,0 +1,52 @@
|
||||
/******************************************************************************
|
||||
* OpenLP - Open Source Lyrics Projection *
|
||||
* --------------------------------------------------------------------------- *
|
||||
* Copyright (c) 2008-2013 Raoul Snyman *
|
||||
* Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan *
|
||||
* Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, *
|
||||
* Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. *
|
||||
* Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, *
|
||||
* Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, *
|
||||
* Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, *
|
||||
* Frode Woldsund, Martin Zibricky *
|
||||
* --------------------------------------------------------------------------- *
|
||||
* This program is free software; you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU General Public License as published by the Free *
|
||||
* Software Foundation; version 2 of the License. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT *
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for *
|
||||
* more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License along *
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59 *
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA *
|
||||
******************************************************************************/
|
||||
window.OpenLP = {
|
||||
loadSlide: function (event) {
|
||||
$.getJSON(
|
||||
"/live/image",
|
||||
function (data, status) {
|
||||
var img = document.getElementById('image');
|
||||
img.src = data.results.slide_image;
|
||||
img.style.display = 'block';
|
||||
}
|
||||
);
|
||||
},
|
||||
pollServer: function () {
|
||||
$.getJSON(
|
||||
"/live/poll",
|
||||
function (data, status) {
|
||||
if (OpenLP.slideCount != data.results.slide_count) {
|
||||
OpenLP.slideCount = data.results.slide_count;
|
||||
OpenLP.loadSlide();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
$.ajaxSetup({ cache: false });
|
||||
setInterval("OpenLP.pollServer();", 500);
|
||||
OpenLP.pollServer();
|
||||
|
@ -124,7 +124,7 @@ import cherrypy
|
||||
from mako.template import Template
|
||||
from PyQt4 import QtCore
|
||||
|
||||
from openlp.core.lib import Registry, Settings, PluginStatus, StringContent
|
||||
from openlp.core.lib import Registry, Settings, PluginStatus, StringContent, image_to_byte
|
||||
from openlp.core.utils import AppLocation, translate
|
||||
|
||||
from cherrypy._cpcompat import sha, ntob
|
||||
@ -136,6 +136,7 @@ def make_sha_hash(password):
|
||||
"""
|
||||
Create an encrypted password for the given password.
|
||||
"""
|
||||
log.debug("make_sha_hash")
|
||||
return sha(ntob(password)).hexdigest()
|
||||
|
||||
|
||||
@ -143,6 +144,7 @@ def fetch_password(username):
|
||||
"""
|
||||
Fetch the password for a provided user.
|
||||
"""
|
||||
log.debug("Fetch Password")
|
||||
if username != Settings().value(u'remotes/user id'):
|
||||
return None
|
||||
return make_sha_hash(Settings().value(u'remotes/password'))
|
||||
@ -175,9 +177,11 @@ class HttpServer(object):
|
||||
self.root = self.Public()
|
||||
self.root.files = self.Files()
|
||||
self.root.stage = self.Stage()
|
||||
self.root.live = self.Live()
|
||||
self.root.router = self.router
|
||||
self.root.files.router = self.router
|
||||
self.root.stage.router = self.router
|
||||
self.root.live.router = self.router
|
||||
cherrypy.tree.mount(self.root, '/', config=self.define_config())
|
||||
# Turn off the flood of access messages cause by poll
|
||||
cherrypy.log.access_log.propagate = False
|
||||
@ -212,6 +216,9 @@ class HttpServer(object):
|
||||
u'tools.staticdir.dir': self.router.html_dir,
|
||||
u'tools.basic_auth.on': False},
|
||||
u'/stage': {u'tools.staticdir.on': True,
|
||||
u'tools.staticdir.dir': self.router.html_dir,
|
||||
u'tools.basic_auth.on': False},
|
||||
u'/live': {u'tools.staticdir.on': True,
|
||||
u'tools.staticdir.dir': self.router.html_dir,
|
||||
u'tools.basic_auth.on': False}}
|
||||
return directory_config
|
||||
@ -246,6 +253,15 @@ class HttpServer(object):
|
||||
url = urlparse.urlparse(cherrypy.url())
|
||||
return self.router.process_http_request(url.path, *args)
|
||||
|
||||
class Live(object):
|
||||
"""
|
||||
Live view is read only so security is not relevant and would reduce it's usability
|
||||
"""
|
||||
@cherrypy.expose
|
||||
def default(self, *args, **kwargs):
|
||||
url = urlparse.urlparse(cherrypy.url())
|
||||
return self.router.process_http_request(url.path, *args)
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
Close down the http server.
|
||||
@ -265,9 +281,12 @@ class HttpRouter(object):
|
||||
self.routes = [
|
||||
(u'^/$', self.serve_file),
|
||||
(u'^/(stage)$', self.serve_file),
|
||||
(u'^/(live)$', self.serve_file),
|
||||
(r'^/files/(.*)$', self.serve_file),
|
||||
(r'^/api/poll$', self.poll),
|
||||
(r'^/stage/poll$', self.poll),
|
||||
(r'^/live/poll$', self.live_poll),
|
||||
(r'^/live/image$', self.live_image),
|
||||
(r'^/api/controller/(live|preview)/(.*)$', self.controller),
|
||||
(r'^/stage/controller/(live|preview)/(.*)$', self.controller),
|
||||
(r'^/api/service/(.*)$', self.service),
|
||||
@ -305,6 +324,7 @@ class HttpRouter(object):
|
||||
if response:
|
||||
return response
|
||||
else:
|
||||
log.debug('Path not found %s', url_path)
|
||||
return self._http_not_found()
|
||||
|
||||
def _get_service_items(self):
|
||||
@ -334,6 +354,7 @@ class HttpRouter(object):
|
||||
self.template_vars = {
|
||||
'app_title': translate('RemotePlugin.Mobile', 'OpenLP 2.1 Remote'),
|
||||
'stage_title': translate('RemotePlugin.Mobile', 'OpenLP 2.1 Stage View'),
|
||||
'live_title': translate('RemotePlugin.Mobile', 'OpenLP 2.1 Live View'),
|
||||
'service_manager': translate('RemotePlugin.Mobile', 'Service Manager'),
|
||||
'slide_controller': translate('RemotePlugin.Mobile', 'Slide Controller'),
|
||||
'alerts': translate('RemotePlugin.Mobile', 'Alerts'),
|
||||
@ -359,18 +380,19 @@ class HttpRouter(object):
|
||||
|
||||
def serve_file(self, filename=None):
|
||||
"""
|
||||
Send a file to the socket. For now, just a subset of file types
|
||||
and must be top level inside the html folder.
|
||||
Send a file to the socket. For now, just a subset of file types and must be top level inside the html folder.
|
||||
If subfolders requested return 404, easier for security for the present.
|
||||
|
||||
Ultimately for i18n, this could first look for xx/file.html before
|
||||
falling back to file.html... where xx is the language, e.g. 'en'
|
||||
Ultimately for i18n, this could first look for xx/file.html before falling back to file.html.
|
||||
where xx is the language, e.g. 'en'
|
||||
"""
|
||||
log.debug(u'serve file request %s' % filename)
|
||||
if not filename:
|
||||
filename = u'index.html'
|
||||
elif filename == u'stage':
|
||||
filename = u'stage.html'
|
||||
elif filename == u'live':
|
||||
filename = u'live.html'
|
||||
path = os.path.normpath(os.path.join(self.html_dir, filename))
|
||||
if not path.startswith(self.html_dir):
|
||||
return self._http_not_found()
|
||||
@ -425,6 +447,26 @@ class HttpRouter(object):
|
||||
cherrypy.response.headers['Content-Type'] = u'application/json'
|
||||
return json.dumps({u'results': result})
|
||||
|
||||
def live_poll(self):
|
||||
"""
|
||||
Poll OpenLP to determine the current slide count.
|
||||
"""
|
||||
result = {
|
||||
u'slide_count': self.live_controller.slide_count
|
||||
}
|
||||
cherrypy.response.headers['Content-Type'] = u'application/json'
|
||||
return json.dumps({u'results': result})
|
||||
|
||||
def live_image(self):
|
||||
"""
|
||||
Return the latest display image as a byte stream.
|
||||
"""
|
||||
result = {
|
||||
u'slide_image': u'data:image/png;base64,' + str(image_to_byte(self.live_controller.slide_image))
|
||||
}
|
||||
cherrypy.response.headers['Content-Type'] = u'application/json'
|
||||
return json.dumps({u'results': result})
|
||||
|
||||
def display(self, action):
|
||||
"""
|
||||
Hide or show the display screen.
|
||||
|
@ -86,6 +86,12 @@ class RemoteTab(SettingsTab):
|
||||
self.stage_url.setObjectName(u'stage_url')
|
||||
self.stage_url.setOpenExternalLinks(True)
|
||||
self.http_setting_layout.addRow(self.stage_url_label, self.stage_url)
|
||||
self.live_url_label = QtGui.QLabel(self.http_settings_group_box)
|
||||
self.live_url_label.setObjectName(u'live_url_label')
|
||||
self.live_url = QtGui.QLabel(self.http_settings_group_box)
|
||||
self.live_url.setObjectName(u'live_url')
|
||||
self.live_url.setOpenExternalLinks(True)
|
||||
self.http_setting_layout.addRow(self.live_url_label, self.live_url)
|
||||
self.left_layout.addWidget(self.http_settings_group_box)
|
||||
self.https_settings_group_box = QtGui.QGroupBox(self.left_column)
|
||||
self.https_settings_group_box.setCheckable(True)
|
||||
@ -116,6 +122,12 @@ class RemoteTab(SettingsTab):
|
||||
self.stage_https_url.setObjectName(u'stage_https_url')
|
||||
self.stage_https_url.setOpenExternalLinks(True)
|
||||
self.https_settings_layout.addRow(self.stage_https_url_label, self.stage_https_url)
|
||||
self.live_https_url_label = QtGui.QLabel(self.https_settings_group_box)
|
||||
self.live_https_url_label.setObjectName(u'live_url_label')
|
||||
self.live_https_url = QtGui.QLabel(self.https_settings_group_box)
|
||||
self.live_https_url.setObjectName(u'live_https_url')
|
||||
self.live_https_url.setOpenExternalLinks(True)
|
||||
self.https_settings_layout.addRow(self.live_https_url_label, self.live_https_url)
|
||||
self.left_layout.addWidget(self.https_settings_group_box)
|
||||
self.user_login_group_box = QtGui.QGroupBox(self.left_column)
|
||||
self.user_login_group_box.setCheckable(True)
|
||||
@ -163,6 +175,7 @@ class RemoteTab(SettingsTab):
|
||||
self.port_label.setText(translate('RemotePlugin.RemoteTab', 'Port number:'))
|
||||
self.remote_url_label.setText(translate('RemotePlugin.RemoteTab', 'Remote URL:'))
|
||||
self.stage_url_label.setText(translate('RemotePlugin.RemoteTab', 'Stage view URL:'))
|
||||
self.live_url_label.setText(translate('RemotePlugin.RemoteTab', 'Live view URL:'))
|
||||
self.twelve_hour_check_box.setText(translate('RemotePlugin.RemoteTab', 'Display stage time in 12h format'))
|
||||
self.android_app_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'Android App'))
|
||||
self.qr_description_label.setText(translate('RemotePlugin.RemoteTab',
|
||||
@ -176,6 +189,7 @@ class RemoteTab(SettingsTab):
|
||||
self.https_port_label.setText(self.port_label.text())
|
||||
self.remote_https_url_label.setText(self.remote_url_label.text())
|
||||
self.stage_https_url_label.setText(self.stage_url_label.text())
|
||||
self.live_https_url_label.setText(self.live_url_label.text())
|
||||
self.user_login_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'User Authentication'))
|
||||
self.user_id_label.setText(translate('RemotePlugin.RemoteTab', 'User id:'))
|
||||
self.password_label.setText(translate('RemotePlugin.RemoteTab', 'Password:'))
|
||||
@ -203,10 +217,14 @@ class RemoteTab(SettingsTab):
|
||||
https_url = u'https://%s:%s/' % (ip_address, self.https_port_spin_box.value())
|
||||
self.remote_url.setText(u'<a href="%s">%s</a>' % (http_url, http_url))
|
||||
self.remote_https_url.setText(u'<a href="%s">%s</a>' % (https_url, https_url))
|
||||
http_url += u'stage'
|
||||
https_url += u'stage'
|
||||
self.stage_url.setText(u'<a href="%s">%s</a>' % (http_url, http_url))
|
||||
self.stage_https_url.setText(u'<a href="%s">%s</a>' % (https_url, https_url))
|
||||
http_url_temp = http_url + u'stage'
|
||||
https_url_temp = https_url + u'stage'
|
||||
self.stage_url.setText(u'<a href="%s">%s</a>' % (http_url_temp, http_url_temp))
|
||||
self.stage_https_url.setText(u'<a href="%s">%s</a>' % (https_url_temp, https_url_temp))
|
||||
http_url_temp = http_url + u'live'
|
||||
https_url_temp = https_url + u'live'
|
||||
self.live_url.setText(u'<a href="%s">%s</a>' % (http_url_temp, http_url_temp))
|
||||
self.live_https_url.setText(u'<a href="%s">%s</a>' % (https_url_temp, https_url_temp))
|
||||
|
||||
def load(self):
|
||||
"""
|
||||
|
Loading…
Reference in New Issue
Block a user