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', translate('OpenLP.MediaManagerItem',
'You must select one or more items to send live.')) 'You must select one or more items to send live.'))
else: else:
log.debug(u'%s Live requested', self.plugin.name) self.goLive()
serviceItem = self.buildServiceItem()
if serviceItem: def goLive(self, item_id=None):
serviceItem.from_plugin = True log.debug(u'%s Live requested', self.plugin.name)
self.parent.liveController.addServiceItem(serviceItem) 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): def onAddClick(self):
""" """
@ -573,3 +580,16 @@ class MediaManagerItem(QtGui.QWidget):
else: else:
item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0] item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0]
return item_id 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 constructor for the plugin manager. Passes the controllers on to
the plugins for them to interact with via their ServiceItems. the plugins for them to interact with via their ServiceItems.
pluginmanager.py
``plugin_dir`` ``plugin_dir``
The directory to search for plugins. The directory to search for plugins.
""" """
@ -211,3 +211,12 @@ class PluginManager(object):
if plugin.isActive(): if plugin.isActive():
plugin.finalise() plugin.finalise()
log.info(u'Finalisation Complete for %s ' % plugin.name) 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="#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="#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="#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> </div>
</div> </div>
@ -55,7 +56,7 @@
<ul data-role="listview" data-inset="true"> <ul data-role="listview" data-inset="true">
</ul> </ul>
</div> </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-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-unblank" data-role="button" data-icon="unblank">Unblank</a>
<a href="#" id="service-refresh" data-role="button" data-icon="refresh">Refresh</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 data-role="listview" data-inset="true">
</ul> </ul>
</div> </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-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-unblank" data-role="button" data-icon="unblank">Unblank</a>
<a href="#" id="controller-refresh" data-role="button" data-icon="refresh">Refresh</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> <a href="#" id="alert-submit" data-role="button">Show Alert</a>
</div> </div>
</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> </body>
</html> </html>

View File

@ -189,7 +189,43 @@ window.OpenLP = {
} }
); );
return false; 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
$("#service-manager").live("pagebeforeshow", OpenLP.loadService); $("#service-manager").live("pagebeforeshow", OpenLP.loadService);
@ -207,6 +243,8 @@ $("#controller-blank").live("click", OpenLP.blankDisplay);
$("#controller-unblank").live("click", OpenLP.unblankDisplay); $("#controller-unblank").live("click", OpenLP.unblankDisplay);
// Alerts // Alerts
$("#alert-submit").live("click", OpenLP.showAlert); $("#alert-submit").live("click", OpenLP.showAlert);
// Search
$("#search-submit").live("click", OpenLP.search);
// Poll the server twice a second to get any updates. // Poll the server twice a second to get any updates.
$.ajaxSetup({ cache: false }); $.ajaxSetup({ cache: false });
setInterval("OpenLP.pollServer();", 500); setInterval("OpenLP.pollServer();", 500);

View File

@ -250,7 +250,10 @@ class HttpConnection(object):
(r'^/api/controller/(live|preview)/(.*)$', self.controller), (r'^/api/controller/(live|preview)/(.*)$', self.controller),
(r'^/api/service/(.*)$', self.service), (r'^/api/service/(.*)$', self.service),
(r'^/api/display/(hide|show)$', self.display), (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()'), QtCore.QObject.connect(self.socket, QtCore.SIGNAL(u'readyRead()'),
self.ready_read) self.ready_read)
@ -443,6 +446,49 @@ class HttpConnection(object):
return HttpResponse(json.dumps({u'results': {u'success': True}}), return HttpResponse(json.dumps({u'results': {u'success': True}}),
{u'Content-Type': u'application/json'}) {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): def send_response(self, response):
http = u'HTTP/1.1 %s\r\n' % response.code http = u'HTTP/1.1 %s\r\n' % response.code
for header, value in response.headers.iteritems(): for header, value in response.headers.iteritems():

View File

@ -171,11 +171,7 @@ class SongMediaItem(MediaManagerItem):
search_type = self.searchTextEdit.currentSearchType() search_type = self.searchTextEdit.currentSearchType()
if search_type == SongSearch.Entire: if search_type == SongSearch.Entire:
log.debug(u'Entire Song Search') log.debug(u'Entire Song Search')
search_results = self.parent.manager.get_all_objects(Song, search_results = self.searchEntire(search_keywords)
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'%')))
self.displayResultsSong(search_results) self.displayResultsSong(search_results)
elif search_type == SongSearch.Titles: elif search_type == SongSearch.Titles:
log.debug(u'Titles Search') log.debug(u'Titles Search')
@ -201,6 +197,13 @@ class SongMediaItem(MediaManagerItem):
self.displayResultsSong(search_results) self.displayResultsSong(search_results)
check_search_result(self.listView, 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): def onSongListLoad(self):
""" """
Handle the exit from the edit dialog and trigger remote updates 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 # Push edits to the service manager to update items
if self.editItem and self.updateServiceOnEdit and \ if self.editItem and self.updateServiceOnEdit and \
not self.remoteTriggered: not self.remoteTriggered:
item = self.buildServiceItem(self.editItem) item_id = _getIdOfItemToGenerate(self.editItem)
item = self.buildServiceItem(item_id)
self.parent.serviceManager.replaceServiceItem(item) self.parent.serviceManager.replaceServiceItem(item)
self.onRemoteEditClear() self.onRemoteEditClear()
self.onSearchTextButtonClick() self.onSearchTextButtonClick()
@ -475,3 +479,19 @@ class SongMediaItem(MediaManagerItem):
""" """
return locale.strcoll(unicode(song_1.title.lower()), return locale.strcoll(unicode(song_1.title.lower()),
unicode(song_2.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.songExportItem, UiStrings().Export)
action_list.remove_action(self.toolsReindexItem, UiStrings().Tools) action_list.remove_action(self.toolsReindexItem, UiStrings().Tools)
Plugin.finalise(self) Plugin.finalise(self)