# -*- 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, 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 . # ########################################################################## """ The :mod:`http` module contains the API web server. This is a lightweight web server used by remotes to interact with OpenLP. It uses JSON to communicate with the remotes. """ import logging import time from PyQt5 import QtCore, QtWidgets from waitress.server import create_server from openlp.core.api.deploy import download_and_check, download_sha256 from openlp.core.api.endpoint.controller import api_controller_endpoint, controller_endpoint from openlp.core.api.endpoint.core import blank_endpoint, chords_endpoint, main_endpoint, stage_endpoint from openlp.core.api.endpoint.remote import remote_endpoint from openlp.core.api.endpoint.service import api_service_endpoint, service_endpoint from openlp.core.api.http import application, register_endpoint from openlp.core.api.poll import Poller from openlp.core.common.applocation import AppLocation from openlp.core.common.i18n import UiStrings, translate from openlp.core.common.mixins import LogMixin, RegistryProperties from openlp.core.common.path import create_paths from openlp.core.common.registry import Registry, RegistryBase from openlp.core.common.settings import Settings from openlp.core.threading import ThreadWorker, run_thread log = logging.getLogger(__name__) class HttpWorker(ThreadWorker): """ A special Qt thread class to allow the HTTP server to run at the same time as the UI. """ def start(self): """ Run the thread. """ address = Settings().value('api/ip address') port = Settings().value('api/port') Registry().execute('get_website_version') try: self.server = create_server(application, host=address, port=port) self.server.run() except OSError: log.exception('An error occurred when serving the application.') self.quit.emit() def stop(self): """ A method to stop the worker """ if hasattr(self, 'server'): # Loop through all the channels and close them to stop the server for channel in self.server._map.values(): if hasattr(channel, 'close'): channel.close() class HttpServer(RegistryBase, RegistryProperties, LogMixin): """ Wrapper round a server instance """ def __init__(self, parent=None): """ Initialise the http server, and start the http server """ super(HttpServer, self).__init__(parent) if not Registry().get_flag('no_web_server'): worker = HttpWorker() run_thread(worker, 'http_server') Registry().register_function('download_website', self.first_time) Registry().register_function('get_website_version', self.website_version) Registry().set_flag('website_version', '0.0') def bootstrap_post_set_up(self): """ Register the poll return service and start the servers. """ self.initialise() self.poller = Poller() Registry().register('poller', self.poller) application.initialise() register_endpoint(controller_endpoint) register_endpoint(api_controller_endpoint) register_endpoint(chords_endpoint) register_endpoint(stage_endpoint) register_endpoint(blank_endpoint) register_endpoint(main_endpoint) register_endpoint(service_endpoint) register_endpoint(api_service_endpoint) register_endpoint(remote_endpoint) @staticmethod def initialise(): """ Create the internal file structure if it does not exist :return: """ create_paths(AppLocation.get_section_data_path('remotes') / 'assets', AppLocation.get_section_data_path('remotes') / 'images', AppLocation.get_section_data_path('remotes') / 'static', AppLocation.get_section_data_path('remotes') / 'static' / 'index', AppLocation.get_section_data_path('remotes') / 'templates') def first_time(self): """ Import web site code if active """ self.application.process_events() progress = DownloadProgressDialog(self) progress.forceShow() self.application.process_events() time.sleep(1) download_and_check(progress) self.application.process_events() time.sleep(1) progress.close() self.application.process_events() Settings().setValue('remotes/download version', self.version) def website_version(self): """ Download and save the website version and sha256 :return: None """ sha256, self.version = download_sha256() Registry().set_flag('website_sha256', sha256) Registry().set_flag('website_version', self.version) class DownloadProgressDialog(QtWidgets.QProgressDialog): """ Local class to handle download display based and supporting httputils:get_web_page """ def __init__(self, parent): super(DownloadProgressDialog, self).__init__(parent.main_window) self.parent = parent self.setWindowModality(QtCore.Qt.WindowModal) self.setWindowTitle(translate('RemotePlugin', 'Importing Website')) self.setLabelText(UiStrings().StartingImport) self.setCancelButton(None) self.setRange(0, 1) self.setMinimumDuration(0) self.was_cancelled = False self.previous_size = 0 def update_progress(self, count, block_size): """ Calculate and display the download progress. """ increment = (count * block_size) - self.previous_size self._increment_progress_bar(None, increment) self.previous_size = count * block_size def _increment_progress_bar(self, status_text, increment=1): """ Update the wizard progress page. :param status_text: Current status information to display. :param increment: The value to increment the progress bar by. """ if status_text: self.setText(status_text) if increment > 0: self.setValue(self.value() + increment) self.parent.application.process_events()