forked from openlp/openlp
Remote song search and go live beginnings
This commit is contained in:
parent
ae2d8c6961
commit
047287950c
@ -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')
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
@ -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);
|
||||||
|
@ -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():
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user