Remote song search and go live beginnings

This commit is contained in:
Jonathan Corwin 2011-05-14 10:48:58 +01:00
parent ae2d8c6961
commit 047287950c
7 changed files with 163 additions and 16 deletions

View File

@ -475,11 +475,18 @@ class MediaManagerItem(QtGui.QWidget):
translate('OpenLP.MediaManagerItem',
'You must select one or more items to send live.'))
else:
log.debug(u'%s Live requested', self.plugin.name)
serviceItem = self.buildServiceItem()
if serviceItem:
serviceItem.from_plugin = True
self.parent.liveController.addServiceItem(serviceItem)
self.goLive()
def goLive(self, item_id=None):
log.debug(u'%s Live requested', self.plugin.name)
item = None
if item_id:
item = QtGui.QListWidgetItem()
item.setData(QtCore.Qt.UserRole, QtCore.QVariant(item_id))
serviceItem = self.buildServiceItem(item)
if serviceItem:
serviceItem.from_plugin = True
self.parent.liveController.addServiceItem(serviceItem)
def onAddClick(self):
"""
@ -573,3 +580,16 @@ class MediaManagerItem(QtGui.QWidget):
else:
item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0]
return item_id
def hasSearch(self):
"""
Returns whether this plugin supports the search method
"""
return False
def search(self, string):
"""
Performs a plugin specific search for items containing ``string``
"""
raise NotImplementedError(
u'Plugin.about needs to be defined by the plugin')

View File

@ -45,7 +45,7 @@ class PluginManager(object):
"""
The constructor for the plugin manager. Passes the controllers on to
the plugins for them to interact with via their ServiceItems.
pluginmanager.py
``plugin_dir``
The directory to search for plugins.
"""
@ -211,3 +211,12 @@ class PluginManager(object):
if plugin.isActive():
plugin.finalise()
log.info(u'Finalisation Complete for %s ' % plugin.name)
def get_plugin_by_name(self, name):
"""
Return the plugin which has a name with value ``name``
"""
for plugin in self.plugins:
if plugin.name == name:
return plugin
return None

View File

@ -43,6 +43,7 @@
<a href="#service-manager" data-role="button" data-icon="arrow-r" data-iconpos="right">Service Manager</a>
<a href="#slide-controller" data-role="button" data-icon="arrow-r" data-iconpos="right">Slide Controller</a>
<a href="#alerts" data-role="button" data-icon="arrow-r" data-iconpos="right">Alerts</a>
<a href="#search" data-role="button" data-icon="arrow-r" data-iconpos="right">Search</a>
</div>
</div>
</div>
@ -55,7 +56,7 @@
<ul data-role="listview" data-inset="true">
</ul>
</div>
<div data-role="footer" data-theme="b" class="ui-bar">
<div data-role="footer" data-position="fixed" data-theme="b" class="ui-bar">
<a href="#" id="service-blank" data-role="button" data-icon="blank">Blank</a>
<a href="#" id="service-unblank" data-role="button" data-icon="unblank">Unblank</a>
<a href="#" id="service-refresh" data-role="button" data-icon="refresh">Refresh</a>
@ -72,7 +73,7 @@
<ul data-role="listview" data-inset="true">
</ul>
</div>
<div data-role="footer" data-theme="b" class="ui-bar">
<div data-role="footer" data-position="fixed" data-theme="b" class="ui-bar">
<a href="#" id="controller-blank" data-role="button" data-icon="blank">Blank</a>
<a href="#" id="controller-unblank" data-role="button" data-icon="unblank">Unblank</a>
<a href="#" id="controller-refresh" data-role="button" data-icon="refresh">Refresh</a>
@ -93,5 +94,19 @@
<a href="#" id="alert-submit" data-role="button">Show Alert</a>
</div>
</div>
<div data-role="page" id="search">
<div data-role="header">
<a href="#" data-rel="back" data-icon="arrow-l">Back</a>
<h1>Search</h1>
</div>
<div data-role="content">
<div data-role="fieldcontain">
<label for="search-text">Search:</label>
<input type="text" name="search-text" id="search-text" value="" />
</div>
<a href="#" id="search-submit" data-role="button">Search</a>
<ul data-role="listview" data-inset="true">
</div>
</div>
</body>
</html>

View File

@ -189,7 +189,43 @@ window.OpenLP = {
}
);
return false;
},
search: function (event) {
var text = JSON.stringify({"request": {"text": $("#search-text").val()}});
$.getJSON(
"/api/Songs/search",
{"data": text},
function (data, status) {
var ul = $("#search > div[data-role=content] > ul[data-role=listview]");
ul.html("");
if (data.results.items.length == 0) {
var li = $("<li data-icon=\"false\">").text('No results');
ul.append(li);
}
else {
$.each(data.results.items, function (idx, value) {
var li = $("<li data-icon=\"false\">").append(
$("<a href=\"#\">").attr("value", value[0]).text(value[1]));
li.children("a").click(OpenLP.goLive);
ul.append(li);
});
}
ul.listview("refresh");
}
);
return false;
},
goLive: function (event) {
var slide = OpenLP.getElement(event);
var id = slide.attr("value");
var text = JSON.stringify({"request": {"id": id}});
$.getJSON(
"/api/Songs/live",
{"data": text})
window.location.replace('/#slide-controller');
return false;
}
}
// Service Manager
$("#service-manager").live("pagebeforeshow", OpenLP.loadService);
@ -207,6 +243,8 @@ $("#controller-blank").live("click", OpenLP.blankDisplay);
$("#controller-unblank").live("click", OpenLP.unblankDisplay);
// Alerts
$("#alert-submit").live("click", OpenLP.showAlert);
// Search
$("#search-submit").live("click", OpenLP.search);
// Poll the server twice a second to get any updates.
$.ajaxSetup({ cache: false });
setInterval("OpenLP.pollServer();", 500);

View File

@ -250,7 +250,10 @@ class HttpConnection(object):
(r'^/api/controller/(live|preview)/(.*)$', self.controller),
(r'^/api/service/(.*)$', self.service),
(r'^/api/display/(hide|show)$', self.display),
(r'^/api/alert$', self.alert)
(r'^/api/alert$', self.alert),
(r'^/api/plugin/(search)$', self.plugin),
(r'^/api/(.*)/search$', self.search),
(r'^/api/(.*)/live$', self.go_live)
]
QtCore.QObject.connect(self.socket, QtCore.SIGNAL(u'readyRead()'),
self.ready_read)
@ -443,6 +446,49 @@ class HttpConnection(object):
return HttpResponse(json.dumps({u'results': {u'success': True}}),
{u'Content-Type': u'application/json'})
def plugin(self, action):
"""
Return plugin related actions
``action`` - The action to perform
if 'search' return a list of plugin names which support search
"""
if action == u'search':
searches = []
for plugin in self.parent.parent.pluginManager.plugins:
media_item = plugin.mediaItem
if media_item and media_item.hasSearch():
searches.append(plugin.Name)
return HttpResponse(
json.dumps({u'results': {u'items': searches}}),
{u'Content-Type': u'application/json'})
def search(self, type):
"""
Return a list of items that match the search text
``type``
The plugin name to search in.
"""
text = json.loads(self.url_params[u'data'][0])[u'request'][u'text']
plugin = self.parent.parent.pluginManager.get_plugin_by_name(type)
media_item = plugin.mediaItem
if media_item and media_item.hasSearch():
results = media_item.search(text)
return HttpResponse(
json.dumps({u'results': {u'items': results}}),
{u'Content-Type': u'application/json'})
def go_live(self, type):
"""
Go live on an item of type ``type``.
"""
id = json.loads(self.url_params[u'data'][0])[u'request'][u'id']
plugin = self.parent.parent.pluginManager.get_plugin_by_name(type)
media_item = plugin.mediaItem
if media_item:
media_item.goLive(id)
def send_response(self, response):
http = u'HTTP/1.1 %s\r\n' % response.code
for header, value in response.headers.iteritems():

View File

@ -171,11 +171,7 @@ class SongMediaItem(MediaManagerItem):
search_type = self.searchTextEdit.currentSearchType()
if search_type == SongSearch.Entire:
log.debug(u'Entire Song Search')
search_results = self.parent.manager.get_all_objects(Song,
or_(Song.search_title.like(u'%' + self.whitespace.sub(u' ',
search_keywords.lower()) + u'%'),
Song.search_lyrics.like(u'%' + search_keywords.lower() + u'%'),
Song.comments.like(u'%' + search_keywords.lower() + u'%')))
search_results = self.searchEntire(search_keywords)
self.displayResultsSong(search_results)
elif search_type == SongSearch.Titles:
log.debug(u'Titles Search')
@ -201,6 +197,13 @@ class SongMediaItem(MediaManagerItem):
self.displayResultsSong(search_results)
check_search_result(self.listView, search_results)
def searchEntire(self, search_keywords):
return self.parent.manager.get_all_objects(Song,
or_(Song.search_title.like(u'%' + self.whitespace.sub(u' ',
search_keywords.lower()) + u'%'),
Song.search_lyrics.like(u'%' + search_keywords.lower() + u'%'),
Song.comments.like(u'%' + search_keywords.lower() + u'%')))
def onSongListLoad(self):
"""
Handle the exit from the edit dialog and trigger remote updates
@ -217,7 +220,8 @@ class SongMediaItem(MediaManagerItem):
# Push edits to the service manager to update items
if self.editItem and self.updateServiceOnEdit and \
not self.remoteTriggered:
item = self.buildServiceItem(self.editItem)
item_id = _getIdOfItemToGenerate(self.editItem)
item = self.buildServiceItem(item_id)
self.parent.serviceManager.replaceServiceItem(item)
self.onRemoteEditClear()
self.onSearchTextButtonClick()
@ -475,3 +479,19 @@ class SongMediaItem(MediaManagerItem):
"""
return locale.strcoll(unicode(song_1.title.lower()),
unicode(song_2.title.lower()))
def hasSearch(self):
"""
Returns whether this plugin supports the search method
"""
return True
def search(self, string):
"""
Search for some songs
"""
search_results = self.searchEntire(string)
results = []
for song in search_results:
results.append([song.id, song.title])
return results

View File

@ -268,4 +268,3 @@ class SongsPlugin(Plugin):
action_list.remove_action(self.songExportItem, UiStrings().Export)
action_list.remove_action(self.toolsReindexItem, UiStrings().Tools)
Plugin.finalise(self)