forked from openlp/openlp
Update remote API to use Flask, and be more RESTful
This commit is contained in:
parent
52d811331c
commit
ef2f798f6f
@ -18,3 +18,21 @@
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
|
||||
from flask import Flask
|
||||
from flask_cors import CORS
|
||||
|
||||
from openlp.core.api.versions import v1
|
||||
from openlp.core.api.versions import v2
|
||||
from openlp.core.api.main import main_views
|
||||
|
||||
app = Flask(__name__)
|
||||
CORS(app)
|
||||
|
||||
app.register_blueprint(main_views)
|
||||
v1.register_blueprints(app)
|
||||
v2.register_blueprints(app)
|
||||
|
||||
|
||||
def register_blueprint(blueprint, url_prefix=None):
|
||||
app.register_blueprint(blueprint, url_prefix)
|
||||
|
@ -65,6 +65,5 @@ def download_and_check(callback=None):
|
||||
file_size = get_url_file_size('https://get.openlp.org/webclient/site.zip')
|
||||
callback.setRange(0, file_size)
|
||||
if download_file(callback, 'https://get.openlp.org/webclient/site.zip',
|
||||
AppLocation.get_section_data_path('remotes') / 'site.zip',
|
||||
sha256=sha256):
|
||||
AppLocation.get_section_data_path('remotes') / 'site.zip'):
|
||||
deploy_zipfile(AppLocation.get_section_data_path('remotes'), 'site.zip')
|
||||
|
@ -1,23 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2020 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# 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, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# 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, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
"""
|
||||
The Endpoint class, which provides plugins with a way to serve their own portion of the API
|
||||
"""
|
@ -1,148 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2020 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# 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, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# 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, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
from openlp.core.api.http import requires_auth
|
||||
from openlp.core.api.http.endpoint import Endpoint
|
||||
from openlp.core.common.applocation import AppLocation
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.lib import create_thumb
|
||||
from openlp.core.lib.serviceitem import ItemCapabilities
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
controller_endpoint = Endpoint('controller')
|
||||
api_controller_endpoint = Endpoint('api')
|
||||
|
||||
|
||||
@api_controller_endpoint.route('controller/live/text')
|
||||
@controller_endpoint.route('live/text')
|
||||
def controller_text(request):
|
||||
"""
|
||||
Perform an action on the slide controller.
|
||||
|
||||
:param request: the http request - not used
|
||||
"""
|
||||
log.debug('controller_text')
|
||||
live_controller = Registry().get('live_controller')
|
||||
current_item = live_controller.service_item
|
||||
data = []
|
||||
if current_item:
|
||||
for index, frame in enumerate(current_item.get_frames()):
|
||||
item = {}
|
||||
# Handle text (songs, custom, bibles)
|
||||
if current_item.is_text():
|
||||
if frame['verse']:
|
||||
item['tag'] = str(frame['verse'])
|
||||
else:
|
||||
item['tag'] = str(index + 1)
|
||||
# TODO: Figure out rendering chords
|
||||
item['chords_text'] = str(frame.get('chords_text', ''))
|
||||
item['text'] = frame['text']
|
||||
item['html'] = current_item.get_rendered_frame(index)
|
||||
# Handle images, unless a custom thumbnail is given or if thumbnails is disabled
|
||||
elif current_item.is_image() and not frame.get('image', '') and Settings().value('api/thumbnails'):
|
||||
item['tag'] = str(index + 1)
|
||||
thumbnail_path = os.path.join('images', 'thumbnails', frame['title'])
|
||||
full_thumbnail_path = AppLocation.get_data_path() / thumbnail_path
|
||||
# Create thumbnail if it doesn't exists
|
||||
if not full_thumbnail_path.exists():
|
||||
create_thumb(Path(current_item.get_frame_path(index)), full_thumbnail_path, False)
|
||||
Registry().get('image_manager').add_image(str(full_thumbnail_path), frame['title'], None, 88, 88)
|
||||
item['img'] = urllib.request.pathname2url(os.path.sep + str(thumbnail_path))
|
||||
item['text'] = str(frame['title'])
|
||||
item['html'] = str(frame['title'])
|
||||
else:
|
||||
# Handle presentation etc.
|
||||
item['tag'] = str(index + 1)
|
||||
if current_item.is_capable(ItemCapabilities.HasDisplayTitle):
|
||||
item['title'] = str(frame['display_title'])
|
||||
if current_item.is_capable(ItemCapabilities.HasNotes):
|
||||
item['slide_notes'] = str(frame['notes'])
|
||||
if current_item.is_capable(ItemCapabilities.HasThumbnails) and Settings().value('api/thumbnails'):
|
||||
# If the file is under our app directory tree send the portion after the match
|
||||
data_path = str(AppLocation.get_data_path())
|
||||
if frame['image'][0:len(data_path)] == data_path:
|
||||
item['img'] = urllib.request.pathname2url(frame['image'][len(data_path):])
|
||||
Registry().get('image_manager').add_image(frame['image'], frame['title'], None, 88, 88)
|
||||
item['text'] = str(frame['title'])
|
||||
item['html'] = str(frame['title'])
|
||||
item['selected'] = (live_controller.selected_row == index)
|
||||
item['title'] = current_item.title
|
||||
data.append(item)
|
||||
json_data = {'results': {'slides': data}}
|
||||
if current_item:
|
||||
json_data['results']['item'] = live_controller.service_item.unique_identifier
|
||||
return json_data
|
||||
|
||||
|
||||
@api_controller_endpoint.route('controller/live/set')
|
||||
@controller_endpoint.route('live/set')
|
||||
@requires_auth
|
||||
def controller_set(request):
|
||||
"""
|
||||
Perform an action on the slide controller.
|
||||
|
||||
:param request: The action to perform.
|
||||
"""
|
||||
event = getattr(Registry().get('live_controller'), 'slidecontroller_live_set')
|
||||
try:
|
||||
json_data = request.GET.get('data')
|
||||
data = int(json.loads(json_data)['request']['id'])
|
||||
event.emit([data])
|
||||
except KeyError:
|
||||
log.error("Endpoint controller/live/set request id not found")
|
||||
return {'results': {'success': True}}
|
||||
|
||||
|
||||
@controller_endpoint.route('{controller}/{action:next|previous}')
|
||||
@requires_auth
|
||||
def controller_direction(request, controller, action):
|
||||
"""
|
||||
Handles requests for setting service items in the slide controller
|
||||
:param request: The http request object.
|
||||
:param controller: the controller slides forward or backward.
|
||||
:param action: the controller slides forward or backward.
|
||||
"""
|
||||
event = getattr(Registry().get('live_controller'), 'slidecontroller_{controller}_{action}'.
|
||||
format(controller=controller, action=action))
|
||||
event.emit()
|
||||
|
||||
|
||||
@api_controller_endpoint.route('controller/{controller}/{action:next|previous}')
|
||||
@requires_auth
|
||||
def controller_direction_api(request, controller, action):
|
||||
"""
|
||||
Handles requests for setting service items in the slide controller
|
||||
|
||||
:param request: The http request object.
|
||||
:param controller: the controller slides forward or backward.
|
||||
:param action: the controller slides forward or backward.
|
||||
"""
|
||||
controller_direction(request, controller, action)
|
||||
return {'results': {'success': True}}
|
@ -1,174 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2020 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# 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, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# 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, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
"""
|
||||
The :mod:`~openlp.core.api.endpoint.core` module contains the core API endpoints
|
||||
"""
|
||||
import logging
|
||||
import os
|
||||
|
||||
from openlp.core.api.http import requires_auth
|
||||
from openlp.core.api.http.endpoint import Endpoint
|
||||
from openlp.core.common.i18n import UiStrings, translate
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.lib import image_to_byte
|
||||
from openlp.core.lib.plugin import PluginStatus, StringContent
|
||||
|
||||
|
||||
template_dir = 'templates'
|
||||
static_dir = 'static'
|
||||
blank_dir = os.path.join(static_dir, 'index')
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
chords_endpoint = Endpoint('chords', template_dir=template_dir, static_dir=static_dir)
|
||||
stage_endpoint = Endpoint('stage', template_dir=template_dir, static_dir=static_dir)
|
||||
main_endpoint = Endpoint('main', template_dir=template_dir, static_dir=static_dir)
|
||||
blank_endpoint = Endpoint('', template_dir=template_dir, static_dir=blank_dir)
|
||||
|
||||
FILE_TYPES = {
|
||||
'.html': 'text/html',
|
||||
'.css': 'text/css',
|
||||
'.js': 'application/javascript',
|
||||
'.jpg': 'image/jpeg',
|
||||
'.gif': 'image/gif',
|
||||
'.ico': 'image/x-icon',
|
||||
'.png': 'image/png'
|
||||
}
|
||||
|
||||
remote = translate('RemotePlugin.Mobile', 'Remote')
|
||||
stage = translate('RemotePlugin.Mobile', 'Stage View')
|
||||
live = translate('RemotePlugin.Mobile', 'Live View')
|
||||
chords = translate('RemotePlugin.Mobile', 'Chords View')
|
||||
|
||||
TRANSLATED_STRINGS = {
|
||||
'app_title': "{main} {remote}".format(main=UiStrings().OpenLP, remote=remote),
|
||||
'stage_title': "{main} {stage}".format(main=UiStrings().OpenLP, stage=stage),
|
||||
'live_title': "{main} {live}".format(main=UiStrings().OpenLP, live=live),
|
||||
'chords_title': "{main} {chords}".format(main=UiStrings().OpenLP, chords=chords),
|
||||
'service_manager': translate('RemotePlugin.Mobile', 'Service Manager'),
|
||||
'slide_controller': translate('RemotePlugin.Mobile', 'Slide Controller'),
|
||||
'alerts': translate('RemotePlugin.Mobile', 'Alerts'),
|
||||
'search': translate('RemotePlugin.Mobile', 'Search'),
|
||||
'home': translate('RemotePlugin.Mobile', 'Home'),
|
||||
'refresh': translate('RemotePlugin.Mobile', 'Refresh'),
|
||||
'blank': translate('RemotePlugin.Mobile', 'Blank'),
|
||||
'theme': translate('RemotePlugin.Mobile', 'Theme'),
|
||||
'desktop': translate('RemotePlugin.Mobile', 'Desktop'),
|
||||
'show': translate('RemotePlugin.Mobile', 'Show'),
|
||||
'prev': translate('RemotePlugin.Mobile', 'Prev'),
|
||||
'next': translate('RemotePlugin.Mobile', 'Next'),
|
||||
'text': translate('RemotePlugin.Mobile', 'Text'),
|
||||
'show_alert': translate('RemotePlugin.Mobile', 'Show Alert'),
|
||||
'go_live': translate('RemotePlugin.Mobile', 'Go Live'),
|
||||
'add_to_service': translate('RemotePlugin.Mobile', 'Add to Service'),
|
||||
'add_and_go_to_service': translate('RemotePlugin.Mobile', 'Add & Go to Service'),
|
||||
'no_results': translate('RemotePlugin.Mobile', 'No Results'),
|
||||
'options': translate('RemotePlugin.Mobile', 'Options'),
|
||||
'service': translate('RemotePlugin.Mobile', 'Service'),
|
||||
'slides': translate('RemotePlugin.Mobile', 'Slides'),
|
||||
'settings': translate('RemotePlugin.Mobile', 'Settings'),
|
||||
}
|
||||
|
||||
|
||||
@stage_endpoint.route('')
|
||||
def stage_index(request):
|
||||
"""
|
||||
Deliver the page for the /stage url
|
||||
"""
|
||||
return stage_endpoint.render_template('stage.mako', **TRANSLATED_STRINGS)
|
||||
|
||||
|
||||
@chords_endpoint.route('')
|
||||
def chords_index(request):
|
||||
"""
|
||||
Deliver the page for the /chords url
|
||||
"""
|
||||
return chords_endpoint.render_template('chords.mako', **TRANSLATED_STRINGS)
|
||||
|
||||
|
||||
@main_endpoint.route('')
|
||||
def main_index(request):
|
||||
"""
|
||||
Deliver the page for the /main url
|
||||
"""
|
||||
return main_endpoint.render_template('main.mako', **TRANSLATED_STRINGS)
|
||||
|
||||
|
||||
@blank_endpoint.route('')
|
||||
def index(request):
|
||||
"""
|
||||
Deliver the page for the / url
|
||||
:param request:
|
||||
"""
|
||||
return blank_endpoint.render_template('index.mako', **TRANSLATED_STRINGS)
|
||||
|
||||
|
||||
@blank_endpoint.route('api/poll')
|
||||
@blank_endpoint.route('poll')
|
||||
def poll(request):
|
||||
"""
|
||||
Deliver the page for the /poll url
|
||||
|
||||
:param request:
|
||||
"""
|
||||
return Registry().get('poller').poll()
|
||||
|
||||
|
||||
@blank_endpoint.route('api/display/{display:hide|show|blank|theme|desktop}')
|
||||
@blank_endpoint.route('display/{display:hide|show|blank|theme|desktop}')
|
||||
@requires_auth
|
||||
def toggle_display(request, display):
|
||||
"""
|
||||
Deliver the functions for the /display url
|
||||
:param request: the http request - not used
|
||||
:param display: the display function to be triggered
|
||||
"""
|
||||
Registry().get('live_controller').slidecontroller_toggle_display.emit(display)
|
||||
return {'results': {'success': True}}
|
||||
|
||||
|
||||
@blank_endpoint.route('api/plugin/search')
|
||||
@blank_endpoint.route('plugin/search')
|
||||
def plugin_search_list(request):
|
||||
"""
|
||||
Deliver a list of active plugins that support search
|
||||
:param request: the http request - not used
|
||||
"""
|
||||
searches = []
|
||||
for plugin in Registry().get('plugin_manager').plugins:
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item and plugin.media_item.has_search:
|
||||
searches.append([plugin.name, str(plugin.text_strings[StringContent.Name]['plural'])])
|
||||
return {'results': {'items': searches}}
|
||||
|
||||
|
||||
@main_endpoint.route('image')
|
||||
def main_image(request):
|
||||
"""
|
||||
Return the latest display image as a byte stream.
|
||||
:param request: base path of the URL. Not used but passed by caller
|
||||
:return:
|
||||
"""
|
||||
live_controller = Registry().get('live_controller')
|
||||
result = {
|
||||
'slide_image': 'data:image/png;base64,' + str(image_to_byte(live_controller.slide_image))
|
||||
}
|
||||
return {'results': result}
|
@ -1,132 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2020 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# 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, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# 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, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
import json
|
||||
import re
|
||||
import urllib
|
||||
|
||||
from webob import Response
|
||||
|
||||
from openlp.core.api.http.errors import NotFound
|
||||
from openlp.core.common.applocation import AppLocation
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.lib import image_to_byte
|
||||
from openlp.core.lib.plugin import PluginStatus
|
||||
|
||||
|
||||
def search(request, plugin_name, log):
|
||||
"""
|
||||
Handles requests for searching the plugins
|
||||
|
||||
:param request: The http request object.
|
||||
:param plugin_name: The plugin name.
|
||||
:param log: The class log object.
|
||||
"""
|
||||
try:
|
||||
json_data = request.GET.get('data')
|
||||
text = json.loads(json_data)['request']['text']
|
||||
except KeyError:
|
||||
log.error("Endpoint {text} search request text not found".format(text=plugin_name))
|
||||
text = ""
|
||||
text = urllib.parse.unquote(text)
|
||||
plugin = Registry().get('plugin_manager').get_plugin_by_name(plugin_name)
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item and plugin.media_item.has_search:
|
||||
results = plugin.media_item.search(text, False)
|
||||
return {'results': {'items': results}}
|
||||
else:
|
||||
raise NotFound()
|
||||
|
||||
|
||||
def live(request, plugin_name, log):
|
||||
"""
|
||||
Handles requests for making live of the plugins
|
||||
|
||||
:param request: The http request object.
|
||||
:param plugin_name: The plugin name.
|
||||
:param log: The class log object.
|
||||
"""
|
||||
try:
|
||||
json_data = request.GET.get('data')
|
||||
request_id = json.loads(json_data)['request']['id']
|
||||
except KeyError:
|
||||
log.error("Endpoint {text} search request text not found".format(text=plugin_name))
|
||||
return []
|
||||
plugin = Registry().get('plugin_manager').get_plugin_by_name(plugin_name)
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item:
|
||||
getattr(plugin.media_item, '{name}_go_live'.format(name=plugin_name)).emit([request_id, True])
|
||||
|
||||
|
||||
def service(request, plugin_name, log):
|
||||
"""
|
||||
Handles requests for adding to a service of the plugins
|
||||
|
||||
:param request: The http request object.
|
||||
:param plugin_name: The plugin name.
|
||||
:param log: The class log object.
|
||||
"""
|
||||
try:
|
||||
json_data = request.GET.get('data')
|
||||
request_id = json.loads(json_data)['request']['id']
|
||||
except KeyError:
|
||||
log.error("Endpoint {plugin} search request text not found".format(plugin=plugin_name))
|
||||
return []
|
||||
plugin = Registry().get('plugin_manager').get_plugin_by_name(plugin_name)
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item:
|
||||
item_id = plugin.media_item.create_item_from_id(request_id)
|
||||
getattr(plugin.media_item, '{name}_add_to_service'.format(name=plugin_name)).emit([item_id, True])
|
||||
|
||||
|
||||
def display_thumbnails(request, controller_name, log, dimensions, file_name, slide=None):
|
||||
"""
|
||||
Handles requests for adding a song to the service
|
||||
|
||||
Return an image to a web page based on a URL
|
||||
:param request: Request object
|
||||
:param controller_name: which controller is requesting the image
|
||||
:param log: the logger object
|
||||
:param dimensions: the image size eg 88x88
|
||||
:param str file_name: the file name of the image
|
||||
:param slide: the individual image name
|
||||
:return:
|
||||
"""
|
||||
log.debug('serve thumbnail {cname}/thumbnails{dim}/{fname}/{slide}'.format(cname=controller_name,
|
||||
dim=dimensions,
|
||||
fname=file_name,
|
||||
slide=slide))
|
||||
# -1 means use the default dimension in ImageManager
|
||||
width = -1
|
||||
height = -1
|
||||
image = None
|
||||
if dimensions:
|
||||
match = re.search(r'(\d+)x(\d+)', dimensions)
|
||||
if match:
|
||||
# let's make sure that the dimensions are within reason
|
||||
width = sorted([10, int(match.group(1)), 1000])[1]
|
||||
height = sorted([10, int(match.group(2)), 1000])[1]
|
||||
if controller_name and file_name:
|
||||
file_name = urllib.parse.unquote(file_name)
|
||||
if '..' not in file_name: # no hacking please
|
||||
full_path = AppLocation.get_section_data_path(controller_name) / 'thumbnails' / file_name
|
||||
if slide:
|
||||
full_path = full_path / slide
|
||||
if full_path.exists():
|
||||
Registry().get('image_manager').add_image(full_path, full_path.name, None, width, height)
|
||||
image = Registry().get('image_manager').get_image(full_path, full_path.name, width, height)
|
||||
return Response(body=image_to_byte(image, False), status=200, content_type='image/png', charset='utf8')
|
@ -1,40 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2020 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# 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, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# 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, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
import logging
|
||||
|
||||
from openlp.core.api.endpoint.core import TRANSLATED_STRINGS
|
||||
from openlp.core.api.http.endpoint import Endpoint
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
remote_endpoint = Endpoint('remote', template_dir='remotes')
|
||||
|
||||
|
||||
@remote_endpoint.route('{view}')
|
||||
def index(request, view):
|
||||
"""
|
||||
Handles requests for /remotes url
|
||||
|
||||
:param request: The http request object.
|
||||
:param view: The view name to be servered.
|
||||
"""
|
||||
return remote_endpoint.render_template('{view}.mako'.format(view=view), **TRANSLATED_STRINGS)
|
@ -1,99 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2020 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# 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, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# 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, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
import json
|
||||
import logging
|
||||
|
||||
from openlp.core.api.http import requires_auth
|
||||
from openlp.core.api.http.endpoint import Endpoint
|
||||
from openlp.core.common.registry import Registry
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
service_endpoint = Endpoint('service')
|
||||
api_service_endpoint = Endpoint('api/service')
|
||||
|
||||
|
||||
@api_service_endpoint.route('list')
|
||||
@service_endpoint.route('list')
|
||||
def list_service(request):
|
||||
"""
|
||||
Handles requests for service items in the service manager
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return {'results': {'items': get_service_items()}}
|
||||
|
||||
|
||||
@api_service_endpoint.route('set')
|
||||
@service_endpoint.route('set')
|
||||
@requires_auth
|
||||
def service_set(request):
|
||||
"""
|
||||
Handles requests for setting service items in the service manager
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
event = getattr(Registry().get('service_manager'), 'servicemanager_set_item')
|
||||
try:
|
||||
json_data = request.GET.get('data')
|
||||
data = int(json.loads(json_data)['request']['id'])
|
||||
event.emit(data)
|
||||
except KeyError:
|
||||
log.error("Endpoint service/set request id not found")
|
||||
return {'results': {'success': True}}
|
||||
|
||||
|
||||
@api_service_endpoint.route('{action:next|previous}')
|
||||
@service_endpoint.route('{action:next|previous}')
|
||||
@requires_auth
|
||||
def service_direction(request, action):
|
||||
"""
|
||||
Handles requests for setting service items in the service manager
|
||||
|
||||
:param request: The http request object.
|
||||
:param action: the the service slides forward or backward.
|
||||
"""
|
||||
event = getattr(Registry().get('service_manager'), 'servicemanager_{action}_item'.format(action=action))
|
||||
event.emit()
|
||||
return {'results': {'success': True}}
|
||||
|
||||
|
||||
def get_service_items():
|
||||
"""
|
||||
Read the service item in use and return the data as a json object
|
||||
"""
|
||||
live_controller = Registry().get('live_controller')
|
||||
service_items = []
|
||||
if live_controller.service_item:
|
||||
current_unique_identifier = live_controller.service_item.unique_identifier
|
||||
else:
|
||||
current_unique_identifier = None
|
||||
for item in Registry().get('service_manager').service_items:
|
||||
service_item = item['service_item']
|
||||
service_items.append({
|
||||
'id': str(service_item.unique_identifier),
|
||||
'title': str(service_item.get_display_title()),
|
||||
'plugin': str(service_item.name),
|
||||
'notes': str(service_item.notes),
|
||||
'selected': (service_item.unique_identifier == current_unique_identifier)
|
||||
})
|
||||
return service_items
|
@ -24,39 +24,8 @@ from functools import wraps
|
||||
|
||||
from webob import Response
|
||||
|
||||
from openlp.core.api.http.wsgiapp import WSGIApplication
|
||||
from openlp.core.common.settings import Settings
|
||||
|
||||
application = WSGIApplication('api')
|
||||
|
||||
|
||||
def _route_from_url(url_prefix, url):
|
||||
"""
|
||||
Create a route from the URL
|
||||
"""
|
||||
url_prefix = '/{prefix}/'.format(prefix=url_prefix.strip('/'))
|
||||
if not url:
|
||||
url = url_prefix[:-1]
|
||||
else:
|
||||
url = url_prefix + url
|
||||
url = url.replace('//', '/')
|
||||
return url
|
||||
|
||||
|
||||
def register_endpoint(end_point):
|
||||
"""
|
||||
Register an endpoint with the app
|
||||
"""
|
||||
for url, view_func, method in end_point.routes:
|
||||
# Set the view functions
|
||||
route = _route_from_url(end_point.url_prefix, url)
|
||||
application.add_route(route, view_func, method)
|
||||
# Add a static route if necessary
|
||||
if end_point.static_dir:
|
||||
static_route = _route_from_url(end_point.url_prefix, 'static')
|
||||
static_route += '(.*)'
|
||||
application.add_static_route(static_route, end_point.static_dir)
|
||||
|
||||
|
||||
def check_auth(auth):
|
||||
"""
|
||||
|
@ -1,80 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2020 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# 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, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# 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, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
"""
|
||||
The Endpoint class, which provides plugins with a way to serve their own portion of the API
|
||||
"""
|
||||
from mako.template import Template
|
||||
|
||||
from openlp.core.common.applocation import AppLocation
|
||||
|
||||
|
||||
class Endpoint(object):
|
||||
"""
|
||||
This is an endpoint for the HTTP API
|
||||
"""
|
||||
def __init__(self, url_prefix, template_dir=None, static_dir=None, assets_dir=None):
|
||||
"""
|
||||
Create an endpoint with a URL prefix
|
||||
"""
|
||||
self.url_prefix = url_prefix
|
||||
self.static_dir = static_dir
|
||||
self.template_dir = template_dir
|
||||
if assets_dir:
|
||||
self.assets_dir = assets_dir
|
||||
else:
|
||||
self.assets_dir = None
|
||||
self.routes = []
|
||||
|
||||
def add_url_route(self, url, view_func, method):
|
||||
"""
|
||||
Add a url route to the list of routes
|
||||
"""
|
||||
self.routes.append((url, view_func, method))
|
||||
|
||||
def route(self, rule, method='GET'):
|
||||
"""
|
||||
Set up a URL route
|
||||
"""
|
||||
def decorator(func):
|
||||
"""
|
||||
Make this a decorator
|
||||
"""
|
||||
self.add_url_route(rule, func, method)
|
||||
return func
|
||||
return decorator
|
||||
|
||||
def render_template(self, filename, **kwargs):
|
||||
"""
|
||||
Render a mako template
|
||||
|
||||
:param str filename: The file name of the template to render.
|
||||
:return: The rendered template object.
|
||||
:rtype: mako.template.Template
|
||||
"""
|
||||
root_path = AppLocation.get_section_data_path('remotes')
|
||||
if not self.template_dir:
|
||||
raise Exception('No template directory specified')
|
||||
path = root_path / self.template_dir / filename
|
||||
if self.static_dir:
|
||||
kwargs['static_url'] = '/{prefix}/static'.format(prefix=self.url_prefix)
|
||||
kwargs['static_url'] = kwargs['static_url'].replace('//', '/')
|
||||
kwargs['assets_url'] = '/assets'
|
||||
return Template(filename=str(path), input_encoding='utf-8').render(**kwargs)
|
@ -1,64 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2020 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# 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, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# 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, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
"""
|
||||
HTTP Error classes
|
||||
"""
|
||||
|
||||
|
||||
class HttpError(Exception):
|
||||
"""
|
||||
A base HTTP error (aka status code)
|
||||
"""
|
||||
def __init__(self, status, message):
|
||||
"""
|
||||
Initialise the exception
|
||||
"""
|
||||
super(HttpError, self).__init__(message)
|
||||
self.status = status
|
||||
self.message = message
|
||||
|
||||
def to_response(self):
|
||||
"""
|
||||
Convert this exception to a Response object
|
||||
"""
|
||||
return self.message, self.status
|
||||
|
||||
|
||||
class NotFound(HttpError):
|
||||
"""
|
||||
A 404
|
||||
"""
|
||||
def __init__(self):
|
||||
"""
|
||||
Make this a 404
|
||||
"""
|
||||
super(NotFound, self).__init__(404, 'Not Found')
|
||||
|
||||
|
||||
class ServerError(HttpError):
|
||||
"""
|
||||
A 500
|
||||
"""
|
||||
def __init__(self):
|
||||
"""
|
||||
Make this a 500
|
||||
"""
|
||||
super(ServerError, self).__init__(500, 'Server Error')
|
@ -24,16 +24,12 @@ with OpenLP. It uses JSON to communicate with the remotes.
|
||||
"""
|
||||
import logging
|
||||
import time
|
||||
from secrets import token_hex
|
||||
|
||||
from PyQt5 import QtCore, QtWidgets
|
||||
from waitress.server import create_server
|
||||
|
||||
from openlp.core.api.deploy import download_and_check, download_sha256
|
||||
from openlp.core.api.endpoint.controller import api_controller_endpoint, controller_endpoint
|
||||
from openlp.core.api.endpoint.core import blank_endpoint, chords_endpoint, main_endpoint, stage_endpoint
|
||||
from openlp.core.api.endpoint.remote import remote_endpoint
|
||||
from openlp.core.api.endpoint.service import api_service_endpoint, service_endpoint
|
||||
from openlp.core.api.http import application, register_endpoint
|
||||
from openlp.core.api.poll import Poller
|
||||
from openlp.core.common.applocation import AppLocation
|
||||
from openlp.core.common.i18n import UiStrings, translate
|
||||
@ -43,6 +39,7 @@ from openlp.core.common.registry import Registry, RegistryBase
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.threading import ThreadWorker, run_thread
|
||||
|
||||
from openlp.core.api import app as application
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -59,6 +56,7 @@ class HttpWorker(ThreadWorker):
|
||||
port = Settings().value('api/port')
|
||||
Registry().execute('get_website_version')
|
||||
try:
|
||||
application.static_folder = str(AppLocation.get_section_data_path('remotes') / 'static')
|
||||
self.server = create_server(application, host=address, port=port)
|
||||
self.server.run()
|
||||
except OSError:
|
||||
@ -85,6 +83,7 @@ class HttpServer(RegistryBase, RegistryProperties, LogMixin):
|
||||
Initialise the http server, and start the http server
|
||||
"""
|
||||
super(HttpServer, self).__init__(parent)
|
||||
Registry().register('authentication_token', token_hex())
|
||||
if not Registry().get_flag('no_web_server'):
|
||||
worker = HttpWorker()
|
||||
run_thread(worker, 'http_server')
|
||||
@ -96,31 +95,9 @@ class HttpServer(RegistryBase, RegistryProperties, LogMixin):
|
||||
"""
|
||||
Register the poll return service and start the servers.
|
||||
"""
|
||||
self.initialise()
|
||||
create_paths(AppLocation.get_section_data_path('remotes'))
|
||||
self.poller = Poller()
|
||||
Registry().register('poller', self.poller)
|
||||
application.initialise()
|
||||
register_endpoint(controller_endpoint)
|
||||
register_endpoint(api_controller_endpoint)
|
||||
register_endpoint(chords_endpoint)
|
||||
register_endpoint(stage_endpoint)
|
||||
register_endpoint(blank_endpoint)
|
||||
register_endpoint(main_endpoint)
|
||||
register_endpoint(service_endpoint)
|
||||
register_endpoint(api_service_endpoint)
|
||||
register_endpoint(remote_endpoint)
|
||||
|
||||
@staticmethod
|
||||
def initialise():
|
||||
"""
|
||||
Create the internal file structure if it does not exist
|
||||
:return:
|
||||
"""
|
||||
create_paths(AppLocation.get_section_data_path('remotes') / 'assets',
|
||||
AppLocation.get_section_data_path('remotes') / 'images',
|
||||
AppLocation.get_section_data_path('remotes') / 'static',
|
||||
AppLocation.get_section_data_path('remotes') / 'static' / 'index',
|
||||
AppLocation.get_section_data_path('remotes') / 'templates')
|
||||
|
||||
def first_time(self):
|
||||
"""
|
||||
|
@ -1,185 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2020 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# 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, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# 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, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
"""
|
||||
App stuff
|
||||
"""
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
|
||||
from webob import Request, Response
|
||||
from webob.static import DirectoryApp
|
||||
|
||||
from openlp.core.api.http.errors import HttpError, NotFound, ServerError
|
||||
from openlp.core.common.applocation import AppLocation
|
||||
|
||||
|
||||
ARGS_REGEX = re.compile(r'''\{(\w+)(?::([^}]+))?\}''', re.VERBOSE)
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _route_to_regex(route):
|
||||
r"""
|
||||
Convert a route to a regular expression
|
||||
|
||||
For example:
|
||||
|
||||
'songs/{song_id}' becomes 'songs/(?P<song_id>[^/]+)'
|
||||
|
||||
and
|
||||
|
||||
'songs/{song_id:\d+}' becomes 'songs/(?P<song_id>\d+)'
|
||||
|
||||
"""
|
||||
route_regex = ''
|
||||
last_pos = 0
|
||||
for match in ARGS_REGEX.finditer(route):
|
||||
route_regex += re.escape(route[last_pos:match.start()])
|
||||
arg_name = match.group(1)
|
||||
expr = match.group(2) or '[^/]+'
|
||||
expr = '(?P<%s>%s)' % (arg_name, expr)
|
||||
route_regex += expr
|
||||
last_pos = match.end()
|
||||
route_regex += re.escape(route[last_pos:])
|
||||
route_regex = '^%s$' % route_regex
|
||||
return route_regex
|
||||
|
||||
|
||||
def _make_response(view_result):
|
||||
"""
|
||||
Create a Response object from response
|
||||
"""
|
||||
log.debug("in Make response")
|
||||
if isinstance(view_result, Response):
|
||||
return view_result
|
||||
elif isinstance(view_result, tuple):
|
||||
content_type = 'text/html'
|
||||
body = view_result[0]
|
||||
if isinstance(body, dict):
|
||||
content_type = 'application/json'
|
||||
body = json.dumps(body)
|
||||
response = Response(body=body, status=view_result[1],
|
||||
content_type=content_type, charset='utf8')
|
||||
if len(view_result) >= 3:
|
||||
response.headers.update(view_result[2])
|
||||
return response
|
||||
elif isinstance(view_result, dict):
|
||||
return Response(body=json.dumps(view_result), status=200,
|
||||
content_type='application/json', charset='utf8')
|
||||
elif isinstance(view_result, str):
|
||||
return Response(body=view_result, status=200,
|
||||
content_type='text/html', charset='utf8')
|
||||
else:
|
||||
return Response(body=view_result, status=200,
|
||||
content_type='text/plain', charset='utf8')
|
||||
|
||||
|
||||
def _handle_exception(error):
|
||||
"""
|
||||
Handle exceptions
|
||||
"""
|
||||
log.exception(error)
|
||||
if isinstance(error, HttpError):
|
||||
return error.to_response()
|
||||
else:
|
||||
return ServerError().to_response()
|
||||
|
||||
|
||||
class WSGIApplication(object):
|
||||
"""
|
||||
This is the core of the API, the WSGI app
|
||||
"""
|
||||
def __init__(self, name):
|
||||
"""
|
||||
Create the app object
|
||||
"""
|
||||
self.name = name
|
||||
self.static_routes = {}
|
||||
self.route_map = {}
|
||||
|
||||
def initialise(self):
|
||||
"""
|
||||
Set up generic roots for the whole application
|
||||
:return: None
|
||||
"""
|
||||
self.add_static_route('/assets(.*)', '')
|
||||
self.add_static_route('/images(.*)', '')
|
||||
pass
|
||||
|
||||
def add_route(self, route, view_func, method):
|
||||
"""
|
||||
Add a route
|
||||
"""
|
||||
route_regex = _route_to_regex(route)
|
||||
if route_regex not in self.route_map:
|
||||
self.route_map[route_regex] = {}
|
||||
self.route_map[route_regex][method.upper()] = view_func
|
||||
|
||||
def add_static_route(self, route, static_dir):
|
||||
"""
|
||||
Add a static directory as a route
|
||||
"""
|
||||
if route not in self.static_routes:
|
||||
static_path = AppLocation.get_section_data_path('remotes') / static_dir
|
||||
if not static_path.exists():
|
||||
log.error('Static path "%s" does not exist. Skipping creating static route/', static_path)
|
||||
return
|
||||
self.static_routes[route] = DirectoryApp(str(static_path.resolve()))
|
||||
|
||||
def dispatch(self, request):
|
||||
"""
|
||||
Find the appropriate URL and run the view function
|
||||
"""
|
||||
# If not a static route, try the views
|
||||
for route, views in self.route_map.items():
|
||||
match = re.match(route, request.path)
|
||||
if match and request.method.upper() in views:
|
||||
kwargs = match.groupdict()
|
||||
log.debug('Found {method} {url}'.format(method=request.method, url=request.path))
|
||||
view_func = views[request.method.upper()]
|
||||
return _make_response(view_func(request, **kwargs))
|
||||
# Look to see if this is a static file request
|
||||
for route, static_app in self.static_routes.items():
|
||||
if re.match(route, request.path):
|
||||
return request.get_response(static_app)
|
||||
log.error('URL {url} - Not found'.format(url=request.path))
|
||||
raise NotFound()
|
||||
|
||||
def wsgi_app(self, environ, start_response):
|
||||
"""
|
||||
The actual WSGI application.
|
||||
"""
|
||||
request = Request(environ)
|
||||
try:
|
||||
response = self.dispatch(request)
|
||||
except Exception as e:
|
||||
response = _make_response(_handle_exception(e))
|
||||
response.headers.add("cache-control", "no-cache, no-store, must-revalidate")
|
||||
response.headers.add("pragma", "no-cache")
|
||||
response.headers.add("expires", "0")
|
||||
return response(environ, start_response)
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
"""
|
||||
Shortcut for wsgi_app.
|
||||
"""
|
||||
return self.wsgi_app(environ, start_response)
|
50
openlp/core/api/lib.py
Normal file
50
openlp/core/api/lib.py
Normal file
@ -0,0 +1,50 @@
|
||||
import json
|
||||
from flask import jsonify, Response, request
|
||||
from functools import wraps
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.common.registry import Registry
|
||||
|
||||
|
||||
def login_required(f):
|
||||
@wraps(f)
|
||||
def decorated(*args, **kwargs):
|
||||
if not Settings().value('api/authentication enabled'):
|
||||
return f(*args, **kwargs)
|
||||
token = request.headers.get('Authorization', '')
|
||||
if token == Registry().get('authentication_token'):
|
||||
return f(*args, **kwargs)
|
||||
else:
|
||||
return '', 401
|
||||
return decorated
|
||||
|
||||
|
||||
def old_success_response():
|
||||
return jsonify({'results': {'sucess': True}})
|
||||
|
||||
|
||||
def extract_request(json_string, name):
|
||||
try:
|
||||
if json_string:
|
||||
return json.loads(json_string)['request'][name]
|
||||
except KeyError:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
# ----------------------- OLD AUTH SECTION ---------------------
|
||||
def check_auth(username, password):
|
||||
return Settings().value('api/user id') == username and \
|
||||
Settings().value('api/password') == password
|
||||
|
||||
|
||||
def old_auth(f):
|
||||
@wraps(f)
|
||||
def decorated(*args, **kwargs):
|
||||
if not Settings().value('api/authentication enabled'):
|
||||
return f(*args, **kwargs)
|
||||
auth = request.authorization
|
||||
if not auth or not check_auth(auth.username, auth.password):
|
||||
return Response('', 401, {'WWW-Authenticate': 'Basic realm="OpenLP Login Required"'})
|
||||
else:
|
||||
return f(*args, **kwargs)
|
||||
return decorated
|
14
openlp/core/api/main.py
Normal file
14
openlp/core/api/main.py
Normal file
@ -0,0 +1,14 @@
|
||||
from flask import Blueprint, send_from_directory
|
||||
from openlp.core.common.applocation import AppLocation
|
||||
|
||||
main_views = Blueprint('main', __name__)
|
||||
|
||||
|
||||
@main_views.route('/')
|
||||
def index():
|
||||
return send_from_directory(str(AppLocation.get_section_data_path('remotes')), 'index.html')
|
||||
|
||||
|
||||
@main_views.route('/<path>')
|
||||
def assets(path):
|
||||
return send_from_directory(str(AppLocation.get_section_data_path('remotes')), path)
|
0
openlp/core/api/versions/__init__.py
Normal file
0
openlp/core/api/versions/__init__.py
Normal file
9
openlp/core/api/versions/v1/__init__.py
Normal file
9
openlp/core/api/versions/v1/__init__.py
Normal file
@ -0,0 +1,9 @@
|
||||
from openlp.core.api.versions.v1.controller import controller_views
|
||||
from openlp.core.api.versions.v1.core import core_views
|
||||
from openlp.core.api.versions.v1.service import service_views
|
||||
|
||||
|
||||
def register_blueprints(app):
|
||||
app.register_blueprint(controller_views)
|
||||
app.register_blueprint(core_views)
|
||||
app.register_blueprint(service_views)
|
83
openlp/core/api/versions/v1/controller.py
Normal file
83
openlp/core/api/versions/v1/controller.py
Normal file
@ -0,0 +1,83 @@
|
||||
import os
|
||||
import logging
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
from openlp.core.api.lib import old_auth, old_success_response, extract_request
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.common.applocation import AppLocation
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.lib import create_thumb
|
||||
from openlp.core.lib.serviceitem import ItemCapabilities
|
||||
|
||||
from flask import jsonify, request, Blueprint
|
||||
|
||||
controller_views = Blueprint('old_controller', __name__)
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@controller_views.route('/api/controller/live/text')
|
||||
def controller_text_api():
|
||||
live_controller = Registry().get('live_controller')
|
||||
current_item = live_controller.service_item
|
||||
result = {'results': {}}
|
||||
data = []
|
||||
if current_item:
|
||||
result['results']['item'] = current_item.unique_identifier
|
||||
for index, frame in enumerate(current_item.get_frames()):
|
||||
item = {}
|
||||
item['tag'] = index + 1
|
||||
item['selected'] = live_controller.selected_row == index
|
||||
item['title'] = current_item.title
|
||||
if current_item.is_text():
|
||||
if frame['verse']:
|
||||
item['tag'] = str(frame['verse'])
|
||||
item['chords_text'] = str(frame.get('chords_text', ''))
|
||||
item['text'] = frame['text']
|
||||
item['html'] = current_item.get_rendered_frame(index)
|
||||
elif current_item.is_image() and not frame.get('image', '') and Settings().value('api/thumbnails'):
|
||||
thumbnail_path = os.path.join('images', 'thumbnails', frame['title'])
|
||||
full_thumbnail_path = AppLocation.get_data_path() / thumbnail_path
|
||||
if not full_thumbnail_path.exists():
|
||||
create_thumb(Path(current_item.get_frame_path(index)), full_thumbnail_path, False)
|
||||
Registry().get('image_manager').add_image(str(full_thumbnail_path), frame['title'], None, 88, 88)
|
||||
item['img'] = urllib.request.pathname2url(os.path.sep + str(thumbnail_path))
|
||||
item['text'] = str(frame['title'])
|
||||
item['html'] = str(frame['title'])
|
||||
else:
|
||||
# presentations and other things
|
||||
if current_item.is_capable(ItemCapabilities.HasDisplayTitle):
|
||||
item['title'] = str(frame['display_title'])
|
||||
if current_item.is_capable(ItemCapabilities.HasNotes):
|
||||
item['slide_notes'] = str(frame['notes'])
|
||||
if current_item.is_capable(ItemCapabilities.HasThumbnails) and Settings().value('api/thumbnails'):
|
||||
# If the file is under our app directory tree send the portion after the match
|
||||
data_path = str(AppLocation.get_data_path())
|
||||
if frame['image'][0:len(data_path)] == data_path:
|
||||
item['img'] = urllib.request.pathname2url(frame['image'][len(data_path):])
|
||||
Registry().get('image_manager').add_image(frame['image'], frame['title'], None, 88, 88)
|
||||
item['text'] = str(frame['title'])
|
||||
item['html'] = str(frame['title'])
|
||||
data.append(item)
|
||||
result['results']['slides'] = data
|
||||
return jsonify(result)
|
||||
|
||||
|
||||
@controller_views.route('/api/controller/live/set')
|
||||
@old_auth
|
||||
def controller_set():
|
||||
event = Registry().get('live_controller').slidecontroller_live_set
|
||||
try:
|
||||
id = int(extract_request(request.args.get('data', ''), 'id'))
|
||||
event.emit([id])
|
||||
except (KeyError, ValueError):
|
||||
log.error('Received malformed request to set live controller')
|
||||
return old_success_response()
|
||||
|
||||
|
||||
@controller_views.route('/api/controller/<controller>/<action>')
|
||||
@old_auth
|
||||
def controller_direction(controller, action):
|
||||
getattr(Registry().get('live_controller'), 'slidecontroller_{controller}_{action}'.
|
||||
format(controller=controller, action=action)).emit()
|
||||
return old_success_response()
|
40
openlp/core/api/versions/v1/core.py
Normal file
40
openlp/core/api/versions/v1/core.py
Normal file
@ -0,0 +1,40 @@
|
||||
from openlp.core.api.lib import old_auth, old_success_response
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.lib import image_to_byte
|
||||
from openlp.core.lib.plugin import PluginStatus, StringContent
|
||||
from openlp.core.state import State
|
||||
|
||||
from flask import jsonify, Blueprint
|
||||
|
||||
|
||||
core_views = Blueprint('old_core', __name__)
|
||||
|
||||
|
||||
@core_views.route('/api/poll')
|
||||
def poll():
|
||||
return jsonify(Registry().get('poller').poll())
|
||||
|
||||
|
||||
@core_views.route('/api/display/<display>')
|
||||
@old_auth
|
||||
def toggle_display(display):
|
||||
ALLOWED_ACTIONS = ['hide', 'show', 'blank', 'theme', 'desktop']
|
||||
display = display.lower()
|
||||
if display in ALLOWED_ACTIONS:
|
||||
Registry().get('live_controller').slidecontroller_toggle_display.emit(display)
|
||||
return old_success_response()
|
||||
|
||||
|
||||
@core_views.route('/api/plugin/search')
|
||||
def plugin_list():
|
||||
searches = []
|
||||
for plugin in State().list_plugins():
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item and plugin.media_item.has_search:
|
||||
searches.append([plugin.name, str(plugin.text_strings[StringContent.Name]['plural'])])
|
||||
return jsonify({'results': {'items': searches}})
|
||||
|
||||
|
||||
@core_views.route('/main/image')
|
||||
def main_image():
|
||||
img = 'data:image/png;base64,{}'.format(image_to_byte(Registry().get('live_controller').grab_maindisplay()))
|
||||
return jsonify({'slide_image': img})
|
48
openlp/core/api/versions/v1/service.py
Normal file
48
openlp/core/api/versions/v1/service.py
Normal file
@ -0,0 +1,48 @@
|
||||
from openlp.core.api.lib import old_auth, old_success_response, extract_request
|
||||
from flask import jsonify, request, Blueprint
|
||||
|
||||
from openlp.core.common.registry import Registry
|
||||
|
||||
service_views = Blueprint('old_service', __name__)
|
||||
|
||||
|
||||
@service_views.route('/api/service/list')
|
||||
def service_items():
|
||||
live_controller = Registry().get('live_controller')
|
||||
service_items = []
|
||||
if live_controller.service_item:
|
||||
current_unique_identifier = live_controller.service_item.unique_identifier
|
||||
else:
|
||||
current_unique_identifier = None
|
||||
for item in Registry().get('service_manager').service_items:
|
||||
service_item = item['service_item']
|
||||
service_items.append({
|
||||
'id': str(service_item.unique_identifier),
|
||||
'title': str(service_item.get_display_title()),
|
||||
'plugin': str(service_item.name),
|
||||
'notes': str(service_item.notes),
|
||||
'selected': (service_item.unique_identifier == current_unique_identifier)
|
||||
})
|
||||
return jsonify({'results': {'items': service_items}})
|
||||
|
||||
|
||||
@service_views.route('/api/service/set')
|
||||
@old_auth
|
||||
def service_set():
|
||||
event = Registry().get('service_manager').servicemanager_set_item
|
||||
try:
|
||||
data = int(extract_request(request.args.get('data', ''), 'id'))
|
||||
event.emit(data)
|
||||
except (KeyError, ValueError):
|
||||
pass
|
||||
return old_success_response()
|
||||
|
||||
|
||||
@service_views.route('/api/service/direction/<action>')
|
||||
@old_auth
|
||||
def service_direction(action):
|
||||
ALLOWED_ACTIONS = ['next', 'previous']
|
||||
action = action.lower()
|
||||
if action in ALLOWED_ACTIONS:
|
||||
getattr(Registry().get('service_manager'), 'servicemanager_{action}_item'.format(action=action)).emit()
|
||||
return old_success_response()
|
9
openlp/core/api/versions/v2/__init__.py
Normal file
9
openlp/core/api/versions/v2/__init__.py
Normal file
@ -0,0 +1,9 @@
|
||||
from openlp.core.api.versions.v2.controller import controller_views
|
||||
from openlp.core.api.versions.v2.core import core
|
||||
from openlp.core.api.versions.v2.service import service_views
|
||||
|
||||
|
||||
def register_blueprints(app):
|
||||
app.register_blueprint(controller_views, url_prefix='/api/v2/controller/')
|
||||
app.register_blueprint(core, url_prefix='/api/v2/core/')
|
||||
app.register_blueprint(service_views, url_prefix='/api/v2/service/')
|
84
openlp/core/api/versions/v2/controller.py
Normal file
84
openlp/core/api/versions/v2/controller.py
Normal file
@ -0,0 +1,84 @@
|
||||
import os
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
from openlp.core.api.lib import login_required
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.common.applocation import AppLocation
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.lib import create_thumb
|
||||
from openlp.core.lib.serviceitem import ItemCapabilities
|
||||
|
||||
from flask import jsonify, request, abort, Blueprint
|
||||
|
||||
controller_views = Blueprint('controller', __name__)
|
||||
|
||||
|
||||
@controller_views.route('/live-item')
|
||||
def controller_text_api():
|
||||
live_controller = Registry().get('live_controller')
|
||||
current_item = live_controller.service_item
|
||||
data = []
|
||||
if current_item:
|
||||
for index, frame in enumerate(current_item.get_frames()):
|
||||
item = {}
|
||||
item['tag'] = index + 1
|
||||
item['selected'] = live_controller.selected_row == index
|
||||
item['title'] = current_item.title
|
||||
if current_item.is_text():
|
||||
if frame['verse']:
|
||||
item['tag'] = str(frame['verse'])
|
||||
item['chords_text'] = str(frame.get('chords_text', ''))
|
||||
item['text'] = frame['text']
|
||||
item['html'] = current_item.get_rendered_frame(index)
|
||||
elif current_item.is_image() and not frame.get('image', '') and Settings().value('api/thumbnails'):
|
||||
thumbnail_path = os.path.join('images', 'thumbnails', frame['title'])
|
||||
full_thumbnail_path = AppLocation.get_data_path() / thumbnail_path
|
||||
if not full_thumbnail_path.exists():
|
||||
create_thumb(Path(current_item.get_frame_path(index)), full_thumbnail_path, False)
|
||||
Registry().get('image_manager').add_image(str(full_thumbnail_path), frame['title'], None, 88, 88)
|
||||
item['img'] = urllib.request.pathname2url(os.path.sep + str(thumbnail_path))
|
||||
item['text'] = str(frame['title'])
|
||||
item['html'] = str(frame['title'])
|
||||
else:
|
||||
# presentations and other things
|
||||
if current_item.is_capable(ItemCapabilities.HasDisplayTitle):
|
||||
item['title'] = str(frame['display_title'])
|
||||
if current_item.is_capable(ItemCapabilities.HasNotes):
|
||||
item['slide_notes'] = str(frame['notes'])
|
||||
if current_item.is_capable(ItemCapabilities.HasThumbnails) and Settings().value('api/thumbnails'):
|
||||
# If the file is under our app directory tree send the portion after the match
|
||||
data_path = str(AppLocation.get_data_path())
|
||||
if frame['image'][0:len(data_path)] == data_path:
|
||||
item['img'] = urllib.request.pathname2url(frame['image'][len(data_path):])
|
||||
Registry().get('image_manager').add_image(frame['image'], frame['title'], None, 88, 88)
|
||||
item['text'] = str(frame['title'])
|
||||
item['html'] = str(frame['title'])
|
||||
data.append(item)
|
||||
return jsonify(data)
|
||||
|
||||
|
||||
@controller_views.route('/show', methods=['POST'])
|
||||
@login_required
|
||||
def controller_set():
|
||||
data = request.json
|
||||
if not data:
|
||||
abort(400)
|
||||
num = data.get('id', -1)
|
||||
Registry().get('live_controller').slidecontroller_live_set.emit([num])
|
||||
return '', 204
|
||||
|
||||
|
||||
@controller_views.route('/progress', methods=['POST'])
|
||||
@login_required
|
||||
def controller_direction():
|
||||
ALLOWED_ACTIONS = ['next', 'previous']
|
||||
data = request.json
|
||||
if not data:
|
||||
abort(400)
|
||||
action = data.get('action', '').lower()
|
||||
if action not in ALLOWED_ACTIONS:
|
||||
abort(400)
|
||||
getattr(Registry().get('live_controller'), 'slidecontroller_live_{action}'.
|
||||
format(action=action)).emit()
|
||||
return '', 204
|
68
openlp/core/api/versions/v2/core.py
Normal file
68
openlp/core/api/versions/v2/core.py
Normal file
@ -0,0 +1,68 @@
|
||||
from openlp.core.api.lib import login_required
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.lib import image_to_byte
|
||||
from openlp.core.lib.plugin import PluginStatus, StringContent
|
||||
from openlp.core.state import State
|
||||
|
||||
from flask import jsonify, request, abort, Blueprint
|
||||
|
||||
|
||||
core = Blueprint('core', __name__)
|
||||
|
||||
|
||||
@core.route('/poll')
|
||||
def poll():
|
||||
return jsonify(Registry().get('poller').poll())
|
||||
|
||||
|
||||
@core.route('/display', methods=['POST'])
|
||||
@login_required
|
||||
def toggle_display():
|
||||
ALLOWED_ACTIONS = ['hide', 'show', 'blank', 'theme', 'desktop']
|
||||
data = request.json
|
||||
if not data:
|
||||
abort(400)
|
||||
display = data.get('display', '').lower()
|
||||
if display not in ALLOWED_ACTIONS:
|
||||
abort(400)
|
||||
|
||||
Registry().get('live_controller').slidecontroller_toggle_display.emit(display)
|
||||
return '', 204
|
||||
|
||||
|
||||
@core.route('/plugins')
|
||||
def plugin_list():
|
||||
searches = []
|
||||
for plugin in State().list_plugins():
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item and plugin.media_item.has_search:
|
||||
searches.append({'key': plugin.name, 'name': str(plugin.text_strings[StringContent.Name]['plural'])})
|
||||
return jsonify(searches)
|
||||
|
||||
|
||||
@core.route('/system')
|
||||
def system_information():
|
||||
data = {}
|
||||
data['websocket_port'] = Settings().value('api/websocket port')
|
||||
data['login_required'] = Settings().value('api/authentication enabled')
|
||||
return jsonify(data)
|
||||
|
||||
|
||||
@core.route('/login', methods=['POST'])
|
||||
def login():
|
||||
data = request.json
|
||||
if not data:
|
||||
abort(400)
|
||||
username = data.get('username', '')
|
||||
password = data.get('password', '')
|
||||
if username == Settings().value('api/user id') and password == Settings().value('api/password'):
|
||||
return jsonify({'token': Registry().get('authentication_token')})
|
||||
else:
|
||||
return '', 401
|
||||
|
||||
|
||||
@core.route('/live-image')
|
||||
def main_image():
|
||||
controller = Registry().get('live_controller')
|
||||
img = 'data:image/png;base64,{}'.format(image_to_byte(controller.grab_maindisplay()))
|
||||
return jsonify({'binary_image': img})
|
61
openlp/core/api/versions/v2/service.py
Normal file
61
openlp/core/api/versions/v2/service.py
Normal file
@ -0,0 +1,61 @@
|
||||
from openlp.core.api.lib import login_required
|
||||
|
||||
from flask import jsonify, request, abort, Blueprint
|
||||
|
||||
from openlp.core.common.registry import Registry
|
||||
|
||||
|
||||
service_views = Blueprint('service', __name__)
|
||||
|
||||
|
||||
@service_views.route('/items')
|
||||
def service_items():
|
||||
live_controller = Registry().get('live_controller')
|
||||
service_items = []
|
||||
if live_controller.service_item:
|
||||
current_unique_identifier = live_controller.service_item.unique_identifier
|
||||
else:
|
||||
current_unique_identifier = None
|
||||
for item in Registry().get('service_manager').service_items:
|
||||
service_item = item['service_item']
|
||||
if 'ccli_number' in service_item.data_string:
|
||||
ccli_number = service_item.data_string['ccli_number']
|
||||
else:
|
||||
ccli_number = ''
|
||||
service_items.append({
|
||||
'id': str(service_item.unique_identifier),
|
||||
'title': str(service_item.get_display_title()),
|
||||
'plugin': str(service_item.name),
|
||||
'ccli_number': str(ccli_number),
|
||||
'notes': str(service_item.notes),
|
||||
'selected': (service_item.unique_identifier == current_unique_identifier)
|
||||
})
|
||||
return jsonify(service_items)
|
||||
|
||||
|
||||
@service_views.route('/show', methods=['POST'])
|
||||
@login_required
|
||||
def service_set():
|
||||
data = request.json
|
||||
if not data:
|
||||
abort(400)
|
||||
try:
|
||||
id = int(data.get('id', -1))
|
||||
except ValueError:
|
||||
abort(400)
|
||||
Registry().get('service_manager').set_item(id)
|
||||
return '', 204
|
||||
|
||||
|
||||
@service_views.route('/progress', methods=['POST'])
|
||||
@login_required
|
||||
def service_direction():
|
||||
ALLOWED_ACTIONS = ['next', 'previous']
|
||||
data = request.json
|
||||
if not data:
|
||||
abort(400)
|
||||
action = data.get('action', '').lower()
|
||||
if action not in ALLOWED_ACTIONS:
|
||||
abort(400)
|
||||
getattr(Registry().get('service_manager'), 'servicemanager_{action}_item'.format(action=action)).emit()
|
||||
return '', 204
|
@ -1,73 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2020 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# 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, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# 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, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
"""
|
||||
The :mod:`~openlp.core.api.endpoint` module contains various API endpoints
|
||||
"""
|
||||
import logging
|
||||
|
||||
from openlp.core.api.http import requires_auth
|
||||
from openlp.core.api.http.endpoint import Endpoint
|
||||
from openlp.core.common.registry import Registry
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
media_endpoint = Endpoint('media')
|
||||
|
||||
|
||||
@media_endpoint.route('play')
|
||||
@requires_auth
|
||||
def media_play(request):
|
||||
"""
|
||||
Handles requests for playing media
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
media = Registry().get('media_controller')
|
||||
live = Registry().get('live_controller')
|
||||
status = media.media_play(live, False)
|
||||
return {'results': {'success': status}}
|
||||
|
||||
|
||||
@media_endpoint.route('pause')
|
||||
@requires_auth
|
||||
def media_pause(request):
|
||||
"""
|
||||
Handles requests for pausing media
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
media = Registry().get('media_controller')
|
||||
live = Registry().get('live_controller')
|
||||
status = media.media_pause(live)
|
||||
return {'results': {'success': status}}
|
||||
|
||||
|
||||
@media_endpoint.route('stop')
|
||||
@requires_auth
|
||||
def media_stop(request):
|
||||
"""
|
||||
Handles requests for stopping
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
event = getattr(Registry().get('live_controller'), 'mediacontroller_live_stop')
|
||||
event.emit()
|
||||
return {'results': {'success': True}}
|
@ -33,7 +33,6 @@ from subprocess import check_output
|
||||
from PyQt5 import QtCore
|
||||
|
||||
from openlp.core.state import State
|
||||
from openlp.core.api.http import register_endpoint
|
||||
from openlp.core.common import is_linux, is_macosx
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.common.mixins import LogMixin, RegistryProperties
|
||||
@ -43,7 +42,7 @@ from openlp.core.lib.serviceitem import ItemCapabilities
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.core.ui import DisplayControllerType
|
||||
from openlp.core.ui.media import MediaState, ItemMediaInfo, MediaType, parse_optical_path, VIDEO_EXT, AUDIO_EXT
|
||||
from openlp.core.ui.media.endpoint import media_endpoint
|
||||
from openlp.core.ui.media.remote import register_views
|
||||
from openlp.core.ui.media.vlcplayer import VlcPlayer, get_vlc
|
||||
|
||||
|
||||
@ -81,7 +80,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
||||
Registry().register_function('songs_hide', self.media_hide)
|
||||
Registry().register_function('songs_blank', self.media_blank)
|
||||
Registry().register_function('songs_unblank', self.media_unblank)
|
||||
register_endpoint(media_endpoint)
|
||||
register_views()
|
||||
|
||||
def bootstrap_initialise(self):
|
||||
"""
|
||||
|
98
openlp/core/ui/media/remote.py
Normal file
98
openlp/core/ui/media/remote.py
Normal file
@ -0,0 +1,98 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2018 OpenLP Developers #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# 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 #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`~openlp.core.api.endpoint` module contains various API endpoints
|
||||
"""
|
||||
import logging
|
||||
from flask import abort, jsonify, Blueprint
|
||||
|
||||
from openlp.core.api import app
|
||||
from openlp.core.api.lib import login_required, old_auth
|
||||
from openlp.core.common.registry import Registry
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
v1_media = Blueprint('v1-media-controller', __name__)
|
||||
v2_media = Blueprint('v2-media-controller', __name__)
|
||||
|
||||
|
||||
@v2_media.route('/play', methods=['POST'])
|
||||
@login_required
|
||||
def media_play():
|
||||
media = Registry().get('media_controller')
|
||||
live = Registry().get('live_controller')
|
||||
try:
|
||||
status = media.media_play(live, True)
|
||||
except Exception:
|
||||
# The current item probably isn't a media item
|
||||
abort(400)
|
||||
if status:
|
||||
return '', 204
|
||||
abort(400)
|
||||
|
||||
|
||||
@v2_media.route('/pause', methods=['POST'])
|
||||
@login_required
|
||||
def media_pause():
|
||||
media = Registry().get('media_controller')
|
||||
live = Registry().get('live_controller')
|
||||
media.media_pause(live)
|
||||
return '', 204
|
||||
|
||||
|
||||
@v2_media.route('/stop', methods=['POST'])
|
||||
@login_required
|
||||
def media_stop():
|
||||
Registry().get('live_controller').mediacontroller_live_stop.emit()
|
||||
return '', 204
|
||||
|
||||
|
||||
# -------------- DEPRECATED ------------------------
|
||||
@v1_media.route('/play')
|
||||
@old_auth
|
||||
def v1_media_play():
|
||||
media = Registry().get('media_controller')
|
||||
live = Registry().get('live_controller')
|
||||
status = media.media_play(live, False)
|
||||
return jsonify({'success': status})
|
||||
|
||||
|
||||
@v1_media.route('/pause')
|
||||
@old_auth
|
||||
def v1_media_pause():
|
||||
media = Registry().get('media_controller')
|
||||
live = Registry().get('live_controller')
|
||||
status = media.media_pause(live)
|
||||
return jsonify({'success': status})
|
||||
|
||||
|
||||
@v1_media.route('/stop')
|
||||
@old_auth
|
||||
def v1_media_stop():
|
||||
Registry().get('live_controller').mediacontroller_live_stop.emit()
|
||||
return ''
|
||||
# -------------- END OF DEPRECATED ------------------------
|
||||
|
||||
|
||||
def register_views():
|
||||
app.register_blueprint(v2_media, url_prefix='/api/v2/media/')
|
||||
app.register_blueprint(v1_media, url_prefix='/api/media/')
|
@ -1238,8 +1238,9 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
|
||||
win_image.setDevicePixelRatio(self.preview_display.devicePixelRatio())
|
||||
# self.slide_preview.setPixmap(win_image)
|
||||
self.slide_image = win_image
|
||||
base64_image = image_to_byte(win_image, True)
|
||||
base64_image = image_to_byte(win_image)
|
||||
self.preview_display.set_single_image_data('#000', base64_image)
|
||||
return self.slide_image
|
||||
|
||||
def on_slide_selected_next_action(self, checked):
|
||||
"""
|
||||
|
@ -22,7 +22,6 @@
|
||||
import logging
|
||||
|
||||
from openlp.core.state import State
|
||||
from openlp.core.api.http import register_endpoint
|
||||
from openlp.core.common.actions import ActionList
|
||||
from openlp.core.common.i18n import UiStrings, translate
|
||||
from openlp.core.common.settings import Settings
|
||||
@ -31,7 +30,7 @@ from openlp.core.lib.plugin import Plugin, StringContent
|
||||
from openlp.core.lib.theme import VerticalType
|
||||
from openlp.core.lib.ui import create_action
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
from openlp.plugins.alerts.endpoint import api_alerts_endpoint, alerts_endpoint
|
||||
from openlp.plugins.alerts.remote import register_views
|
||||
from openlp.plugins.alerts.forms.alertform import AlertForm
|
||||
from openlp.plugins.alerts.lib.alertsmanager import AlertsManager
|
||||
from openlp.plugins.alerts.lib.alertstab import AlertsTab
|
||||
@ -129,8 +128,7 @@ class AlertsPlugin(Plugin):
|
||||
AlertsManager(self)
|
||||
self.manager = Manager('alerts', init_schema)
|
||||
self.alert_form = AlertForm(self)
|
||||
register_endpoint(alerts_endpoint)
|
||||
register_endpoint(api_alerts_endpoint)
|
||||
register_views()
|
||||
State().add_service(self.name, self.weight, is_plugin=True)
|
||||
State().update_pre_conditions(self.name, self.check_pre_conditions())
|
||||
|
||||
|
@ -1,58 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2020 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# 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, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# 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, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
import json
|
||||
import logging
|
||||
import urllib
|
||||
|
||||
from openlp.core.api.http import requires_auth
|
||||
from openlp.core.api.http.endpoint import Endpoint
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.lib.plugin import PluginStatus
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
alerts_endpoint = Endpoint('alert')
|
||||
api_alerts_endpoint = Endpoint('api')
|
||||
|
||||
|
||||
@alerts_endpoint.route('')
|
||||
@api_alerts_endpoint.route('alert')
|
||||
@requires_auth
|
||||
def alert(request):
|
||||
"""
|
||||
Handles requests for setting service items in the service manager
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
plugin = Registry().get('plugin_manager').get_plugin_by_name("alerts")
|
||||
if plugin.status == PluginStatus.Active:
|
||||
try:
|
||||
json_data = request.GET.get('data')
|
||||
text = json.loads(json_data)['request']['text']
|
||||
except KeyError:
|
||||
log.error("Endpoint alerts request text not found")
|
||||
text = urllib.parse.unquote(text)
|
||||
Registry().get('alerts_manager').alerts_text.emit([text])
|
||||
success = True
|
||||
else:
|
||||
success = False
|
||||
return {'results': {'success': success}}
|
39
openlp/plugins/alerts/remote.py
Normal file
39
openlp/plugins/alerts/remote.py
Normal file
@ -0,0 +1,39 @@
|
||||
from flask import Blueprint, request, abort
|
||||
|
||||
from openlp.core.api import app
|
||||
from openlp.core.api.lib import login_required, extract_request, old_success_response, old_auth
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.lib.plugin import PluginStatus
|
||||
|
||||
|
||||
v1_views = Blueprint('v1-alert-plugin', __name__)
|
||||
v2_views = Blueprint('v2-alert-plugin', __name__)
|
||||
|
||||
|
||||
@v2_views.route('', methods=['POST'])
|
||||
@login_required
|
||||
def alert():
|
||||
data = request.json
|
||||
if not data:
|
||||
abort(400)
|
||||
alert = data.get('text', '')
|
||||
if alert:
|
||||
if Registry().get('plugin_manager').get_plugin_by_name('alerts').status == PluginStatus.Active:
|
||||
Registry().get('alerts_manager').alerts_text.emit([alert])
|
||||
return '', 204
|
||||
abort(400)
|
||||
|
||||
|
||||
@v1_views.route('')
|
||||
@old_auth
|
||||
def old_alert():
|
||||
alert = extract_request(request.args.get('data', ''), 'text')
|
||||
if alert:
|
||||
if Registry().get('plugin_manager').get_plugin_by_name('alerts').status == PluginStatus.Active:
|
||||
Registry().get('alerts_manager').alerts_text.emit([alert])
|
||||
return old_success_response()
|
||||
|
||||
|
||||
def register_views():
|
||||
app.register_blueprint(v2_views, url_prefix='/api/v2/plugins/alerts')
|
||||
app.register_blueprint(v1_views, url_prefix='/api/alert')
|
@ -22,13 +22,12 @@
|
||||
import logging
|
||||
|
||||
from openlp.core.state import State
|
||||
from openlp.core.api.http import register_endpoint
|
||||
from openlp.core.common.actions import ActionList
|
||||
from openlp.core.common.i18n import UiStrings, translate
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
from openlp.core.lib.plugin import Plugin, StringContent
|
||||
from openlp.core.lib.ui import create_action
|
||||
from openlp.plugins.bibles.endpoint import api_bibles_endpoint, bibles_endpoint
|
||||
from openlp.plugins.bibles.remote import register_views
|
||||
from openlp.plugins.bibles.lib.biblestab import BiblesTab
|
||||
from openlp.plugins.bibles.lib.manager import BibleManager
|
||||
from openlp.plugins.bibles.lib.mediaitem import BibleMediaItem
|
||||
@ -49,8 +48,7 @@ class BiblePlugin(Plugin):
|
||||
self.icon_path = UiIcons().bible
|
||||
self.icon = UiIcons().bible
|
||||
self.manager = BibleManager(self)
|
||||
register_endpoint(bibles_endpoint)
|
||||
register_endpoint(api_bibles_endpoint)
|
||||
register_views()
|
||||
State().add_service('bible', self.weight, is_plugin=True)
|
||||
State().update_pre_conditions('bible', self.check_pre_conditions())
|
||||
|
||||
|
@ -1,99 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2020 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# 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, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# 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, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
import logging
|
||||
|
||||
from openlp.core.api.endpoint.pluginhelpers import live, search, service
|
||||
from openlp.core.api.http import requires_auth
|
||||
from openlp.core.api.http.endpoint import Endpoint
|
||||
from openlp.core.api.http.errors import NotFound
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
bibles_endpoint = Endpoint('bibles')
|
||||
api_bibles_endpoint = Endpoint('api')
|
||||
|
||||
|
||||
@bibles_endpoint.route('search')
|
||||
def bibles_search(request):
|
||||
"""
|
||||
Handles requests for searching the bibles plugin
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return search(request, 'bibles', log)
|
||||
|
||||
|
||||
@bibles_endpoint.route('live')
|
||||
@requires_auth
|
||||
def bibles_live(request):
|
||||
"""
|
||||
Handles requests for making a song live
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return live(request, 'bibles', log)
|
||||
|
||||
|
||||
@bibles_endpoint.route('add')
|
||||
@requires_auth
|
||||
def bibles_service(request):
|
||||
"""
|
||||
Handles requests for adding a song to the service
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return service(request, 'bibles', log)
|
||||
|
||||
|
||||
@api_bibles_endpoint.route('bibles/search')
|
||||
def bibles_search_api(request):
|
||||
"""
|
||||
Handles requests for searching the bibles plugin
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return search(request, 'bibles', log)
|
||||
|
||||
|
||||
@api_bibles_endpoint.route('bibles/live')
|
||||
@requires_auth
|
||||
def bibles_live_api(request):
|
||||
"""
|
||||
Handles requests for making a song live
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return live(request, 'bibles', log)
|
||||
|
||||
|
||||
@api_bibles_endpoint.route('bibles/add')
|
||||
@requires_auth
|
||||
def bibles_service_api(request):
|
||||
"""
|
||||
Handles requests for adding a song to the service
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
try:
|
||||
return search(request, 'bibles', log)
|
||||
except NotFound:
|
||||
return {'results': {'items': []}}
|
112
openlp/plugins/bibles/remote.py
Normal file
112
openlp/plugins/bibles/remote.py
Normal file
@ -0,0 +1,112 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2019 OpenLP Developers #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# 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
|
||||
from flask import request, jsonify, Blueprint
|
||||
|
||||
from openlp.core.api import app
|
||||
from openlp.core.api.lib import login_required, extract_request, old_auth
|
||||
from openlp.core.lib.plugin import PluginStatus
|
||||
from openlp.core.common.registry import Registry
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
v1_views = Blueprint('v1-bibles-plugin', __name__)
|
||||
v2_views = Blueprint('v2-bibles-plugin', __name__)
|
||||
|
||||
|
||||
def search(text):
|
||||
plugin = Registry().get('plugin_manager').get_plugin_by_name('bibles')
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item and plugin.media_item.has_search:
|
||||
results = plugin.media_item.search(text, False)
|
||||
return results
|
||||
return None
|
||||
|
||||
|
||||
def live(id):
|
||||
plugin = Registry().get('plugin_manager').get_plugin_by_name('bibles')
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item:
|
||||
plugin.media_item.bibles_go_live.emit([id, True])
|
||||
|
||||
|
||||
def add(id):
|
||||
plugin = Registry().get('plugin_manager').get_plugin_by_name('bibles')
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item:
|
||||
item_id = plugin.media_item.create_item_from_id(id)
|
||||
plugin.media_item.bibles_add_to_service.emit([item_id, True])
|
||||
|
||||
|
||||
@v2_views.route('/search')
|
||||
@login_required
|
||||
def search_bible():
|
||||
text = request.args.get('text', '')
|
||||
result = search(text)
|
||||
if result:
|
||||
return jsonify(result)
|
||||
return '', 400
|
||||
|
||||
|
||||
@v2_views.route('/live', methods=['POST'])
|
||||
@login_required
|
||||
def send_live():
|
||||
id = request.json.get('id', -1)
|
||||
live(id)
|
||||
return '', 204
|
||||
|
||||
|
||||
@v2_views.route('/add', methods=['POST'])
|
||||
@login_required
|
||||
def add_to_service():
|
||||
id = request.json.get('id', -1)
|
||||
add(id)
|
||||
return '', 204
|
||||
|
||||
|
||||
# ---------------- DEPRECATED REMOVE AFTER RELEASE --------------
|
||||
@v1_views.route('/search')
|
||||
@old_auth
|
||||
def old_search_bible():
|
||||
text = extract_request(request.args.get('data', ''), 'text')
|
||||
return jsonify({'results': {'items': search(text)}})
|
||||
|
||||
|
||||
@v1_views.route('/live')
|
||||
@old_auth
|
||||
def old_send_live():
|
||||
id = extract_request(request.args.get('data', ''), 'id')
|
||||
live(id)
|
||||
return '', 204
|
||||
|
||||
|
||||
@v1_views.route('/add')
|
||||
@old_auth
|
||||
def old_add_to_service():
|
||||
id = extract_request(request.args.get('data', ''), 'id')
|
||||
add(id)
|
||||
return '', 204
|
||||
# ------------ END DEPRECATED ----------------------------------
|
||||
|
||||
|
||||
def register_views():
|
||||
app.register_blueprint(v2_views, url_prefix='/api/v2/plugins/bibles')
|
||||
app.register_blueprint(v1_views, url_prefix='/api/bibles')
|
@ -26,13 +26,12 @@ for the Custom Slides plugin.
|
||||
import logging
|
||||
|
||||
from openlp.core.state import State
|
||||
from openlp.core.api.http import register_endpoint
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.lib import build_icon
|
||||
from openlp.core.lib.db import Manager
|
||||
from openlp.core.lib.plugin import Plugin, StringContent
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
from openlp.plugins.custom.endpoint import api_custom_endpoint, custom_endpoint
|
||||
from openlp.plugins.custom.remote import register_views
|
||||
from openlp.plugins.custom.lib.db import CustomSlide, init_schema
|
||||
from openlp.plugins.custom.lib.mediaitem import CustomMediaItem
|
||||
from openlp.plugins.custom.lib.customtab import CustomTab
|
||||
@ -56,8 +55,7 @@ class CustomPlugin(Plugin):
|
||||
self.db_manager = Manager('custom', init_schema)
|
||||
self.icon_path = UiIcons().clone
|
||||
self.icon = build_icon(self.icon_path)
|
||||
register_endpoint(custom_endpoint)
|
||||
register_endpoint(api_custom_endpoint)
|
||||
register_views()
|
||||
State().add_service(self.name, self.weight, is_plugin=True)
|
||||
State().update_pre_conditions(self.name, self.check_pre_conditions())
|
||||
|
||||
|
@ -1,99 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2020 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# 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, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# 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, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
import logging
|
||||
|
||||
from openlp.core.api.endpoint.pluginhelpers import live, search, service
|
||||
from openlp.core.api.http import requires_auth
|
||||
from openlp.core.api.http.endpoint import Endpoint
|
||||
from openlp.core.api.http.errors import NotFound
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
custom_endpoint = Endpoint('custom')
|
||||
api_custom_endpoint = Endpoint('api')
|
||||
|
||||
|
||||
@custom_endpoint.route('search')
|
||||
def custom_search(request):
|
||||
"""
|
||||
Handles requests for searching the custom plugin
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return search(request, 'custom', log)
|
||||
|
||||
|
||||
@custom_endpoint.route('live')
|
||||
@requires_auth
|
||||
def custom_live(request):
|
||||
"""
|
||||
Handles requests for making a song live
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return live(request, 'custom', log)
|
||||
|
||||
|
||||
@custom_endpoint.route('add')
|
||||
@requires_auth
|
||||
def custom_service(request):
|
||||
"""
|
||||
Handles requests for adding a song to the service
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return service(request, 'custom', log)
|
||||
|
||||
|
||||
@api_custom_endpoint.route('custom/search')
|
||||
def custom_search_api(request):
|
||||
"""
|
||||
Handles requests for searching the custom plugin
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return search(request, 'custom', log)
|
||||
|
||||
|
||||
@api_custom_endpoint.route('custom/live')
|
||||
@requires_auth
|
||||
def custom_live_api(request):
|
||||
"""
|
||||
Handles requests for making a song live
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return live(request, 'custom', log)
|
||||
|
||||
|
||||
@api_custom_endpoint.route('custom/add')
|
||||
@requires_auth
|
||||
def custom_service_api(request):
|
||||
"""
|
||||
Handles requests for adding a song to the service
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
try:
|
||||
return search(request, 'custom', log)
|
||||
except NotFound:
|
||||
return {'results': {'items': []}}
|
118
openlp/plugins/custom/remote.py
Normal file
118
openlp/plugins/custom/remote.py
Normal file
@ -0,0 +1,118 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2019 OpenLP Developers #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# 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
|
||||
|
||||
from flask import abort, request, Blueprint, jsonify
|
||||
|
||||
from openlp.core.api import app
|
||||
from openlp.core.api.lib import login_required, extract_request, old_auth
|
||||
from openlp.core.lib.plugin import PluginStatus
|
||||
from openlp.core.common.registry import Registry
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
v1_custom = Blueprint('v1-custom-plugin', __name__)
|
||||
v2_custom = Blueprint('v2-custom-plugin', __name__)
|
||||
|
||||
|
||||
def search(text):
|
||||
plugin = Registry().get('plugin_manager').get_plugin_by_name('custom')
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item and plugin.media_item.has_search:
|
||||
results = plugin.media_item.search(text, False)
|
||||
return results
|
||||
return None
|
||||
|
||||
|
||||
def live(id):
|
||||
plugin = Registry().get('plugin_manager').get_plugin_by_name('custom')
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item:
|
||||
plugin.media_item.custom_go_live.emit([id, True])
|
||||
|
||||
|
||||
def add(id):
|
||||
plugin = Registry().get('plugin_manager').get_plugin_by_name('custom')
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item:
|
||||
item_id = plugin.media_item.create_item_from_id(id)
|
||||
plugin.media_item.custom_add_to_service.emit([item_id, True])
|
||||
|
||||
|
||||
@v2_custom.route('/search')
|
||||
@login_required
|
||||
def search_view():
|
||||
text = request.args.get('text', '')
|
||||
result = search(text)
|
||||
if result:
|
||||
return jsonify(result)
|
||||
return '', 400
|
||||
|
||||
|
||||
@v2_custom.route('/add', methods=['POST'])
|
||||
@login_required
|
||||
def add_view():
|
||||
data = request.json
|
||||
if not data:
|
||||
abort(400)
|
||||
id = data.get('id', -1)
|
||||
add(id)
|
||||
return '', 204
|
||||
|
||||
|
||||
@v2_custom.route('/live', methods=['POST'])
|
||||
@login_required
|
||||
def live_view():
|
||||
data = request.json
|
||||
if not data:
|
||||
abort(400)
|
||||
id = data.get('id', -1)
|
||||
live(id)
|
||||
return '', 204
|
||||
|
||||
|
||||
# ----------------- DEPRECATED --------------
|
||||
@v1_custom.route('/search')
|
||||
@old_auth
|
||||
def old_search():
|
||||
text = extract_request(request.args.get('data', ''), 'text')
|
||||
return jsonify({'results': {'items': search(text)}})
|
||||
|
||||
|
||||
@v1_custom.route('/add')
|
||||
@old_auth
|
||||
def old_add():
|
||||
id = extract_request(request.args.get('data', ''), 'id')
|
||||
add(id)
|
||||
return '', 204
|
||||
|
||||
|
||||
@v1_custom.route('/live')
|
||||
@old_auth
|
||||
def old_live():
|
||||
id = extract_request(request.args.get('data', ''), 'id')
|
||||
live(id)
|
||||
return '', 204
|
||||
# ---------------- END DEPRECATED ----------------
|
||||
|
||||
|
||||
def register_views():
|
||||
app.register_blueprint(v2_custom, url_prefix='/api/v2/plugins/custom/')
|
||||
app.register_blueprint(v1_custom, url_prefix='/api/custom/')
|
@ -1,112 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2020 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# 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, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# 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, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
import logging
|
||||
|
||||
from openlp.core.api.endpoint.pluginhelpers import display_thumbnails, live, search, service
|
||||
from openlp.core.api.http import requires_auth
|
||||
from openlp.core.api.http.endpoint import Endpoint
|
||||
from openlp.core.api.http.errors import NotFound
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
images_endpoint = Endpoint('images')
|
||||
api_images_endpoint = Endpoint('api')
|
||||
|
||||
|
||||
# images/thumbnails/320x240/1.jpg
|
||||
@images_endpoint.route('thumbnails/{dimensions}/{file_name}')
|
||||
def images_thumbnails(request, dimensions, file_name):
|
||||
"""
|
||||
Return an image to a web page based on a URL
|
||||
:param request: Request object
|
||||
:param dimensions: the image size eg 88x88
|
||||
:param file_name: the individual image name
|
||||
:return:
|
||||
"""
|
||||
return display_thumbnails(request, 'images', log, dimensions, file_name)
|
||||
|
||||
|
||||
@images_endpoint.route('search')
|
||||
def images_search(request):
|
||||
"""
|
||||
Handles requests for searching the images plugin
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return search(request, 'images', log)
|
||||
|
||||
|
||||
@images_endpoint.route('live')
|
||||
@requires_auth
|
||||
def images_live(request):
|
||||
"""
|
||||
Handles requests for making a song live
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return live(request, 'images', log)
|
||||
|
||||
|
||||
@images_endpoint.route('add')
|
||||
@requires_auth
|
||||
def images_service(request):
|
||||
"""
|
||||
Handles requests for adding a song to the service
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return service(request, 'images', log)
|
||||
|
||||
|
||||
@api_images_endpoint.route('images/search')
|
||||
def images_search_api(request):
|
||||
"""
|
||||
Handles requests for searching the images plugin
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return search(request, 'images', log)
|
||||
|
||||
|
||||
@api_images_endpoint.route('images/live')
|
||||
@requires_auth
|
||||
def images_live_api(request):
|
||||
"""
|
||||
Handles requests for making a song live
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return live(request, 'images', log)
|
||||
|
||||
|
||||
@api_images_endpoint.route('images/add')
|
||||
@requires_auth
|
||||
def images_service_api(request):
|
||||
"""
|
||||
Handles requests for adding a song to the service
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
try:
|
||||
return search(request, 'images', log)
|
||||
except NotFound:
|
||||
return {'results': {'items': []}}
|
@ -24,14 +24,13 @@ import logging
|
||||
from PyQt5 import QtGui
|
||||
|
||||
from openlp.core.state import State
|
||||
from openlp.core.api.http import register_endpoint
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.lib import ImageSource, build_icon
|
||||
from openlp.core.lib.db import Manager
|
||||
from openlp.core.lib.plugin import Plugin, StringContent
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
from openlp.plugins.images.endpoint import api_images_endpoint, images_endpoint
|
||||
from openlp.plugins.images.remote import register_views
|
||||
from openlp.plugins.images.lib import upgrade
|
||||
from openlp.plugins.images.lib.mediaitem import ImageMediaItem
|
||||
from openlp.plugins.images.lib.imagetab import ImageTab
|
||||
@ -50,8 +49,7 @@ class ImagePlugin(Plugin):
|
||||
self.weight = -7
|
||||
self.icon_path = UiIcons().picture
|
||||
self.icon = build_icon(self.icon_path)
|
||||
register_endpoint(images_endpoint)
|
||||
register_endpoint(api_images_endpoint)
|
||||
register_views()
|
||||
State().add_service('image', self.weight, is_plugin=True)
|
||||
State().update_pre_conditions('image', self.check_pre_conditions())
|
||||
|
||||
|
118
openlp/plugins/images/remote.py
Normal file
118
openlp/plugins/images/remote.py
Normal file
@ -0,0 +1,118 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2019 OpenLP Developers #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# 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
|
||||
|
||||
from flask import abort, request, Blueprint, jsonify
|
||||
|
||||
from openlp.core.api import app
|
||||
from openlp.core.api.lib import login_required, extract_request, old_auth
|
||||
from openlp.core.lib.plugin import PluginStatus
|
||||
from openlp.core.common.registry import Registry
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
v1_images = Blueprint('v1-images-plugin', __name__)
|
||||
v2_images = Blueprint('v2-images-plugin', __name__)
|
||||
|
||||
|
||||
def search(text):
|
||||
plugin = Registry().get('plugin_manager').get_plugin_by_name('images')
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item and plugin.media_item.has_search:
|
||||
results = plugin.media_item.search(text, False)
|
||||
return results
|
||||
return None
|
||||
|
||||
|
||||
def live(id):
|
||||
plugin = Registry().get('plugin_manager').get_plugin_by_name('images')
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item:
|
||||
plugin.media_item.images_go_live.emit([id, True])
|
||||
|
||||
|
||||
def add(id):
|
||||
plugin = Registry().get('plugin_manager').get_plugin_by_name('images')
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item:
|
||||
item_id = plugin.media_item.create_item_from_id(id)
|
||||
plugin.media_item.images_add_to_service.emit([item_id, True])
|
||||
|
||||
|
||||
@v2_images.route('/search')
|
||||
@login_required
|
||||
def search_view():
|
||||
text = request.args.get('text', '')
|
||||
result = search(text)
|
||||
if result:
|
||||
return jsonify(result)
|
||||
return '', 400
|
||||
|
||||
|
||||
@v2_images.route('/add', methods=['POST'])
|
||||
@login_required
|
||||
def add_view():
|
||||
data = request.json
|
||||
if not data:
|
||||
abort(400)
|
||||
id = data.get('id', -1)
|
||||
add(id)
|
||||
return '', 204
|
||||
|
||||
|
||||
@v2_images.route('/live', methods=['POST'])
|
||||
@login_required
|
||||
def live_view():
|
||||
data = request.json
|
||||
if not data:
|
||||
abort(400)
|
||||
id = data.get('id', -1)
|
||||
live(id)
|
||||
return '', 204
|
||||
|
||||
|
||||
# ----------------- DEPRECATED --------------
|
||||
@v1_images.route('/search')
|
||||
@old_auth
|
||||
def old_search():
|
||||
text = extract_request(request.args.get('data', ''), 'text')
|
||||
return jsonify({'results': {'items': search(text)}})
|
||||
|
||||
|
||||
@v1_images.route('/add')
|
||||
@old_auth
|
||||
def old_add():
|
||||
id = extract_request(request.args.get('data', ''), 'id')
|
||||
add(id)
|
||||
return '', 204
|
||||
|
||||
|
||||
@v1_images.route('/live')
|
||||
@old_auth
|
||||
def old_live():
|
||||
id = extract_request(request.args.get('data', ''), 'id')
|
||||
live(id)
|
||||
return '', 204
|
||||
# ---------------- END DEPRECATED ----------------
|
||||
|
||||
|
||||
def register_views():
|
||||
app.register_blueprint(v2_images, url_prefix='/api/v2/plugins/images/')
|
||||
app.register_blueprint(v1_images, url_prefix='/api/images/')
|
@ -1,99 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2020 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# 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, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# 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, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
import logging
|
||||
|
||||
from openlp.core.api.endpoint.pluginhelpers import live, search, service
|
||||
from openlp.core.api.http import requires_auth
|
||||
from openlp.core.api.http.endpoint import Endpoint
|
||||
from openlp.core.api.http.errors import NotFound
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
media_endpoint = Endpoint('media')
|
||||
api_media_endpoint = Endpoint('api')
|
||||
|
||||
|
||||
@media_endpoint.route('search')
|
||||
def media_search(request):
|
||||
"""
|
||||
Handles requests for searching the media plugin
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return search(request, 'media', log)
|
||||
|
||||
|
||||
@media_endpoint.route('live')
|
||||
@requires_auth
|
||||
def media_live(request):
|
||||
"""
|
||||
Handles requests for making a song live
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return live(request, 'media', log)
|
||||
|
||||
|
||||
@media_endpoint.route('add')
|
||||
@requires_auth
|
||||
def media_service(request):
|
||||
"""
|
||||
Handles requests for adding a song to the service
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return service(request, 'media', log)
|
||||
|
||||
|
||||
@api_media_endpoint.route('media/search')
|
||||
def media_search_api(request):
|
||||
"""
|
||||
Handles requests for searching the media plugin
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return search(request, 'media', log)
|
||||
|
||||
|
||||
@api_media_endpoint.route('media/live')
|
||||
@requires_auth
|
||||
def media_live_api(request):
|
||||
"""
|
||||
Handles requests for making a song live
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return live(request, 'media', log)
|
||||
|
||||
|
||||
@api_media_endpoint.route('media/add')
|
||||
@requires_auth
|
||||
def media_service_api(request):
|
||||
"""
|
||||
Handles requests for adding a song to the service
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
try:
|
||||
return search(request, 'media', log)
|
||||
except NotFound:
|
||||
return {'results': {'items': []}}
|
@ -24,12 +24,11 @@ The Media plugin
|
||||
import logging
|
||||
|
||||
from openlp.core.state import State
|
||||
from openlp.core.api.http import register_endpoint
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
from openlp.core.lib import build_icon
|
||||
from openlp.core.lib.plugin import Plugin, StringContent
|
||||
from openlp.plugins.media.endpoint import api_media_endpoint, media_endpoint
|
||||
from openlp.plugins.media.remote import register_views
|
||||
from openlp.plugins.media.lib.mediaitem import MediaMediaItem
|
||||
|
||||
|
||||
@ -51,8 +50,7 @@ class MediaPlugin(Plugin):
|
||||
self.icon = build_icon(self.icon_path)
|
||||
# passed with drag and drop messages
|
||||
self.dnd_id = 'Media'
|
||||
register_endpoint(media_endpoint)
|
||||
register_endpoint(api_media_endpoint)
|
||||
register_views()
|
||||
State().add_service(self.name, self.weight, requires='mediacontroller', is_plugin=True)
|
||||
State().update_pre_conditions(self.name, self.check_pre_conditions())
|
||||
|
||||
|
118
openlp/plugins/media/remote.py
Normal file
118
openlp/plugins/media/remote.py
Normal file
@ -0,0 +1,118 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2019 OpenLP Developers #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# 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
|
||||
|
||||
from flask import abort, request, Blueprint, jsonify
|
||||
|
||||
from openlp.core.api import app
|
||||
from openlp.core.api.lib import login_required, extract_request, old_auth
|
||||
from openlp.core.lib.plugin import PluginStatus
|
||||
from openlp.core.common.registry import Registry
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
v1_media = Blueprint('v1-media-plugin', __name__)
|
||||
v2_media = Blueprint('v2-media-plugin', __name__)
|
||||
|
||||
|
||||
def search(text):
|
||||
plugin = Registry().get('plugin_manager').get_plugin_by_name('media')
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item and plugin.media_item.has_search:
|
||||
results = plugin.media_item.search(text, False)
|
||||
return results
|
||||
return None
|
||||
|
||||
|
||||
def live(id):
|
||||
plugin = Registry().get('plugin_manager').get_plugin_by_name('media')
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item:
|
||||
plugin.media_item.media_go_live.emit([id, True])
|
||||
|
||||
|
||||
def add(id):
|
||||
plugin = Registry().get('plugin_manager').get_plugin_by_name('media')
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item:
|
||||
item_id = plugin.media_item.create_item_from_id(id)
|
||||
plugin.media_item.media_add_to_service.emit([item_id, True])
|
||||
|
||||
|
||||
@v2_media.route('/search')
|
||||
@login_required
|
||||
def search_view():
|
||||
text = request.args.get('text', '')
|
||||
result = search(text)
|
||||
if result:
|
||||
return jsonify(result)
|
||||
return '', 400
|
||||
|
||||
|
||||
@v2_media.route('/add', methods=['POST'])
|
||||
@login_required
|
||||
def add_view():
|
||||
data = request.json
|
||||
if not data:
|
||||
abort(400)
|
||||
id = data.get('id', -1)
|
||||
add(id)
|
||||
return '', 204
|
||||
|
||||
|
||||
@v2_media.route('/live', methods=['POST'])
|
||||
@login_required
|
||||
def live_view():
|
||||
data = request.json
|
||||
if not data:
|
||||
abort(400)
|
||||
id = data.get('id', -1)
|
||||
live(id)
|
||||
return '', 204
|
||||
|
||||
|
||||
# ----------------- DEPRECATED --------------
|
||||
@v1_media.route('/search')
|
||||
@old_auth
|
||||
def old_search():
|
||||
text = extract_request(request.args.get('data', ''), 'text')
|
||||
return jsonify({'results': {'items': search(text)}})
|
||||
|
||||
|
||||
@v1_media.route('/add')
|
||||
@old_auth
|
||||
def old_add():
|
||||
id = extract_request(request.args.get('data', ''), 'id')
|
||||
add(id)
|
||||
return '', 204
|
||||
|
||||
|
||||
@v1_media.route('/live')
|
||||
@old_auth
|
||||
def old_live():
|
||||
id = extract_request(request.args.get('data', ''), 'id')
|
||||
live(id)
|
||||
return '', 204
|
||||
# ---------------- END DEPRECATED ----------------
|
||||
|
||||
|
||||
def register_views():
|
||||
app.register_blueprint(v2_media, url_prefix='/api/v2/plugins/media/')
|
||||
app.register_blueprint(v1_media, url_prefix='/api/media/')
|
@ -1,113 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2020 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# 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, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# 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, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
import logging
|
||||
|
||||
from openlp.core.api.endpoint.pluginhelpers import display_thumbnails, live, search, service
|
||||
from openlp.core.api.http import requires_auth
|
||||
from openlp.core.api.http.endpoint import Endpoint
|
||||
from openlp.core.api.http.errors import NotFound
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
presentations_endpoint = Endpoint('presentations')
|
||||
api_presentations_endpoint = Endpoint('api')
|
||||
|
||||
|
||||
# /presentations/thumbnails88x88/PA%20Rota.pdf/slide5.png
|
||||
@presentations_endpoint.route('thumbnails/{dimensions}/{file_name}/{slide}')
|
||||
def presentations_thumbnails(request, dimensions, file_name, slide):
|
||||
"""
|
||||
Return a presentation to a web page based on a URL
|
||||
:param request: Request object
|
||||
:param dimensions: the image size eg 88x88
|
||||
:param file_name: the file name of the image
|
||||
:param slide: the individual image name
|
||||
:return:
|
||||
"""
|
||||
return display_thumbnails(request, 'presentations', log, dimensions, file_name, slide)
|
||||
|
||||
|
||||
@presentations_endpoint.route('search')
|
||||
def presentations_search(request):
|
||||
"""
|
||||
Handles requests for searching the presentations plugin
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return search(request, 'presentations', log)
|
||||
|
||||
|
||||
@presentations_endpoint.route('live')
|
||||
@requires_auth
|
||||
def presentations_live(request):
|
||||
"""
|
||||
Handles requests for making a song live
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return live(request, 'presentations', log)
|
||||
|
||||
|
||||
@presentations_endpoint.route('add')
|
||||
@requires_auth
|
||||
def presentations_service(request):
|
||||
"""
|
||||
Handles requests for adding a song to the service
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return service(request, 'presentations', log)
|
||||
|
||||
|
||||
@api_presentations_endpoint.route('presentations/search')
|
||||
def presentations_search_api(request):
|
||||
"""
|
||||
Handles requests for searching the presentations plugin
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return search(request, 'presentations', log)
|
||||
|
||||
|
||||
@api_presentations_endpoint.route('presentations/live')
|
||||
@requires_auth
|
||||
def presentations_live_api(request):
|
||||
"""
|
||||
Handles requests for making a song live
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return live(request, 'presentations', log)
|
||||
|
||||
|
||||
@api_presentations_endpoint.route('presentations/add')
|
||||
@requires_auth
|
||||
def presentations_service_api(request):
|
||||
"""
|
||||
Handles requests for adding a song to the service
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
try:
|
||||
return search(request, 'presentations', log)
|
||||
except NotFound:
|
||||
return {'results': {'items': []}}
|
@ -25,7 +25,7 @@ presentations from a variety of document formats.
|
||||
import logging
|
||||
import os
|
||||
|
||||
from openlp.core.api.http import register_endpoint
|
||||
|
||||
from openlp.core.common import extension_loader
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.common.settings import Settings
|
||||
@ -33,7 +33,7 @@ from openlp.core.lib import build_icon
|
||||
from openlp.core.lib.plugin import Plugin, StringContent
|
||||
from openlp.core.state import State
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
from openlp.plugins.presentations.endpoint import api_presentations_endpoint, presentations_endpoint
|
||||
from openlp.plugins.presentations.remote import register_views
|
||||
from openlp.plugins.presentations.lib.presentationcontroller import PresentationController
|
||||
from openlp.plugins.presentations.lib.mediaitem import PresentationMediaItem
|
||||
from openlp.plugins.presentations.lib.presentationtab import PresentationTab
|
||||
@ -59,8 +59,7 @@ class PresentationPlugin(Plugin):
|
||||
self.weight = -8
|
||||
self.icon_path = UiIcons().presentation
|
||||
self.icon = build_icon(self.icon_path)
|
||||
register_endpoint(presentations_endpoint)
|
||||
register_endpoint(api_presentations_endpoint)
|
||||
register_views()
|
||||
State().add_service('presentation', self.weight, is_plugin=True)
|
||||
State().update_pre_conditions('presentation', self.check_pre_conditions())
|
||||
|
||||
|
118
openlp/plugins/presentations/remote.py
Normal file
118
openlp/plugins/presentations/remote.py
Normal file
@ -0,0 +1,118 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2019 OpenLP Developers #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# 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
|
||||
|
||||
from flask import abort, request, Blueprint, jsonify
|
||||
|
||||
from openlp.core.api import app
|
||||
from openlp.core.api.lib import login_required, extract_request, old_auth
|
||||
from openlp.core.lib.plugin import PluginStatus
|
||||
from openlp.core.common.registry import Registry
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
v1_presentations = Blueprint('v1-presentations-plugin', __name__)
|
||||
v2_presentations = Blueprint('v2-presentations-plugin', __name__)
|
||||
|
||||
|
||||
def search(text):
|
||||
plugin = Registry().get('plugin_manager').get_plugin_by_name('presentations')
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item and plugin.media_item.has_search:
|
||||
results = plugin.media_item.search(text, False)
|
||||
return results
|
||||
return None
|
||||
|
||||
|
||||
def live(id):
|
||||
plugin = Registry().get('plugin_manager').get_plugin_by_name('presentations')
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item:
|
||||
plugin.media_item.presentations_go_live.emit([id, True])
|
||||
|
||||
|
||||
def add(id):
|
||||
plugin = Registry().get('plugin_manager').get_plugin_by_name('presentations')
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item:
|
||||
item_id = plugin.media_item.create_item_from_id(id)
|
||||
plugin.media_item.presentations_add_to_service.emit([item_id, True])
|
||||
|
||||
|
||||
@v2_presentations.route('/search')
|
||||
@login_required
|
||||
def search_view():
|
||||
text = request.args.get('text', '')
|
||||
result = search(text)
|
||||
if result:
|
||||
return jsonify(result)
|
||||
return '', 400
|
||||
|
||||
|
||||
@v2_presentations.route('/add', methods=['POST'])
|
||||
@login_required
|
||||
def add_view():
|
||||
data = request.json
|
||||
if not data:
|
||||
abort(400)
|
||||
id = data.get('id', -1)
|
||||
add(id)
|
||||
return '', 204
|
||||
|
||||
|
||||
@v2_presentations.route('/live', methods=['POST'])
|
||||
@login_required
|
||||
def live_view():
|
||||
data = request.json
|
||||
if not data:
|
||||
abort(400)
|
||||
id = data.get('id', -1)
|
||||
live(id)
|
||||
return '', 204
|
||||
|
||||
|
||||
# ----------------- DEPRECATED --------------
|
||||
@v1_presentations.route('/search')
|
||||
@old_auth
|
||||
def old_search():
|
||||
text = extract_request(request.args.get('data', ''), 'text')
|
||||
return jsonify({'results': {'items': search(text)}})
|
||||
|
||||
|
||||
@v1_presentations.route('/add')
|
||||
@old_auth
|
||||
def old_add():
|
||||
id = extract_request(request.args.get('data', ''), 'id')
|
||||
add(id)
|
||||
return '', 204
|
||||
|
||||
|
||||
@v1_presentations.route('/live')
|
||||
@old_auth
|
||||
def old_live():
|
||||
id = extract_request(request.args.get('data', ''), 'id')
|
||||
live(id)
|
||||
return '', 204
|
||||
# ---------------- END DEPRECATED ----------------
|
||||
|
||||
|
||||
def register_views():
|
||||
app.register_blueprint(v2_presentations, url_prefix='/api/v2/plugins/presentations/')
|
||||
app.register_blueprint(v1_presentations, url_prefix='/api/presentations/')
|
@ -1,99 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2020 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# 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, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# 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, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
import logging
|
||||
|
||||
from openlp.core.api.endpoint.pluginhelpers import live, search, service
|
||||
from openlp.core.api.http import requires_auth
|
||||
from openlp.core.api.http.endpoint import Endpoint
|
||||
from openlp.core.api.http.errors import NotFound
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
songs_endpoint = Endpoint('songs')
|
||||
api_songs_endpoint = Endpoint('api')
|
||||
|
||||
|
||||
@songs_endpoint.route('search')
|
||||
def songs_search(request):
|
||||
"""
|
||||
Handles requests for searching the songs plugin
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return search(request, 'songs', log)
|
||||
|
||||
|
||||
@songs_endpoint.route('live')
|
||||
@requires_auth
|
||||
def songs_live(request):
|
||||
"""
|
||||
Handles requests for making a song live
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return live(request, 'songs', log)
|
||||
|
||||
|
||||
@songs_endpoint.route('add')
|
||||
@requires_auth
|
||||
def songs_service(request):
|
||||
"""
|
||||
Handles requests for adding a song to the service
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return service(request, 'songs', log)
|
||||
|
||||
|
||||
@api_songs_endpoint.route('songs/search')
|
||||
def songs_search_api(request):
|
||||
"""
|
||||
Handles requests for searching the songs plugin
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return search(request, 'songs', log)
|
||||
|
||||
|
||||
@api_songs_endpoint.route('songs/live')
|
||||
@requires_auth
|
||||
def songs_live_api(request):
|
||||
"""
|
||||
Handles requests for making a song live
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
return live(request, 'songs', log)
|
||||
|
||||
|
||||
@api_songs_endpoint.route('songs/add')
|
||||
@requires_auth
|
||||
def songs_service_api(request):
|
||||
"""
|
||||
Handles requests for adding a song to the service
|
||||
|
||||
:param request: The http request object.
|
||||
"""
|
||||
try:
|
||||
return service(request, 'songs', log)
|
||||
except NotFound:
|
||||
return {'results': {'items': []}}
|
@ -613,7 +613,11 @@ class SongMediaItem(MediaManagerItem):
|
||||
service_item.add_from_text(split_verse, verse_def)
|
||||
service_item.title = song.title
|
||||
author_list = self.generate_footer(service_item, song)
|
||||
service_item.data_string = {'title': song.search_title, 'authors': ', '.join(author_list)}
|
||||
service_item.data_string = {
|
||||
'title': song.search_title,
|
||||
'authors': ', '.join(author_list),
|
||||
'ccli_number': song.ccli_number
|
||||
}
|
||||
service_item.xml_version = self.open_lyrics.song_to_xml(song)
|
||||
# Add the audio file to the service item.
|
||||
if song.media_files:
|
||||
|
118
openlp/plugins/songs/remote.py
Normal file
118
openlp/plugins/songs/remote.py
Normal file
@ -0,0 +1,118 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2019 OpenLP Developers #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# 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
|
||||
|
||||
from flask import abort, request, Blueprint, jsonify
|
||||
|
||||
from openlp.core.api import app
|
||||
from openlp.core.api.lib import login_required, extract_request, old_auth
|
||||
from openlp.core.lib.plugin import PluginStatus
|
||||
from openlp.core.common.registry import Registry
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
v1_songs = Blueprint('v1-songs-plugin', __name__)
|
||||
v2_songs = Blueprint('v2-songs-plugin', __name__)
|
||||
|
||||
|
||||
def search(text):
|
||||
plugin = Registry().get('plugin_manager').get_plugin_by_name('songs')
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item and plugin.media_item.has_search:
|
||||
results = plugin.media_item.search(text, False)
|
||||
return results
|
||||
return None
|
||||
|
||||
|
||||
def live(id):
|
||||
plugin = Registry().get('plugin_manager').get_plugin_by_name('songs')
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item:
|
||||
plugin.media_item.songs_go_live.emit([id, True])
|
||||
|
||||
|
||||
def add(id):
|
||||
plugin = Registry().get('plugin_manager').get_plugin_by_name('songs')
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item:
|
||||
item_id = plugin.media_item.create_item_from_id(id)
|
||||
plugin.media_item.songs_add_to_service.emit([item_id, True])
|
||||
|
||||
|
||||
@v2_songs.route('/search')
|
||||
@login_required
|
||||
def search_view():
|
||||
text = request.args.get('text', '')
|
||||
result = search(text)
|
||||
if result:
|
||||
return jsonify(result)
|
||||
return '', 400
|
||||
|
||||
|
||||
@v2_songs.route('/add', methods=['POST'])
|
||||
@login_required
|
||||
def add_view():
|
||||
data = request.json
|
||||
if not data:
|
||||
abort(400)
|
||||
id = data.get('id', -1)
|
||||
add(id)
|
||||
return '', 204
|
||||
|
||||
|
||||
@v2_songs.route('/live', methods=['POST'])
|
||||
@login_required
|
||||
def live_view():
|
||||
data = request.json
|
||||
if not data:
|
||||
abort(400)
|
||||
id = data.get('id', -1)
|
||||
live(id)
|
||||
return '', 204
|
||||
|
||||
|
||||
# ----------------- DEPRECATED --------------
|
||||
@v1_songs.route('/search')
|
||||
@old_auth
|
||||
def old_search():
|
||||
text = extract_request(request.args.get('data', ''), 'text')
|
||||
return jsonify({'results': {'items': search(text)}})
|
||||
|
||||
|
||||
@v1_songs.route('/add')
|
||||
@old_auth
|
||||
def old_add():
|
||||
id = extract_request(request.args.get('data', ''), 'id')
|
||||
add(id)
|
||||
return '', 204
|
||||
|
||||
|
||||
@v1_songs.route('/live')
|
||||
@old_auth
|
||||
def old_live():
|
||||
id = extract_request(request.args.get('data', ''), 'id')
|
||||
live(id)
|
||||
return '', 204
|
||||
# ---------------- END DEPRECATED ----------------
|
||||
|
||||
|
||||
def register_views():
|
||||
app.register_blueprint(v2_songs, url_prefix='/api/v2/plugins/songs/')
|
||||
app.register_blueprint(v1_songs, url_prefix='/api/songs/')
|
@ -31,7 +31,6 @@ from tempfile import gettempdir
|
||||
from PyQt5 import QtCore, QtWidgets
|
||||
|
||||
from openlp.core.state import State
|
||||
from openlp.core.api.http import register_endpoint
|
||||
from openlp.core.common.actions import ActionList
|
||||
from openlp.core.common.i18n import UiStrings, translate
|
||||
from openlp.core.common.registry import Registry
|
||||
@ -41,7 +40,7 @@ from openlp.core.lib.plugin import Plugin, StringContent
|
||||
from openlp.core.lib.ui import create_action
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
from openlp.plugins.songs import reporting
|
||||
from openlp.plugins.songs.endpoint import api_songs_endpoint, songs_endpoint
|
||||
from openlp.plugins.songs.remote import register_views
|
||||
from openlp.plugins.songs.forms.duplicatesongremovalform import DuplicateSongRemovalForm
|
||||
from openlp.plugins.songs.forms.songselectform import SongSelectForm
|
||||
from openlp.plugins.songs.lib import clean_song, upgrade
|
||||
@ -128,8 +127,7 @@ class SongsPlugin(Plugin):
|
||||
self.icon = build_icon(self.icon_path)
|
||||
self.songselect_form = None
|
||||
self.settings.extend_default_settings(song_footer)
|
||||
register_endpoint(songs_endpoint)
|
||||
register_endpoint(api_songs_endpoint)
|
||||
register_views()
|
||||
State().add_service(self.name, self.weight, is_plugin=True)
|
||||
State().update_pre_conditions(self.name, self.check_pre_conditions())
|
||||
if not self.settings.value('songs/last import type'):
|
||||
|
@ -83,6 +83,8 @@ MODULES = [
|
||||
'alembic',
|
||||
'lxml',
|
||||
'chardet',
|
||||
'flask',
|
||||
'flask_cors',
|
||||
'bs4',
|
||||
'mako',
|
||||
'websockets',
|
||||
|
2
setup.py
2
setup.py
@ -102,6 +102,8 @@ using a computer and a data projector.""",
|
||||
'chardet',
|
||||
'dbus-python; platform_system=="Linux"',
|
||||
'distro; platform_system=="Linux"',
|
||||
'flask',
|
||||
'flask-cors',
|
||||
'lxml',
|
||||
'Mako',
|
||||
'pymediainfo >= 2.2',
|
||||
|
@ -24,11 +24,12 @@ from unittest.mock import MagicMock, patch
|
||||
|
||||
from PyQt5 import QtCore
|
||||
|
||||
from openlp.core.api import app as flask_app
|
||||
from openlp.core.state import State
|
||||
|
||||
# Mock QtWebEngineWidgets
|
||||
# sys.modules['PyQt5.QtWebEngineWidgets'] = MagicMock()
|
||||
|
||||
from openlp.core.api.endpoint.controller import controller_direction, controller_text
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.display.screens import ScreenList
|
||||
from openlp.core.lib.serviceitem import ServiceItem
|
||||
@ -75,6 +76,8 @@ class TestController(TestCase):
|
||||
self.mocked_renderer.format_slide = self.mocked_slide_formater
|
||||
Registry().register('live_controller', self.mocked_live_controller)
|
||||
Registry().register('renderer', self.mocked_renderer)
|
||||
flask_app.config['TESTING'] = True
|
||||
self.client = flask_app.test_client()
|
||||
Registry().register('settings', MagicMock(**{'value.return_value': 'english'}))
|
||||
|
||||
def test_controller_text_empty(self):
|
||||
@ -88,7 +91,7 @@ class TestController(TestCase):
|
||||
self.mocked_live_controller.service_item = mocked_service_item
|
||||
|
||||
# WHEN: I trigger the method
|
||||
ret = controller_text(MagicMock())
|
||||
ret = self.client.get('/api/controller/live/text').get_json()
|
||||
|
||||
# THEN: I get a basic set of results
|
||||
assert ret['results']['item'] == 'mock-service-item'
|
||||
@ -108,7 +111,7 @@ class TestController(TestCase):
|
||||
self.mocked_live_controller.service_item.set_from_service(line)
|
||||
self.mocked_live_controller.service_item._create_slides()
|
||||
# WHEN: I trigger the method
|
||||
ret = controller_text("SomeText")
|
||||
ret = self.client.get('/api/controller/live/text').get_json()
|
||||
|
||||
# THEN: I get a basic set of results
|
||||
results = ret['results']
|
||||
@ -125,8 +128,7 @@ class TestController(TestCase):
|
||||
self.mocked_live_controller.service_item = MagicMock()
|
||||
|
||||
# WHEN: I trigger the method
|
||||
controller_direction(None, 'live', 'next')
|
||||
|
||||
self.client.get('/api/controller/live/next')
|
||||
# THEN: The correct method is called
|
||||
mocked_emit.assert_called_once_with()
|
||||
|
||||
@ -140,7 +142,6 @@ class TestController(TestCase):
|
||||
self.mocked_live_controller.service_item = MagicMock()
|
||||
|
||||
# WHEN: I trigger the method
|
||||
controller_direction(None, 'live', 'previous')
|
||||
|
||||
self.client.get('/api/controller/live/previous')
|
||||
# THEN: The correct method is called
|
||||
mocked_emit.assert_called_once_with()
|
||||
|
@ -1,43 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2020 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# 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, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# 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, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
"""
|
||||
Functional tests to test the remote index
|
||||
"""
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from openlp.core.api.endpoint.core import TRANSLATED_STRINGS
|
||||
from openlp.core.api.endpoint.remote import index
|
||||
|
||||
|
||||
@patch('openlp.core.api.endpoint.remote.remote_endpoint')
|
||||
def test_index(mocked_endpoint):
|
||||
"""
|
||||
Test the index method of the remote
|
||||
"""
|
||||
# GIVEN: A mocked Endpoint
|
||||
mocked_endpoint.render_template.return_value = 'test template'
|
||||
|
||||
# WHEN: index is called
|
||||
result = index(MagicMock(), 'index')
|
||||
|
||||
# THEN: The result should be "test template" and the right methods should have been called
|
||||
mocked_endpoint.render_template.assert_called_once_with('index.mako', **TRANSLATED_STRINGS)
|
||||
assert result == 'test template'
|
@ -1,60 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2020 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# 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, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# 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, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
"""
|
||||
Functional tests to test the API Error Class.
|
||||
"""
|
||||
from openlp.core.api.http.errors import HttpError, NotFound, ServerError
|
||||
|
||||
|
||||
def test_http_error():
|
||||
"""
|
||||
Test the HTTPError class
|
||||
"""
|
||||
# GIVEN: An HTTPError class
|
||||
# WHEN: An instance is created
|
||||
error = HttpError(400, 'Access Denied')
|
||||
|
||||
# THEN: The to_response() method should return the correct information
|
||||
assert error.to_response() == ('Access Denied', 400), 'to_response() should have returned the correct info'
|
||||
|
||||
|
||||
def test_not_found():
|
||||
"""
|
||||
Test the Not Found error displays the correct information
|
||||
"""
|
||||
# GIVEN: A NotFound class
|
||||
# WHEN: An instance is created
|
||||
error = NotFound()
|
||||
|
||||
# THEN: The to_response() method should return the correct information
|
||||
assert error.to_response() == ('Not Found', 404), 'to_response() should have returned the correct info'
|
||||
|
||||
|
||||
def test_server_error():
|
||||
"""
|
||||
Test the server error displays the correct information
|
||||
"""
|
||||
# GIVEN: A ServerError class
|
||||
# WHEN: An instance of the class is created
|
||||
error = ServerError()
|
||||
|
||||
# THEN: The to_response() method should return the correct information
|
||||
assert error.to_response() == ('Server Error', 500), 'to_response() should have returned the correct info'
|
@ -1,88 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2020 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# 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, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# 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, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
"""
|
||||
Functional test the routing code.
|
||||
"""
|
||||
import os
|
||||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from openlp.core.api.http import register_endpoint, application
|
||||
from openlp.core.api.http.endpoint import Endpoint
|
||||
from openlp.core.api.http.errors import NotFound
|
||||
|
||||
|
||||
ROOT_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
test_endpoint = Endpoint('test', template_dir=ROOT_DIR, static_dir=ROOT_DIR)
|
||||
|
||||
|
||||
class TestRouting(TestCase):
|
||||
"""
|
||||
Test the HTTP routing
|
||||
"""
|
||||
def setUp(self):
|
||||
"""
|
||||
Convert the application to a test application
|
||||
:return:
|
||||
"""
|
||||
for route, views in application.route_map.items():
|
||||
application.route_map[route]['GET'] = MagicMock()
|
||||
|
||||
def test_routing(self):
|
||||
"""
|
||||
Test the Routing in the new application via dispatch
|
||||
:return:
|
||||
"""
|
||||
# GIVE: I try to request and
|
||||
# WHEN: when the URL is not correct and dispatch called
|
||||
rqst = MagicMock()
|
||||
rqst.path = '/test/api'
|
||||
rqst.method = 'GET'
|
||||
with self.assertRaises(NotFound) as context:
|
||||
application.dispatch(rqst)
|
||||
|
||||
# THEN: the not found returned
|
||||
assert context.exception.args[0] == 'Not Found', 'URL not found in dispatcher'
|
||||
|
||||
# WHEN: when the URL is correct and dispatch called
|
||||
rqst = MagicMock()
|
||||
rqst.path = '/test/image'
|
||||
rqst.method = 'GET'
|
||||
application.dispatch(rqst)
|
||||
# THEN: the not found id called
|
||||
route_key = next(iter(application.route_map))
|
||||
assert '/image' in route_key
|
||||
assert 1 == application.route_map[route_key]['GET'].call_count, \
|
||||
'main_index function should have been called'
|
||||
|
||||
|
||||
@test_endpoint.route('image')
|
||||
def image(request):
|
||||
pass
|
||||
|
||||
|
||||
@test_endpoint.route('')
|
||||
def index(request):
|
||||
pass
|
||||
|
||||
|
||||
register_endpoint(test_endpoint)
|
0
tests/functional/openlp_core/api/v2/__init__.py
Normal file
0
tests/functional/openlp_core/api/v2/__init__.py
Normal file
9
tests/functional/openlp_core/api/v2/conftest.py
Normal file
9
tests/functional/openlp_core/api/v2/conftest.py
Normal file
@ -0,0 +1,9 @@
|
||||
import pytest
|
||||
|
||||
from openlp.core.api import app as flask_app
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def flask_client():
|
||||
flask_app.config['TESTING'] = True
|
||||
return flask_app.test_client()
|
57
tests/functional/openlp_core/api/v2/test_controller.py
Normal file
57
tests/functional/openlp_core/api/v2/test_controller.py
Normal file
@ -0,0 +1,57 @@
|
||||
import pytest
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.common.settings import Settings
|
||||
|
||||
|
||||
def test_retrieve_live_item(flask_client):
|
||||
pytest.skip()
|
||||
res = flask_client.get('/api/v2/controller/live-item').get_json()
|
||||
assert len(res) == 0
|
||||
|
||||
|
||||
def test_controller_set_requires_login(flask_client):
|
||||
pytest.skip('Need to figure out how to patch settings for one test only')
|
||||
# Settings().setValue('api/authentication enabled', True)
|
||||
res = flask_client.post('/api/v2/controller/show', json=dict())
|
||||
Settings().setValue('api/authentication enabled', False)
|
||||
assert res.status_code == 401
|
||||
|
||||
|
||||
def test_controller_set_does_not_accept_get(flask_client):
|
||||
res = flask_client.get('/api/v2/controller/show')
|
||||
assert res.status_code == 405
|
||||
|
||||
|
||||
def test_controller_set_calls_live_controller(flask_client):
|
||||
fake_live_controller = MagicMock()
|
||||
Registry.create().register('live_controller', fake_live_controller)
|
||||
res = flask_client.post('/api/v2/controller/show', json=dict(id=400))
|
||||
assert res.status_code == 204
|
||||
fake_live_controller.slidecontroller_live_set.emit.assert_called_once_with([400])
|
||||
|
||||
|
||||
def test_controller_direction_requires_login(flask_client):
|
||||
Settings().setValue('api/authentication enabled', True)
|
||||
res = flask_client.post('/api/v2/controller/progress', json=dict())
|
||||
Settings().setValue('api/authentication enabled', False)
|
||||
assert res.status_code == 401
|
||||
|
||||
|
||||
def test_controller_direction_does_not_accept_get(flask_client):
|
||||
res = flask_client.get('/api/v2/controller/progress')
|
||||
assert res.status_code == 405
|
||||
|
||||
|
||||
def test_controller_direction_does_fails_on_wrong_data(flask_client):
|
||||
res = flask_client.post('/api/v2/controller/progress', json=dict(action='foo'))
|
||||
assert res.status_code == 400
|
||||
|
||||
|
||||
def test_controller_direction_calls_service_manager(flask_client):
|
||||
fake_live_controller = MagicMock()
|
||||
Registry.create().register('live_controller', fake_live_controller)
|
||||
res = flask_client.post('/api/v2/controller/progress', json=dict(action='next'))
|
||||
assert res.status_code == 204
|
||||
fake_live_controller.slidecontroller_live_next.emit.assert_called_once()
|
113
tests/functional/openlp_core/api/v2/test_core.py
Normal file
113
tests/functional/openlp_core/api/v2/test_core.py
Normal file
@ -0,0 +1,113 @@
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.state import State
|
||||
from openlp.core.lib.plugin import PluginStatus, StringContent
|
||||
|
||||
|
||||
def test_plugins_returns_list(flask_client):
|
||||
State().load_settings()
|
||||
res = flask_client.get('/api/v2/core/plugins').get_json()
|
||||
assert len(res) == 0
|
||||
|
||||
class FakeMediaItem:
|
||||
has_search = True
|
||||
|
||||
class FakePlugin:
|
||||
name = 'Faked'
|
||||
is_plugin = True
|
||||
status = PluginStatus.Active
|
||||
media_item = FakeMediaItem()
|
||||
text_strings = {StringContent.Name: {'plural': 'just a text'}}
|
||||
plugin = FakePlugin()
|
||||
State().modules['testplug'] = plugin
|
||||
Registry.create().register('testplug_plugin', plugin)
|
||||
res = flask_client.get('/api/v2/core/plugins').get_json()
|
||||
assert len(res) == 1
|
||||
assert res[0]['key'] == plugin.name
|
||||
assert res[0]['name'] == plugin.text_strings[StringContent.Name]['plural']
|
||||
|
||||
|
||||
def test_system_information(flask_client):
|
||||
Settings().setValue('api/authentication enabled', False)
|
||||
res = flask_client.get('/api/v2/core/system').get_json()
|
||||
assert res['websocket_port'] > 0
|
||||
assert not res['login_required']
|
||||
|
||||
|
||||
def test_poll(flask_client):
|
||||
class FakePoller:
|
||||
def poll(self):
|
||||
return {'foo': 'bar'}
|
||||
Registry.create().register('poller', FakePoller())
|
||||
res = flask_client.get('/api/v2/core/poll').get_json()
|
||||
assert res['foo'] == 'bar'
|
||||
|
||||
|
||||
def test_login_get_is_refused(flask_client):
|
||||
res = flask_client.get('/api/v2/core/login')
|
||||
assert res.status_code == 405
|
||||
|
||||
|
||||
def test_login_without_data_returns_400(flask_client):
|
||||
res = flask_client.post('/api/v2/core/login')
|
||||
assert res.status_code == 400
|
||||
|
||||
|
||||
def test_login_with_invalid_credetials_returns_401(flask_client):
|
||||
res = flask_client.post('/api/v2/core/login', json=dict(username='openlp', password='invalid'))
|
||||
assert res.status_code == 401
|
||||
|
||||
|
||||
def test_login_with_valid_credetials_returns_token(flask_client):
|
||||
Registry().register('authentication_token', 'foobar')
|
||||
res = flask_client.post('/api/v2/core/login', json=dict(username='openlp', password='password'))
|
||||
assert res.status_code == 200
|
||||
assert res.get_json()['token'] == 'foobar'
|
||||
|
||||
|
||||
def test_retrieving_image(flask_client):
|
||||
class FakeController:
|
||||
def grab_maindisplay(self):
|
||||
class FakeImage:
|
||||
def save(self, first, second):
|
||||
pass
|
||||
return FakeImage()
|
||||
Registry.create().register('live_controller', FakeController())
|
||||
res = flask_client.get('/api/v2/core/live-image').get_json()
|
||||
assert res['binary_image'] != ''
|
||||
|
||||
|
||||
def test_toggle_display_requires_login(flask_client):
|
||||
Settings().setValue('api/authentication enabled', True)
|
||||
res = flask_client.post('/api/v2/core/display')
|
||||
Settings().setValue('api/authentication enabled', False)
|
||||
assert res.status_code == 401
|
||||
|
||||
|
||||
def test_toggle_display_does_not_allow_get(flask_client):
|
||||
res = flask_client.get('/api/v2/core/display')
|
||||
assert res.status_code == 405
|
||||
|
||||
|
||||
def test_toggle_display_invalid_action(flask_client):
|
||||
res = flask_client.post('/api/v2/core/display', json={'display': 'foo'})
|
||||
assert res.status_code == 400
|
||||
|
||||
|
||||
def test_toggle_display_valid_action_updates_controller(flask_client):
|
||||
class FakeController:
|
||||
class Emitter:
|
||||
def emit(self, value):
|
||||
self.set = value
|
||||
slidecontroller_toggle_display = Emitter()
|
||||
controller = FakeController()
|
||||
Registry.create().register('live_controller', controller)
|
||||
res = flask_client.post('/api/v2/core/display', json={'display': 'show'})
|
||||
assert res.status_code == 204
|
||||
assert controller.slidecontroller_toggle_display.set == 'show'
|
||||
|
||||
|
||||
def test_cors_headers_are_present(flask_client):
|
||||
res = flask_client.get('/api/v2/core/system')
|
||||
assert 'Access-Control-Allow-Origin' in res.headers
|
||||
assert res.headers['Access-Control-Allow-Origin'] == '*'
|
56
tests/functional/openlp_core/api/v2/test_service.py
Normal file
56
tests/functional/openlp_core/api/v2/test_service.py
Normal file
@ -0,0 +1,56 @@
|
||||
import pytest
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.common.settings import Settings
|
||||
|
||||
|
||||
def test_retrieve_service_items(flask_client):
|
||||
pytest.skip()
|
||||
res = flask_client.get('/api/v2/service/items').get_json()
|
||||
assert len(res) == 0
|
||||
|
||||
|
||||
def test_service_set_requires_login(flask_client):
|
||||
Settings().setValue('api/authentication enabled', True)
|
||||
res = flask_client.post('/api/v2/service/show', json=dict())
|
||||
Settings().setValue('api/authentication enabled', False)
|
||||
assert res.status_code == 401
|
||||
|
||||
|
||||
def test_service_set_does_not_accept_get(flask_client):
|
||||
res = flask_client.get('/api/v2/service/show')
|
||||
assert res.status_code == 405
|
||||
|
||||
|
||||
def test_service_set_calls_service_manager(flask_client):
|
||||
fake_service_manager = MagicMock()
|
||||
Registry.create().register('service_manager', fake_service_manager)
|
||||
res = flask_client.post('/api/v2/service/show', json=dict(id=400))
|
||||
assert res.status_code == 204
|
||||
fake_service_manager.set_item.assert_called_once_with(400)
|
||||
|
||||
|
||||
def test_service_direction_requires_login(flask_client):
|
||||
Settings().setValue('api/authentication enabled', True)
|
||||
res = flask_client.post('/api/v2/service/progress', json=dict())
|
||||
Settings().setValue('api/authentication enabled', False)
|
||||
assert res.status_code == 401
|
||||
|
||||
|
||||
def test_service_direction_does_not_accept_get(flask_client):
|
||||
res = flask_client.get('/api/v2/service/progress')
|
||||
assert res.status_code == 405
|
||||
|
||||
|
||||
def test_service_direction_does_fails_on_wrong_data(flask_client):
|
||||
res = flask_client.post('/api/v2/service/progress', json=dict(action='foo'))
|
||||
assert res.status_code == 400
|
||||
|
||||
|
||||
def test_service_direction_calls_service_manager(flask_client):
|
||||
fake_service_manager = MagicMock()
|
||||
Registry.create().register('service_manager', fake_service_manager)
|
||||
res = flask_client.post('/api/v2/service/progress', json=dict(action='next'))
|
||||
assert res.status_code == 204
|
||||
fake_service_manager.servicemanager_next_item.emit.assert_called_once()
|
@ -161,8 +161,8 @@ class TestMainWindow(TestCase, TestMixin):
|
||||
# WHEN: you check the started functions
|
||||
|
||||
# THEN: the following registry functions should have been registered
|
||||
expected_service_list = ['application', 'main_window', 'http_server', 'settings_form', 'service_manager',
|
||||
'theme_manager', 'projector_manager']
|
||||
expected_service_list = ['application', 'main_window', 'http_server', 'authentication_token', 'settings_form',
|
||||
'service_manager', 'theme_manager', 'projector_manager']
|
||||
expected_functions_list = ['bootstrap_initialise', 'bootstrap_post_set_up', 'bootstrap_completion',
|
||||
'theme_update_global', 'config_screen_changed']
|
||||
assert list(self.registry.service_list.keys()) == expected_service_list, \
|
||||
|
Loading…
Reference in New Issue
Block a user