Update remote API to use Flask, and be more RESTful

This commit is contained in:
Simon Hanna 2020-01-27 22:57:58 +00:00 committed by Raoul Snyman
parent 52d811331c
commit ef2f798f6f
62 changed files with 1602 additions and 1991 deletions

View File

@ -18,3 +18,21 @@
# You should have received a copy of the GNU General Public License # # You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. # # 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)

View File

@ -65,6 +65,5 @@ def download_and_check(callback=None):
file_size = get_url_file_size('https://get.openlp.org/webclient/site.zip') file_size = get_url_file_size('https://get.openlp.org/webclient/site.zip')
callback.setRange(0, file_size) callback.setRange(0, file_size)
if download_file(callback, 'https://get.openlp.org/webclient/site.zip', if download_file(callback, 'https://get.openlp.org/webclient/site.zip',
AppLocation.get_section_data_path('remotes') / 'site.zip', AppLocation.get_section_data_path('remotes') / 'site.zip'):
sha256=sha256):
deploy_zipfile(AppLocation.get_section_data_path('remotes'), 'site.zip') deploy_zipfile(AppLocation.get_section_data_path('remotes'), 'site.zip')

View File

@ -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
"""

View File

@ -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}}

View File

@ -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 &amp; 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}

View File

@ -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')

View File

@ -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)

View File

@ -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

View File

@ -24,39 +24,8 @@ from functools import wraps
from webob import Response from webob import Response
from openlp.core.api.http.wsgiapp import WSGIApplication
from openlp.core.common.settings import Settings 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): def check_auth(auth):
""" """

View File

@ -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)

View File

@ -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')

View File

@ -24,16 +24,12 @@ with OpenLP. It uses JSON to communicate with the remotes.
""" """
import logging import logging
import time import time
from secrets import token_hex
from PyQt5 import QtCore, QtWidgets from PyQt5 import QtCore, QtWidgets
from waitress.server import create_server from waitress.server import create_server
from openlp.core.api.deploy import download_and_check, download_sha256 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.api.poll import Poller
from openlp.core.common.applocation import AppLocation from openlp.core.common.applocation import AppLocation
from openlp.core.common.i18n import UiStrings, translate 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.common.settings import Settings
from openlp.core.threading import ThreadWorker, run_thread from openlp.core.threading import ThreadWorker, run_thread
from openlp.core.api import app as application
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -59,6 +56,7 @@ class HttpWorker(ThreadWorker):
port = Settings().value('api/port') port = Settings().value('api/port')
Registry().execute('get_website_version') Registry().execute('get_website_version')
try: try:
application.static_folder = str(AppLocation.get_section_data_path('remotes') / 'static')
self.server = create_server(application, host=address, port=port) self.server = create_server(application, host=address, port=port)
self.server.run() self.server.run()
except OSError: except OSError:
@ -85,6 +83,7 @@ class HttpServer(RegistryBase, RegistryProperties, LogMixin):
Initialise the http server, and start the http server Initialise the http server, and start the http server
""" """
super(HttpServer, self).__init__(parent) super(HttpServer, self).__init__(parent)
Registry().register('authentication_token', token_hex())
if not Registry().get_flag('no_web_server'): if not Registry().get_flag('no_web_server'):
worker = HttpWorker() worker = HttpWorker()
run_thread(worker, 'http_server') run_thread(worker, 'http_server')
@ -96,31 +95,9 @@ class HttpServer(RegistryBase, RegistryProperties, LogMixin):
""" """
Register the poll return service and start the servers. Register the poll return service and start the servers.
""" """
self.initialise() create_paths(AppLocation.get_section_data_path('remotes'))
self.poller = Poller() self.poller = Poller()
Registry().register('poller', self.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): def first_time(self):
""" """

View File

@ -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
View 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
View 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)

View File

View 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)

View 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()

View 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})

View 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()

View 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/')

View 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

View 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})

View 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

View File

@ -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}}

View File

@ -33,7 +33,6 @@ from subprocess import check_output
from PyQt5 import QtCore from PyQt5 import QtCore
from openlp.core.state import State 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 import is_linux, is_macosx
from openlp.core.common.i18n import translate from openlp.core.common.i18n import translate
from openlp.core.common.mixins import LogMixin, RegistryProperties 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.lib.ui import critical_error_message_box
from openlp.core.ui import DisplayControllerType 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 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 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_hide', self.media_hide)
Registry().register_function('songs_blank', self.media_blank) Registry().register_function('songs_blank', self.media_blank)
Registry().register_function('songs_unblank', self.media_unblank) Registry().register_function('songs_unblank', self.media_unblank)
register_endpoint(media_endpoint) register_views()
def bootstrap_initialise(self): def bootstrap_initialise(self):
""" """

View 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/')

View File

@ -1238,8 +1238,9 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
win_image.setDevicePixelRatio(self.preview_display.devicePixelRatio()) win_image.setDevicePixelRatio(self.preview_display.devicePixelRatio())
# self.slide_preview.setPixmap(win_image) # self.slide_preview.setPixmap(win_image)
self.slide_image = 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) self.preview_display.set_single_image_data('#000', base64_image)
return self.slide_image
def on_slide_selected_next_action(self, checked): def on_slide_selected_next_action(self, checked):
""" """

View File

@ -22,7 +22,6 @@
import logging import logging
from openlp.core.state import State 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.actions import ActionList
from openlp.core.common.i18n import UiStrings, translate from openlp.core.common.i18n import UiStrings, translate
from openlp.core.common.settings import Settings 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.theme import VerticalType
from openlp.core.lib.ui import create_action from openlp.core.lib.ui import create_action
from openlp.core.ui.icons import UiIcons 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.forms.alertform import AlertForm
from openlp.plugins.alerts.lib.alertsmanager import AlertsManager from openlp.plugins.alerts.lib.alertsmanager import AlertsManager
from openlp.plugins.alerts.lib.alertstab import AlertsTab from openlp.plugins.alerts.lib.alertstab import AlertsTab
@ -129,8 +128,7 @@ class AlertsPlugin(Plugin):
AlertsManager(self) AlertsManager(self)
self.manager = Manager('alerts', init_schema) self.manager = Manager('alerts', init_schema)
self.alert_form = AlertForm(self) self.alert_form = AlertForm(self)
register_endpoint(alerts_endpoint) register_views()
register_endpoint(api_alerts_endpoint)
State().add_service(self.name, self.weight, is_plugin=True) State().add_service(self.name, self.weight, is_plugin=True)
State().update_pre_conditions(self.name, self.check_pre_conditions()) State().update_pre_conditions(self.name, self.check_pre_conditions())

View File

@ -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}}

View 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')

View File

@ -22,13 +22,12 @@
import logging import logging
from openlp.core.state import State 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.actions import ActionList
from openlp.core.common.i18n import UiStrings, translate from openlp.core.common.i18n import UiStrings, translate
from openlp.core.ui.icons import UiIcons from openlp.core.ui.icons import UiIcons
from openlp.core.lib.plugin import Plugin, StringContent from openlp.core.lib.plugin import Plugin, StringContent
from openlp.core.lib.ui import create_action 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.biblestab import BiblesTab
from openlp.plugins.bibles.lib.manager import BibleManager from openlp.plugins.bibles.lib.manager import BibleManager
from openlp.plugins.bibles.lib.mediaitem import BibleMediaItem from openlp.plugins.bibles.lib.mediaitem import BibleMediaItem
@ -49,8 +48,7 @@ class BiblePlugin(Plugin):
self.icon_path = UiIcons().bible self.icon_path = UiIcons().bible
self.icon = UiIcons().bible self.icon = UiIcons().bible
self.manager = BibleManager(self) self.manager = BibleManager(self)
register_endpoint(bibles_endpoint) register_views()
register_endpoint(api_bibles_endpoint)
State().add_service('bible', self.weight, is_plugin=True) State().add_service('bible', self.weight, is_plugin=True)
State().update_pre_conditions('bible', self.check_pre_conditions()) State().update_pre_conditions('bible', self.check_pre_conditions())

View File

@ -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': []}}

View 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')

View File

@ -26,13 +26,12 @@ for the Custom Slides plugin.
import logging import logging
from openlp.core.state import State 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.i18n import translate
from openlp.core.lib import build_icon from openlp.core.lib import build_icon
from openlp.core.lib.db import Manager from openlp.core.lib.db import Manager
from openlp.core.lib.plugin import Plugin, StringContent from openlp.core.lib.plugin import Plugin, StringContent
from openlp.core.ui.icons import UiIcons 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.db import CustomSlide, init_schema
from openlp.plugins.custom.lib.mediaitem import CustomMediaItem from openlp.plugins.custom.lib.mediaitem import CustomMediaItem
from openlp.plugins.custom.lib.customtab import CustomTab from openlp.plugins.custom.lib.customtab import CustomTab
@ -56,8 +55,7 @@ class CustomPlugin(Plugin):
self.db_manager = Manager('custom', init_schema) self.db_manager = Manager('custom', init_schema)
self.icon_path = UiIcons().clone self.icon_path = UiIcons().clone
self.icon = build_icon(self.icon_path) self.icon = build_icon(self.icon_path)
register_endpoint(custom_endpoint) register_views()
register_endpoint(api_custom_endpoint)
State().add_service(self.name, self.weight, is_plugin=True) State().add_service(self.name, self.weight, is_plugin=True)
State().update_pre_conditions(self.name, self.check_pre_conditions()) State().update_pre_conditions(self.name, self.check_pre_conditions())

View File

@ -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': []}}

View 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/')

View File

@ -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': []}}

View File

@ -24,14 +24,13 @@ import logging
from PyQt5 import QtGui from PyQt5 import QtGui
from openlp.core.state import State 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.i18n import translate
from openlp.core.common.settings import Settings from openlp.core.common.settings import Settings
from openlp.core.lib import ImageSource, build_icon from openlp.core.lib import ImageSource, build_icon
from openlp.core.lib.db import Manager from openlp.core.lib.db import Manager
from openlp.core.lib.plugin import Plugin, StringContent from openlp.core.lib.plugin import Plugin, StringContent
from openlp.core.ui.icons import UiIcons 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 import upgrade
from openlp.plugins.images.lib.mediaitem import ImageMediaItem from openlp.plugins.images.lib.mediaitem import ImageMediaItem
from openlp.plugins.images.lib.imagetab import ImageTab from openlp.plugins.images.lib.imagetab import ImageTab
@ -50,8 +49,7 @@ class ImagePlugin(Plugin):
self.weight = -7 self.weight = -7
self.icon_path = UiIcons().picture self.icon_path = UiIcons().picture
self.icon = build_icon(self.icon_path) self.icon = build_icon(self.icon_path)
register_endpoint(images_endpoint) register_views()
register_endpoint(api_images_endpoint)
State().add_service('image', self.weight, is_plugin=True) State().add_service('image', self.weight, is_plugin=True)
State().update_pre_conditions('image', self.check_pre_conditions()) State().update_pre_conditions('image', self.check_pre_conditions())

View 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/')

View File

@ -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': []}}

View File

@ -24,12 +24,11 @@ The Media plugin
import logging import logging
from openlp.core.state import State 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.i18n import translate
from openlp.core.ui.icons import UiIcons from openlp.core.ui.icons import UiIcons
from openlp.core.lib import build_icon from openlp.core.lib import build_icon
from openlp.core.lib.plugin import Plugin, StringContent 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 from openlp.plugins.media.lib.mediaitem import MediaMediaItem
@ -51,8 +50,7 @@ class MediaPlugin(Plugin):
self.icon = build_icon(self.icon_path) self.icon = build_icon(self.icon_path)
# passed with drag and drop messages # passed with drag and drop messages
self.dnd_id = 'Media' self.dnd_id = 'Media'
register_endpoint(media_endpoint) register_views()
register_endpoint(api_media_endpoint)
State().add_service(self.name, self.weight, requires='mediacontroller', is_plugin=True) State().add_service(self.name, self.weight, requires='mediacontroller', is_plugin=True)
State().update_pre_conditions(self.name, self.check_pre_conditions()) State().update_pre_conditions(self.name, self.check_pre_conditions())

View 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/')

View File

@ -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': []}}

View File

@ -25,7 +25,7 @@ presentations from a variety of document formats.
import logging import logging
import os import os
from openlp.core.api.http import register_endpoint
from openlp.core.common import extension_loader from openlp.core.common import extension_loader
from openlp.core.common.i18n import translate from openlp.core.common.i18n import translate
from openlp.core.common.settings import Settings 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.lib.plugin import Plugin, StringContent
from openlp.core.state import State from openlp.core.state import State
from openlp.core.ui.icons import UiIcons 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.presentationcontroller import PresentationController
from openlp.plugins.presentations.lib.mediaitem import PresentationMediaItem from openlp.plugins.presentations.lib.mediaitem import PresentationMediaItem
from openlp.plugins.presentations.lib.presentationtab import PresentationTab from openlp.plugins.presentations.lib.presentationtab import PresentationTab
@ -59,8 +59,7 @@ class PresentationPlugin(Plugin):
self.weight = -8 self.weight = -8
self.icon_path = UiIcons().presentation self.icon_path = UiIcons().presentation
self.icon = build_icon(self.icon_path) self.icon = build_icon(self.icon_path)
register_endpoint(presentations_endpoint) register_views()
register_endpoint(api_presentations_endpoint)
State().add_service('presentation', self.weight, is_plugin=True) State().add_service('presentation', self.weight, is_plugin=True)
State().update_pre_conditions('presentation', self.check_pre_conditions()) State().update_pre_conditions('presentation', self.check_pre_conditions())

View 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/')

View File

@ -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': []}}

View File

@ -613,7 +613,11 @@ class SongMediaItem(MediaManagerItem):
service_item.add_from_text(split_verse, verse_def) service_item.add_from_text(split_verse, verse_def)
service_item.title = song.title service_item.title = song.title
author_list = self.generate_footer(service_item, song) 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) service_item.xml_version = self.open_lyrics.song_to_xml(song)
# Add the audio file to the service item. # Add the audio file to the service item.
if song.media_files: if song.media_files:

View 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/')

View File

@ -31,7 +31,6 @@ from tempfile import gettempdir
from PyQt5 import QtCore, QtWidgets from PyQt5 import QtCore, QtWidgets
from openlp.core.state import State 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.actions import ActionList
from openlp.core.common.i18n import UiStrings, translate from openlp.core.common.i18n import UiStrings, translate
from openlp.core.common.registry import Registry 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.lib.ui import create_action
from openlp.core.ui.icons import UiIcons from openlp.core.ui.icons import UiIcons
from openlp.plugins.songs import reporting 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.duplicatesongremovalform import DuplicateSongRemovalForm
from openlp.plugins.songs.forms.songselectform import SongSelectForm from openlp.plugins.songs.forms.songselectform import SongSelectForm
from openlp.plugins.songs.lib import clean_song, upgrade from openlp.plugins.songs.lib import clean_song, upgrade
@ -128,8 +127,7 @@ class SongsPlugin(Plugin):
self.icon = build_icon(self.icon_path) self.icon = build_icon(self.icon_path)
self.songselect_form = None self.songselect_form = None
self.settings.extend_default_settings(song_footer) self.settings.extend_default_settings(song_footer)
register_endpoint(songs_endpoint) register_views()
register_endpoint(api_songs_endpoint)
State().add_service(self.name, self.weight, is_plugin=True) State().add_service(self.name, self.weight, is_plugin=True)
State().update_pre_conditions(self.name, self.check_pre_conditions()) State().update_pre_conditions(self.name, self.check_pre_conditions())
if not self.settings.value('songs/last import type'): if not self.settings.value('songs/last import type'):

View File

@ -83,6 +83,8 @@ MODULES = [
'alembic', 'alembic',
'lxml', 'lxml',
'chardet', 'chardet',
'flask',
'flask_cors',
'bs4', 'bs4',
'mako', 'mako',
'websockets', 'websockets',

View File

@ -102,6 +102,8 @@ using a computer and a data projector.""",
'chardet', 'chardet',
'dbus-python; platform_system=="Linux"', 'dbus-python; platform_system=="Linux"',
'distro; platform_system=="Linux"', 'distro; platform_system=="Linux"',
'flask',
'flask-cors',
'lxml', 'lxml',
'Mako', 'Mako',
'pymediainfo >= 2.2', 'pymediainfo >= 2.2',

