openlp-core team mailing list archive
-
openlp-core team
-
Mailing list archive
-
Message #01498
[Merge] lp:~j-corwin/openlp/remote into lp:openlp
Jonathan Corwin has proposed merging lp:~j-corwin/openlp/remote into lp:openlp.
Requested reviews:
OpenLP Core (openlp-core)
Control OpenLP via the web
No security at present, well other than switching the plugin off or securing the wifi...
--
https://code.launchpad.net/~j-corwin/openlp/remote/+merge/24548
Your team OpenLP Core is subscribed to branch lp:openlp.
=== modified file 'openlp/core/lib/eventreceiver.py'
--- openlp/core/lib/eventreceiver.py 2010-04-22 21:22:09 +0000
+++ openlp/core/lib/eventreceiver.py 2010-05-01 12:15:26 +0000
@@ -65,11 +65,29 @@
``slidecontroller_{live|preview}_last``
Moves to the last slide
+ ``slidecontroller_{live|preview}_set``
+ Moves to a specific slide, by index
+
``slidecontroller_{live|preview}_started``
Broadcasts that an item has been made live/previewed
``slidecontroller_{live|preview}_change``
- Informs the slidecontroller that a slide change has occurred
+ Informs the slidecontroller that a slide change has occurred and to
+ update itself
+
+ ``slidecontroller_{live|preview}_changed``
+ Broadcasts that the slidecontroller has changed the current slide
+
+ ``slidecontroller_{live|preview}_text_request``
+ Request the text for the current item in the controller
+ Returns a slidecontroller_{live|preview}_text_response with an
+ array of dictionaries with the tag and verse text
+
+ ``slidecontroller_{live|preview}_blank``
+ Request that the output screen is blanked
+
+ ``slidecontroller_{live|preview}_unblank``
+ Request that the output screen is unblanked
``slidecontroller_live_spin_delay``
Pushes out the loop delay
@@ -77,9 +95,19 @@
``slidecontroller_live_stop_loop``
Stop the loop on the main display
- ``servicecontroller_next_item``
+ ``servicemanager_previous_item``
+ Display the previous item in the service
+
+ ``servicemanager_next_item``
Display the next item in the service
+ ``servicemanager_set_item``
+ Go live on a specific item, by index
+
+ ``servicemanager_list_request``
+ Request the service list. Responds with servicemanager_list_response
+ containing a array of dictionaries
+
``maindisplay_blank``
Blank the maindisplay window
@@ -110,6 +138,9 @@
``videodisplay_stop``
Stop playing a media item
+ ``videodisplay_background``
+ Replace the background video
+
``theme_update_list``
send out message with new themes
@@ -174,6 +205,10 @@
``bibles_stop_import``
Stops the Bible Import
+ ``remotes_poll_request``
+ Waits for openlp to do something "interesting" and sends a
+ remotes_poll_response signal when it does
+
"""
def __init__(self):
"""
=== modified file 'openlp/core/ui/servicemanager.py'
--- openlp/core/ui/servicemanager.py 2010-04-30 22:38:15 +0000
+++ openlp/core/ui/servicemanager.py 2010-05-01 12:15:26 +0000
@@ -188,6 +188,12 @@
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'servicemanager_next_item'), self.nextItem)
QtCore.QObject.connect(Receiver.get_receiver(),
+ QtCore.SIGNAL(u'servicemanager_previous_item'), self.previousItem)
+ QtCore.QObject.connect(Receiver.get_receiver(),
+ QtCore.SIGNAL(u'servicemanager_set_item'), self.onSetItem)
+ QtCore.QObject.connect(Receiver.get_receiver(),
+ QtCore.SIGNAL(u'servicemanager_list_request'), self.listRequest)
+ QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'config_updated'), self.regenerateServiceItems)
# Last little bits of setting up
self.service_theme = unicode(QtCore.QSettings().value(
@@ -289,6 +295,41 @@
lookFor = 1
serviceIterator += 1
+ def previousItem(self):
+ """
+ Called by the SlideController to select the
+ previous service item
+ """
+ if len(self.ServiceManagerList.selectedItems()) == 0:
+ return
+ selected = self.ServiceManagerList.selectedItems()[0]
+ prevItem = None
+ serviceIterator = QtGui.QTreeWidgetItemIterator(self.ServiceManagerList)
+ while serviceIterator.value():
+ if serviceIterator.value() == selected:
+ if prevItem:
+ self.ServiceManagerList.setCurrentItem(prevItem)
+ self.makeLive()
+ return
+ if serviceIterator.value().parent() is None:
+ prevItem = serviceIterator.value()
+ serviceIterator += 1
+
+ def onSetItem(self, message):
+ """
+ Called by a signal to select a specific item
+ """
+ self.setItem(int(message[0]))
+
+ def setItem(self, index):
+ """
+ Makes a specific item in the service live
+ """
+ if index >= 0 and index < self.ServiceManagerList.topLevelItemCount:
+ item = self.ServiceManagerList.topLevelItem(index)
+ self.ServiceManagerList.setCurrentItem(item)
+ self.makeLive()
+
def onMoveSelectionUp(self):
"""
Moves the selection up the window
@@ -855,3 +896,20 @@
return item.data(0, QtCore.Qt.UserRole).toInt()[0]
else:
return parentitem.data(0, QtCore.Qt.UserRole).toInt()[0]
+
+ def listRequest(self, message=None):
+ data = []
+ curindex, count = self.findServiceItem()
+ if curindex >= 0 and curindex < len(self.serviceItems):
+ curitem = self.serviceItems[curindex]
+ else:
+ curitem = None
+ for item in self.serviceItems:
+ service_item = item[u'service_item']
+ data_item = {}
+ data_item[u'title'] = unicode(service_item.title)
+ data_item[u'plugin'] = unicode(service_item.name)
+ data_item[u'notes'] = unicode(service_item.notes)
+ data_item[u'selected'] = (item == curitem)
+ data.append(data_item)
+ Receiver.send_message(u'servicemanager_list_response', data)
=== modified file 'openlp/core/ui/slidecontroller.py'
--- openlp/core/ui/slidecontroller.py 2010-04-30 22:38:15 +0000
+++ openlp/core/ui/slidecontroller.py 2010-05-01 12:15:26 +0000
@@ -338,6 +338,18 @@
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'slidecontroller_%s_change' % self.type_prefix),
self.onSlideChange)
+ QtCore.QObject.connect(Receiver.get_receiver(),
+ QtCore.SIGNAL(u'slidecontroller_%s_set' % self.type_prefix),
+ self.onSlideSelectedIndex)
+ QtCore.QObject.connect(Receiver.get_receiver(),
+ QtCore.SIGNAL(u'slidecontroller_%s_blank' % self.type_prefix),
+ self.onSlideBlank)
+ QtCore.QObject.connect(Receiver.get_receiver(),
+ QtCore.SIGNAL(u'slidecontroller_%s_unblank' % self.type_prefix),
+ self.onSlideUnblank)
+ QtCore.QObject.connect(Receiver.get_receiver(),
+ QtCore.SIGNAL(u'slidecontroller_%s_text_request' % self.type_prefix),
+ self.onTextRequest)
QtCore.QObject.connect(self.Splitter,
QtCore.SIGNAL(u'splitterMoved(int, int)'), self.trackSplitter)
QtCore.QObject.connect(Receiver.get_receiver(),
@@ -556,12 +568,30 @@
self.enableToolBar(serviceItem)
self.onSlideSelected()
self.PreviewListWidget.setFocus()
- Receiver.send_message(u'%s_%s_started' %
- (self.serviceItem.name.lower(),
- 'live' if self.isLive else 'preview'),
+ Receiver.send_message(u'slidecontroller_%s_started' % self.type_prefix,
[serviceItem])
log.log(15, u'Display Rendering took %4s' % (time.time() - before))
+ def onTextRequest(self):
+ """
+ Return the text for the current item in controller
+ """
+ data = []
+ if self.serviceItem:
+ for framenumber, frame in enumerate(self.serviceItem.get_frames()):
+ data_item = {}
+ if self.serviceItem.is_text():
+ data_item[u'tag'] = unicode(frame[u'verseTag'])
+ data_item[u'text'] = unicode(frame[u'text'])
+ else:
+ data_item[u'tag'] = unicode(framenumber)
+ data_item[u'text'] = u''
+ data_item[u'selected'] = \
+ (self.PreviewListWidget.currentRow() == framenumber)
+ data.append(data_item)
+ Receiver.send_message(u'slidecontroller_%s_text_response'
+ % self.type_prefix, data)
+
#Screen event methods
def onSlideSelectedFirst(self):
"""
@@ -577,6 +607,33 @@
self.PreviewListWidget.selectRow(0)
self.onSlideSelected()
+ def onSlideSelectedIndex(self, message):
+ """
+ Go to the requested slide
+ """
+ index = int(message[0])
+ if not self.serviceItem:
+ return
+ Receiver.send_message(u'%s_slide' % self.serviceItem.name.lower(),
+ [self.serviceItem, self.isLive, index])
+ if self.serviceItem.is_command():
+ self.updatePreview()
+ else:
+ self.PreviewListWidget.selectRow(index)
+ self.onSlideSelected()
+
+ def onSlideBlank(self):
+ """
+ Handle the slidecontroller blank event
+ """
+ self.onBlankDisplay(True)
+
+ def onSlideUnblank(self):
+ """
+ Handle the slidecontroller unblank event
+ """
+ self.onBlankDisplay(False)
+
def onBlankDisplay(self, checked):
"""
Handle the blank screen button
@@ -665,6 +722,8 @@
if self.isLive:
self.mainDisplay.frameView(frame, True)
self.selectedRow = row
+ Receiver.send_message(u'slidecontroller_%s_changed' % self.type_prefix,
+ row)
def onSlideChange(self, row):
"""
@@ -672,6 +731,8 @@
"""
self.PreviewListWidget.selectRow(row)
self.updatePreview()
+ Receiver.send_message(u'slidecontroller_%s_changed' % self.type_prefix,
+ row)
def updatePreview(self):
rm = self.parent.RenderManager
=== modified file 'openlp/plugins/alerts/lib/alertsmanager.py'
--- openlp/plugins/alerts/lib/alertsmanager.py 2010-04-21 21:56:48 +0000
+++ openlp/plugins/alerts/lib/alertsmanager.py 2010-05-01 12:15:26 +0000
@@ -46,7 +46,7 @@
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'maindisplay_active'), self.generateAlert)
QtCore.QObject.connect(Receiver.get_receiver(),
- QtCore.SIGNAL(u'alerts_text'), self.displayAlert)
+ QtCore.SIGNAL(u'alerts_text'), self.onAlertText)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'config_screen_changed'), self.screenChanged)
@@ -70,6 +70,16 @@
self.parent.maindisplay.setAlertSize(self.alertScreenPosition,\
self.alertHeight)
+ def onAlertText(self, message):
+ """
+ Called via a alerts_text event. Message is single element array
+ containing text
+ """
+ if message:
+ self.displayAlert(message[0])
+ else:
+ self.displayAlert(u'')
+
def displayAlert(self, text=u''):
"""
Called from the Alert Tab to display an alert
=== added directory 'openlp/plugins/remotes/html'
=== added file 'openlp/plugins/remotes/html/index.html'
--- openlp/plugins/remotes/html/index.html 1970-01-01 00:00:00 +0000
+++ openlp/plugins/remotes/html/index.html 2010-05-01 12:15:26 +0000
@@ -0,0 +1,117 @@
+<html>
+<head>
+<title>OpenLP Controller</title>
+<script type='text/javascript'>
+
+function send_event(eventname, data){
+ var req = new XMLHttpRequest();
+ req.onreadystatechange = function() {
+ if(req.readyState==4)
+ response(eventname, req);
+ }
+ var url = '';
+ if(eventname.substr(-8) == '_request')
+ url = 'request';
+ else
+ url = 'send';
+ url += '/' + eventname;
+ if(data!=null)
+ url += '?q=' + escape(data);
+ req.open('GET', url, true);
+ req.send();
+}
+function failed_response(eventname, req){
+ switch(eventname){
+ case 'remotes_poll_request':
+ if(req.status==408)
+ send_event("remotes_poll_request");
+ break;
+ }
+}
+function response(eventname, req){
+ if(req.status!=200){
+ failed_response(eventname, req);
+ return;
+ }
+ text = req.responseText;
+ switch(eventname){
+ case 'servicemanager_list_request':
+ var data = eval('(' + text + ')');
+ var html = '<table>';
+ for(row in data){
+ html += '<tr onclick="send_event('
+ html += "'servicemanager_set_item', " + row + ')"';
+ if(data[row]['selected'])
+ html += ' style="font-weight: bold"';
+ html += '>'
+ html += '<td>' + (parseInt(row)+1) + '</td>'
+ html += '<td>' + data[row]['title'] + '</td>'
+ html += '<td>' + data[row]['plugin'] + '</td>'
+ html += '<td>' + data[row]['notes'] + '</td>'
+ html += '</tr>';
+ }
+ html += '</table>';
+ document.getElementById('service').innerHTML = html;
+ break;
+ case 'slidecontroller_live_text_request':
+ var data = eval('(' + text + ')');
+ var html = '<table>';
+ for(row in data){
+ html += '<tr onclick="send_event('
+ html += "'slidecontroller_live_set', " + row + ')"';
+ if(data[row]['selected'])
+ html += ' style="font-weight: bold"';
+ html += '>';
+ html += '<td>' + data[row]['tag'] + '</td>';
+ html += '<td>' + data[row]['text'].replace(/\\n/g, '<br>');
+ html += '</td></tr>';
+ }
+ html += '</table>';
+ document.getElementById('currentitem').innerHTML = html;
+ break;
+ case 'remotes_poll_request':
+ send_event("remotes_poll_request");
+ send_event("servicemanager_list_request");
+ send_event("slidecontroller_live_text_request");
+ break;
+ }
+}
+send_event("servicemanager_list_request");
+send_event("slidecontroller_live_text_request");
+send_event("remotes_poll_request");
+</script>
+</head>
+<body>
+ <h1>OpenLP Controller</h1>
+ <input type='button' value='<- Previous Slide'
+ onclick='send_event("slidecontroller_live_previous");' />
+ <input type='button' value='Next Slide ->'
+ onclick='send_event("slidecontroller_live_next");' />
+ <br/>
+ <input type='button' value='<- Previous Item'
+ onclick='send_event("servicemanager_previous_item");' />
+ <input type='button' value='Next Item ->'
+ onclick='send_event("servicemanager_next_item");' />
+ <br/>
+ <input type='button' value='Blank'
+ onclick='send_event("slidecontroller_live_blank");' />
+ <input type='button' value='Unblank'
+ onclick='send_event("slidecontroller_live_unblank");' />
+ <br/>
+ <label>Alert text</label><input id='alert' type='text' />
+ <input type='button' value='Send'
+ onclick='send_event("alerts_text",
+ document.getElementById("alert").value);' />
+ <hr>
+ <input type='button' value='Order of service'
+ onclick='send_event("servicemanager_list_request");'>
+ <div id='service'></div>
+ <hr>
+ <input type='button' value='Current item'
+ onclick='send_event("slidecontroller_live_text_request");'>
+ <div id='currentitem'></div>
+ <hr>
+ <a href="http://www.openlp.org/">OpenLP website</a>
+</body>
+</html>
+
=== modified file 'openlp/plugins/remotes/lib/__init__.py'
--- openlp/plugins/remotes/lib/__init__.py 2010-03-21 23:58:01 +0000
+++ openlp/plugins/remotes/lib/__init__.py 2010-05-01 12:15:26 +0000
@@ -24,3 +24,4 @@
###############################################################################
from remotetab import RemoteTab
+from httpserver import HttpServer
=== added file 'openlp/plugins/remotes/lib/httpserver.py'
--- openlp/plugins/remotes/lib/httpserver.py 1970-01-01 00:00:00 +0000
+++ openlp/plugins/remotes/lib/httpserver.py 2010-05-01 12:15:26 +0000
@@ -0,0 +1,319 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2010 Raoul Snyman #
+# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
+# Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin #
+# Thompson, Jon Tibble, Carsten Tinggaard #
+# --------------------------------------------------------------------------- #
+# 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 #
+###############################################################################
+
+import logging
+import os
+import json
+import urlparse
+
+from PyQt4 import QtCore, QtNetwork
+
+from openlp.core.lib import Receiver
+from openlp.core.utils import AppLocation
+
+log = logging.getLogger(__name__)
+
+class HttpServer(object):
+ """
+ Ability to control OpenLP via a webbrowser
+ e.g. http://localhost:4316/send/slidecontroller_live_next
+ http://localhost:4316/send/alerts_text?q=your%20alert%20text
+ """
+ def __init__(self, parent):
+ """
+ Initialise the httpserver, and start the server
+ """
+ log.debug(u'Initialise httpserver')
+ self.parent = parent
+ self.html_dir = os.path.join(
+ AppLocation.get_directory(AppLocation.PluginsDir),
+ u'remotes', u'html')
+ self.connections = []
+ self.current_item = None
+ self.current_slide = None
+ self.start_tcp()
+
+ def start_tcp(self):
+ """
+ Start the http server, use the port in the settings default to 4316
+ Listen out for slide and song changes so they can be broadcast to
+ clients. Listen out for socket connections
+ """
+ log.debug(u'Start TCP server')
+ port = QtCore.QSettings().value(
+ self.parent.settingsSection + u'/remote port',
+ QtCore.QVariant(4316)).toInt()[0]
+ self.server = QtNetwork.QTcpServer()
+ self.server.listen(QtNetwork.QHostAddress(QtNetwork.QHostAddress.Any),
+ port)
+ QtCore.QObject.connect(Receiver.get_receiver(),
+ QtCore.SIGNAL(u'slidecontroller_live_changed'),
+ self.slide_change)
+ QtCore.QObject.connect(Receiver.get_receiver(),
+ QtCore.SIGNAL(u'slidecontroller_live_started'),
+ self.item_change)
+ QtCore.QObject.connect(self.server,
+ QtCore.SIGNAL(u'newConnection()'), self.new_connection)
+ log.debug(u'TCP listening on port %d' % port)
+
+ def slide_change(self, row):
+ """
+ Slide change listener. Store the item and tell the clients
+ """
+ self.current_slide = row
+ self.send_poll()
+
+ def item_change(self, items):
+ """
+ Item (song) change listener. Store the slide and tell the clients
+ """
+ self.current_item = items[0].title
+ self.send_poll()
+
+ def send_poll(self):
+ """
+ Tell the clients something has changed
+ """
+ Receiver.send_message(u'remotes_poll_response',
+ {'slide': self.current_slide,
+ 'item': self.current_item})
+
+ def new_connection(self):
+ """
+ A new http connection has been made. Create a client object to handle
+ communication
+ """
+ log.debug(u'new http connection')
+ socket = self.server.nextPendingConnection()
+ if socket:
+ self.connections.append(HttpConnection(self, socket))
+
+ def close_connection(self, connection):
+ """
+ The connection has been closed. Clean up
+ """
+ log.debug(u'close http connection')
+ self.connections.remove(connection)
+
+ def close(self):
+ """
+ Close down the http server
+ """
+ log.debug(u'close http server')
+ self.server.close()
+
+class HttpConnection(object):
+ """
+ A single connection, this handles communication between the server
+ and the client
+ """
+ def __init__(self, parent, socket):
+ """
+ Initialise the http connection. Listen out for socket signals
+ """
+ log.debug(u'Initialise HttpConnection: %s' %
+ socket.peerAddress().toString())
+ self.socket = socket
+ self.parent = parent
+ QtCore.QObject.connect(self.socket, QtCore.SIGNAL(u'readyRead()'),
+ self.ready_read)
+ QtCore.QObject.connect(self.socket, QtCore.SIGNAL(u'disconnected()'),
+ self.disconnected)
+
+ def ready_read(self):
+ """
+ Data has been sent from the client. Respond to it
+ """
+ log.debug(u'ready to read socket')
+ if self.socket.canReadLine():
+ data = unicode(self.socket.readLine())
+ log.debug(u'received: ' + data)
+ words = data.split(u' ')
+ html = None
+ if words[0] == u'GET':
+ url = urlparse.urlparse(words[1])
+ params = self.load_params(url.query)
+ folders = url.path.split(u'/')
+ if folders[1] == u'':
+ html = self.serve_file(u'')
+ elif folders[1] == u'files':
+ html = self.serve_file(folders[2])
+ elif folders[1] == u'send':
+ html = self.process_event(folders[2], params)
+ elif folders[1] == u'request':
+ if self.process_request(folders[2], params):
+ return
+ if html:
+ html = self.get_200_ok() + html + u'\n'
+ else:
+ html = self.get_404_not_found()
+ self.socket.write(html)
+ self.close()
+
+ def serve_file(self, filename):
+ """
+ Send a file to the socket. For now, just .html files
+ 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'
+ """
+ log.debug(u'serve file request %s' % filename)
+ if not filename:
+ filename = u'index.html'
+ if os.path.basename(filename) != filename:
+ return None
+ (fileroot, ext) = os.path.splitext(filename)
+ if ext != u'.html':
+ return None
+ path = os.path.join(self.parent.html_dir, filename)
+ try:
+ f = open(path, u'rb')
+ except:
+ log.exception(u'Failed to open %s' % path)
+ return None
+ log.debug(u'Opened %s' % path)
+ html = f.read()
+ f.close()
+ return html
+
+ def load_params(self, query):
+ """
+ Decode the query string parameters sent from the browser
+ """
+ params = urlparse.parse_qs(query)
+ if not params:
+ return None
+ else:
+ return params['q']
+
+ def process_event(self, event, params):
+ """
+ Send a signal to openlp to perform an action.
+ Currently lets anything through. Later we should restrict and perform
+ basic parameter checking, otherwise rogue clients could crash openlp
+ """
+ if params:
+ Receiver.send_message(event, params)
+ else:
+ Receiver.send_message(event)
+ return u'OK'
+
+ def process_request(self, event, params):
+ """
+ Client has requested data. Send the signal and parameters for openlp
+ to handle, then listen out for a corresponding _request signal
+ which will have the data to return.
+ For most event timeout after 10 seconds (i.e. incase the signal
+ recipient isn't listening)
+ remotes_poll_request is a special case, this is a ajax long poll which
+ is just waiting for slide change/song change activity. This can wait
+ longer (one minute)
+ """
+ if not event.endswith(u'_request'):
+ return False
+ self.event = event
+ response = event.replace(u'_request', u'_response')
+ QtCore.QObject.connect(Receiver.get_receiver(),
+ QtCore.SIGNAL(response), self.process_response)
+ self.timer = QtCore.QTimer()
+ self.timer.setSingleShot(True)
+ QtCore.QObject.connect(self.timer,
+ QtCore.SIGNAL(u'timeout()'), self.timeout)
+ if event == 'remotes_poll_request':
+ self.timer.start(60000)
+ else:
+ self.timer.start(10000)
+ if params:
+ Receiver.send_message(event, params)
+ else:
+ Receiver.send_message(event)
+ return True
+
+ def process_response(self, data):
+ """
+ The recipient of a _request signal has sent data. Convert this to
+ json and return it to client
+ """
+ if not self.socket:
+ return
+ self.timer.stop()
+ html = json.dumps(data)
+ html = self.get_200_ok() + html + u'\n'
+ self.socket.write(html)
+ self.close()
+
+ def get_200_ok(self):
+ """
+ Successful request. Send OK headers. Assume html for now.
+ """
+ return u'HTTP/1.1 200 OK\r\n' + \
+ u'Content-Type: text/html; charset="utf-8"\r\n' + \
+ u'\r\n'
+
+ def get_404_not_found(self):
+ """
+ Invalid url. Say so
+ """
+ return u'HTTP/1.1 404 Not Found\r\n'+ \
+ u'Content-Type: text/html; charset="utf-8"\r\n' + \
+ u'\r\n'
+
+ def get_408_timeout(self):
+ """
+ A _request hasn't returned anything in the timeout period.
+ Return timeout
+ """
+ return u'HTTP/1.1 408 Request Timeout\r\n'
+
+ def timeout(self):
+ """
+ Listener for timeout signal
+ """
+ if not self.socket:
+ return
+ html = self.get_408_timeout()
+ self.socket.write(html)
+ self.close()
+
+ def disconnected(self):
+ """
+ The client has disconnected. Tidy up
+ """
+ log.debug(u'socket disconnected')
+ self.close()
+
+ def close(self):
+ """
+ The server has closed the connection. Tidy up
+ """
+ if not self.socket:
+ return
+ log.debug(u'close socket')
+ self.socket.close()
+ self.socket = None
+ self.parent.close_connection(self)
+
=== modified file 'openlp/plugins/remotes/remoteplugin.py'
--- openlp/plugins/remotes/remoteplugin.py 2010-04-30 22:38:15 +0000
+++ openlp/plugins/remotes/remoteplugin.py 2010-05-01 12:15:26 +0000
@@ -28,7 +28,7 @@
from PyQt4 import QtNetwork, QtCore
from openlp.core.lib import Plugin, Receiver
-from openlp.plugins.remotes.lib import RemoteTab
+from openlp.plugins.remotes.lib import RemoteTab, HttpServer
log = logging.getLogger(__name__)
@@ -36,22 +36,26 @@
log.info(u'Remote Plugin loaded')
def __init__(self, plugin_helpers):
+ """
+ remotes constructor
+ """
Plugin.__init__(self, u'Remotes', u'1.9.1', plugin_helpers)
self.weight = -1
self.server = None
def initialise(self):
+ """
+ Initialise the remotes plugin, and start the http server
+ """
log.debug(u'initialise')
Plugin.initialise(self)
self.insert_toolbox_item()
- self.server = QtNetwork.QUdpSocket()
- self.server.bind(
- QtCore.QSettings().value(self.settingsSection + u'/remote port',
- QtCore.QVariant(4316)).toInt()[0])
- QtCore.QObject.connect(self.server,
- QtCore.SIGNAL(u'readyRead()'), self.readData)
+ self.server = HttpServer(self)
def finalise(self):
+ """
+ Tidy up and close down the http server
+ """
log.debug(u'finalise')
self.remove_toolbox_item()
if self.server:
@@ -62,28 +66,13 @@
Create the settings Tab
"""
return RemoteTab(self.name)
-
- def readData(self):
- log.info(u'Remoted data has arrived')
- while self.server.hasPendingDatagrams():
- datagram, host, port = self.server.readDatagram(
- self.server.pendingDatagramSize())
- self.handle_datagram(datagram)
-
- def handle_datagram(self, datagram):
- log.info(u'Sending event %s ', datagram)
- pos = datagram.find(u':')
- event = unicode(datagram[:pos].lower())
- if event == u'alert':
- Receiver.send_message(u'alerts_text', unicode(datagram[pos + 1:]))
- elif event == u'next_slide':
- Receiver.send_message(u'slidecontroller_live_next')
- else:
- Receiver.send_message(event, unicode(datagram[pos + 1:]))
def about(self):
+ """
+ Information about this plugin
+ """
about_text = self.trUtf8('<b>Remote Plugin</b><br>This plugin '
'provides the ability to send messages to a running version of '
- 'openlp on a different computer.<br>The Primary use for this '
- 'would be to send alerts from a creche')
+ 'openlp on a different computer via a web browser or other app<br>'
+ 'The Primary use for this would be to send alerts from a creche')
return about_text
=== modified file 'openlp/plugins/songusage/songusageplugin.py'
--- openlp/plugins/songusage/songusageplugin.py 2010-04-30 22:38:15 +0000
+++ openlp/plugins/songusage/songusageplugin.py 2010-05-01 12:15:26 +0000
@@ -108,7 +108,7 @@
log.info(u'SongUsage Initialising')
Plugin.initialise(self)
QtCore.QObject.connect(Receiver.get_receiver(),
- QtCore.SIGNAL(u'slidecontroller_live_started'),
+ QtCore.SIGNAL(u'songs_live_started'),
self.onReceiveSongUsage)
self.SongUsageActive = QtCore.QSettings().value(
self.settingsSection + u'/active',
=== modified file 'scripts/openlp-remoteclient.py'
--- scripts/openlp-remoteclient.py 2010-04-09 19:15:26 +0000
+++ scripts/openlp-remoteclient.py 2010-05-01 12:15:26 +0000
@@ -24,34 +24,33 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
-import socket
+import urllib
import sys
from optparse import OptionParser
-def sendData(options, message):
- addr = (options.address, options.port)
+def sendData(options):
+ addr = 'http://%s:%s/send/%s?q=%s' % (options.address, options.port,
+ options.event, options.message)
try:
- UDPSock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
- UDPSock.sendto(message, addr)
- print u'message sent ', message, addr
+ urllib.urlopen(addr)
+ print u'Message sent ', addr
except:
- print u'Errow thrown ', sys.exc_info()[1]
-
-def format_message(options):
- return u'%s:%s' % (u'alert', options.message)
+ print u'Error thrown ', sys.exc_info()[1]
def main():
- usage = "usage: %prog [options] arg1 arg2"
+ usage = "usage: %prog [-a address] [-p port] [-e event] [-m message]"
parser = OptionParser(usage=usage)
- parser.add_option("-v", "--verbose",
- action="store_true", dest="verbose", default=True,
- help="make lots of noise [%default]")
parser.add_option("-p", "--port", default=4316,
help="IP Port number %default ")
parser.add_option("-a", "--address",
- help="Recipient address ")
+ help="Recipient address ",
+ default="localhost")
+ parser.add_option("-e", "--event",
+ help="Action to be performed",
+ default="alerts_text")
parser.add_option("-m", "--message",
- help="Message to be passed for the action")
+ help="Message to be passed for the action",
+ default="")
(options, args) = parser.parse_args()
if args:
@@ -60,12 +59,8 @@
elif options.address is None:
parser.print_help()
parser.error("IP address missing")
- elif options.message is None:
- parser.print_help()
- parser.error("No message passed")
else:
- text = format_message(options)
- sendData(options, text)
+ sendData(options)
if __name__ == u'__main__':
main()
Follow ups