View File

@ -24,11 +24,12 @@ from unittest.mock import MagicMock, patch
from PyQt5 import QtCore from PyQt5 import QtCore
from openlp.core.api import app as flask_app
from openlp.core.state import State from openlp.core.state import State
# Mock QtWebEngineWidgets # Mock QtWebEngineWidgets
# sys.modules['PyQt5.QtWebEngineWidgets'] = MagicMock() # 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.common.registry import Registry
from openlp.core.display.screens import ScreenList from openlp.core.display.screens import ScreenList
from openlp.core.lib.serviceitem import ServiceItem from openlp.core.lib.serviceitem import ServiceItem
@ -75,6 +76,8 @@ class TestController(TestCase):
self.mocked_renderer.format_slide = self.mocked_slide_formater self.mocked_renderer.format_slide = self.mocked_slide_formater
Registry().register('live_controller', self.mocked_live_controller) Registry().register('live_controller', self.mocked_live_controller)
Registry().register('renderer', self.mocked_renderer) 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'})) Registry().register('settings', MagicMock(**{'value.return_value': 'english'}))
def test_controller_text_empty(self): def test_controller_text_empty(self):
@ -88,7 +91,7 @@ class TestController(TestCase):
self.mocked_live_controller.service_item = mocked_service_item self.mocked_live_controller.service_item = mocked_service_item
# WHEN: I trigger the method # 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 # THEN: I get a basic set of results
assert ret['results']['item'] == 'mock-service-item' 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.set_from_service(line)
self.mocked_live_controller.service_item._create_slides() self.mocked_live_controller.service_item._create_slides()
# WHEN: I trigger the method # 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 # THEN: I get a basic set of results
results = ret['results'] results = ret['results']
@ -125,8 +128,7 @@ class TestController(TestCase):
self.mocked_live_controller.service_item = MagicMock() self.mocked_live_controller.service_item = MagicMock()
# WHEN: I trigger the method # WHEN: I trigger the method
controller_direction(None, 'live', 'next') self.client.get('/api/controller/live/next')
# THEN: The correct method is called # THEN: The correct method is called
mocked_emit.assert_called_once_with() mocked_emit.assert_called_once_with()
@ -140,7 +142,6 @@ class TestController(TestCase):
self.mocked_live_controller.service_item = MagicMock() self.mocked_live_controller.service_item = MagicMock()
# WHEN: I trigger the method # WHEN: I trigger the method
controller_direction(None, 'live', 'previous') self.client.get('/api/controller/live/previous')
# THEN: The correct method is called # THEN: The correct method is called
mocked_emit.assert_called_once_with() mocked_emit.assert_called_once_with()

View File

@ -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'

View File

@ -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'

View File

@ -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)

View 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()

View 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()

View 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'] == '*'

View 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()

View File

@ -161,8 +161,8 @@ class TestMainWindow(TestCase, TestMixin):
# WHEN: you check the started functions # WHEN: you check the started functions
# THEN: the following registry functions should have been registered # THEN: the following registry functions should have been registered
expected_service_list = ['application', 'main_window', 'http_server', 'settings_form', 'service_manager', expected_service_list = ['application', 'main_window', 'http_server', 'authentication_token', 'settings_form',
'theme_manager', 'projector_manager'] 'service_manager', 'theme_manager', 'projector_manager']
expected_functions_list = ['bootstrap_initialise', 'bootstrap_post_set_up', 'bootstrap_completion', expected_functions_list = ['bootstrap_initialise', 'bootstrap_post_set_up', 'bootstrap_completion',
'theme_update_global', 'config_screen_changed'] 'theme_update_global', 'config_screen_changed']
assert list(self.registry.service_list.keys()) == expected_service_list, \ assert list(self.registry.service_list.keys()) == expected_service_list, \