From 1d4a4142405fb6fa7b0f8815fbdf71b00e96c6ab Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 20 Oct 2018 15:41:32 +0100 Subject: [PATCH] more state stuff --- openlp/core/app.py | 2 + openlp/core/lib/plugin.py | 6 +- openlp/core/loader.py | 3 +- openlp/core/state.py | 60 +++++++- openlp/core/ui/mainwindow.py | 1 - openlp/core/ui/media/mediacontroller.py | 30 ++-- .../core/ui/media/vendor/mediainfoWrapper.py | 128 ------------------ openlp/core/ui/media/vlcplayer.py | 2 +- openlp/core/ui/slidecontroller.py | 83 ++++++------ openlp/plugins/alerts/alertsplugin.py | 3 + openlp/plugins/bibles/bibleplugin.py | 4 +- openlp/plugins/custom/customplugin.py | 3 + openlp/plugins/images/imageplugin.py | 3 + openlp/plugins/media/mediaplugin.py | 3 + .../presentations/presentationplugin.py | 3 + openlp/plugins/songs/lib/mediaitem.py | 2 +- openlp/plugins/songs/songsplugin.py | 3 + openlp/plugins/songusage/songusageplugin.py | 3 + scripts/check_dependencies.py | 3 +- tests/functional/openlp_core/test_state.py | 40 +++++- .../ui/media/test_mediacontroller.py | 21 +++ .../openlp_core/ui/media/__init__.py | 21 --- .../openlp_core/ui/media/vendor/__init__.py | 21 --- .../ui/media/vendor/test_mediainfoWrapper.py | 48 ------- 24 files changed, 210 insertions(+), 286 deletions(-) delete mode 100644 openlp/core/ui/media/vendor/mediainfoWrapper.py delete mode 100644 tests/interfaces/openlp_core/ui/media/__init__.py delete mode 100644 tests/interfaces/openlp_core/ui/media/vendor/__init__.py delete mode 100644 tests/interfaces/openlp_core/ui/media/vendor/test_mediainfoWrapper.py diff --git a/openlp/core/app.py b/openlp/core/app.py index 66a821e09..b1077f1b0 100644 --- a/openlp/core/app.py +++ b/openlp/core/app.py @@ -35,6 +35,7 @@ from traceback import format_exception from PyQt5 import QtCore, QtWidgets +from openlp.core.state import State from openlp.core.common import is_macosx, is_win from openlp.core.common.applocation import AppLocation from openlp.core.loader import loader @@ -118,6 +119,7 @@ class OpenLP(QtWidgets.QApplication): self.main_window = MainWindow() Registry().execute('bootstrap_initialise') Registry().execute('bootstrap_post_set_up') + State().flush_preconditions() Registry().initialise = False self.main_window.show() if can_show_splash: diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index acb874119..c47ea5ced 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -24,11 +24,9 @@ Provide the generic plugin functionality for OpenLP plugins. """ import logging -from PyQt5 import QtCore - from openlp.core.common.i18n import UiStrings from openlp.core.common.mixins import RegistryProperties -from openlp.core.common.registry import Registry +from openlp.core.common.registry import Registry, RegistryBase from openlp.core.common.settings import Settings from openlp.core.version import get_version @@ -60,7 +58,7 @@ class StringContent(object): VisibleName = 'visible_name' -class Plugin(QtCore.QObject, RegistryProperties): +class Plugin(RegistryBase, RegistryProperties): """ Base class for openlp plugins to inherit from. diff --git a/openlp/core/loader.py b/openlp/core/loader.py index 098e81dae..80c002172 100644 --- a/openlp/core/loader.py +++ b/openlp/core/loader.py @@ -25,6 +25,7 @@ The :mod:`~openlp.core.commmon.loader` module provides a bootstrap for the commo from openlp.core.state import State from openlp.core.ui.media import MediaController +from openlp.core.lib.pluginmanager import PluginManager def loader(): @@ -34,5 +35,5 @@ def loader(): :return: None """ State().load_settings() - MediaController() + PluginManager() diff --git a/openlp/core/state.py b/openlp/core/state.py index 934490d0a..cfcfd952e 100644 --- a/openlp/core/state.py +++ b/openlp/core/state.py @@ -28,13 +28,27 @@ logging and a plugin framework are contained within the openlp.core module. """ import logging +from openlp.core.common.mixins import LogMixin from openlp.core.lib.plugin import PluginStatus log = logging.getLogger() -class State(object): +class StateModule(LogMixin): + def __init__(self): + """ + """ + super(StateModule, self).__init__() + self.name = None + self.order = 0 + self.status = PluginStatus.Inactive + self.pass_preconditions = True + self.requires = None + self.required_by = None + + +class State(LogMixin): __instance__ = None @@ -44,7 +58,6 @@ class State(object): """ if not cls.__instance__: cls.__instance__ = object.__new__(cls) - cls.modules = {} return cls.__instance__ def load_settings(self): @@ -53,12 +66,47 @@ class State(object): def save_settings(self): pass - def add_service(self, name, order, status, dependance=None): + def add_service(self, name, order, status=PluginStatus.Active, requires=None): + """ + Add a module to the array and lod dependancies. There will only be one item per module + :param name: Module name + :param order: Order fo display + :param status: The active status + :param requires: Module name this requires + :return: + """ if name not in self.modules: - self.modules[name] = {'order': order, 'status': status, 'depemdancy': dependance} + state = StateModule() + state.name = name + state.order = order + state.status = status + state.requires = requires + state.required_by = [] + self.modules[name] = state + if requires and requires in self.modules: + if requires not in self.modules[requires].required_by: + self.modules[requires].required_by.append(name) - def is_service_active(self, name): - return self.modules[name]['status'] == PluginStatus.Active + def update_pre_conditions(self, name, status): + """ + Updates the preconditions state of a module + :param name: Module name + :param status: Module new status + :return: + """ + self.modules[name].pass_preconditions = status + + def flush_preconditions(self): + """ + Now all modules are loaded lets update all the preconditions. + :return: + """ + for mods in self.modules: + for req in self.modules[mods].required_by: + self.modules[mods].pass_preconditions = self.modules[req].pass_preconditions + + def is_module_active(self, name): + return self.modules[name].status == PluginStatus.Active def check_active_dependency(self, name): pass diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 592f48a04..41eee40db 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -506,7 +506,6 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert self.formatting_tag_form = FormattingTagForm(self) self.shortcut_form = ShortcutListForm(self) # Set up the path with plugins - PluginManager(self) ImageManager() Renderer() # Set up the interface diff --git a/openlp/core/ui/media/mediacontroller.py b/openlp/core/ui/media/mediacontroller.py index 7afabe3cb..ad75e143a 100644 --- a/openlp/core/ui/media/mediacontroller.py +++ b/openlp/core/ui/media/mediacontroller.py @@ -25,13 +25,20 @@ related to playing media, such as sliders. """ import datetime import logging -import os + +try: + import pymediainfo + pymediainfo_available = True +except ImportError: + pymediainfo_available = False + +from subprocess import check_output from PyQt5 import QtCore, QtWidgets +from openlp.core.state import State from openlp.core.api.http import register_endpoint -from openlp.core.common import extension_loader -from openlp.core.common.i18n import UiStrings, translate +from openlp.core.common.i18n import translate from openlp.core.common.mixins import LogMixin, RegistryProperties from openlp.core.common.registry import Registry, RegistryBase from openlp.core.common.settings import Settings @@ -41,9 +48,7 @@ from openlp.core.ui import DisplayControllerType from openlp.core.ui.icons import UiIcons from openlp.core.ui.media import MediaState, MediaInfo, MediaType, parse_optical_path from openlp.core.ui.media.endpoint import media_endpoint -from openlp.core.ui.media.mediaplayer import MediaPlayer -from openlp.core.ui.media.vlcplayer import VlcPlayer -from openlp.core.ui.media.vendor.mediainfoWrapper import MediaInfoWrapper +from openlp.core.ui.media.vlcplayer import VlcPlayer, get_vlc from openlp.core.widgets.toolbar import OpenLPToolbar log = logging.getLogger(__name__) @@ -62,7 +67,6 @@ class MediaSlider(QtWidgets.QSlider): super(MediaSlider, self).__init__(direction) self.manager = manager self.controller = controller - self.no_matching_player = translate('MediaPlugin.MediaItem', 'File %s not supported using player %s') def mouseMoveEvent(self, event): """ @@ -193,6 +197,9 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): # self.register_players() self.setup() self.media_players = VlcPlayer(self) + State().add_service("mediacontroller", 0) + if get_vlc() and pymediainfo_available: + State().update_pre_conditions("mediacontroller", True) self._generate_extensions_lists() return True @@ -460,7 +467,14 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): media_info = MediaInfo() media_info.volume = 0 media_info.file_info = QtCore.QFileInfo(service_item.get_frame_path()) - media_data = MediaInfoWrapper.parse(service_item.get_frame_path()) + filename = service_item.get_frame_path() + if pymediainfo.MediaInfo.can_parse(): + media_data = pymediainfo.MediaInfo.parse(filename) + else: + xml = check_output(['mediainfo', '-f', '--Output=XML', '--Inform=OLDXML', filename]) + if not xml.startswith(b'".format(self.track_id, self.track_type) - - def to_data(self): - data = {} - for k, v in self.__dict__.items(): - if k != 'xml_dom_fragment': - data[k] = v - return data - - -class MediaInfoWrapper(object): - - def __init__(self, xml): - self.xml_dom = xml - xml_types = (str,) # no unicode type in python3 - if isinstance(xml, xml_types): - self.xml_dom = MediaInfoWrapper.parse_xml_data_into_dom(xml) - - @staticmethod - def parse_xml_data_into_dom(xml_data): - return BeautifulSoup(xml_data, "xml") - - @staticmethod - def parse(filename, environment=ENV_DICT): - xml = check_output(['mediainfo', '-f', '--Output=XML', '--Inform=OLDXML', filename]) - if not xml.startswith(b'{label}: {media}'. + service_item.metadata.append('{label}: {media}'. format(label=translate('SongsPlugin.MediaItem', 'Media'), media=service_item.background_audio)) return True diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index e810dab3e..356b96f02 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -31,6 +31,7 @@ from tempfile import gettempdir from PyQt5 import QtCore, QtWidgets +from openlp.core.state import State from openlp.core.api.http import register_endpoint from openlp.core.common.actions import ActionList from openlp.core.common.i18n import UiStrings, translate @@ -99,6 +100,8 @@ class SongsPlugin(Plugin): self.songselect_form = None register_endpoint(songs_endpoint) register_endpoint(api_songs_endpoint) + State().add_service(self.name, self.weight) + State().update_pre_conditions(self.name, self.check_pre_conditions()) def check_pre_conditions(self): """ diff --git a/openlp/plugins/songusage/songusageplugin.py b/openlp/plugins/songusage/songusageplugin.py index a0dcff54f..86227db73 100644 --- a/openlp/plugins/songusage/songusageplugin.py +++ b/openlp/plugins/songusage/songusageplugin.py @@ -25,6 +25,7 @@ from datetime import datetime from PyQt5 import QtCore, QtWidgets +from openlp.core.state import State from openlp.core.common.actions import ActionList from openlp.core.common.i18n import translate from openlp.core.common.registry import Registry @@ -66,6 +67,8 @@ class SongUsagePlugin(Plugin): self.weight = -4 self.icon = UiIcons().song_usage self.song_usage_active = False + State().add_service(self.name, self.weight) + State().update_pre_conditions(self.name, self.check_pre_conditions()) def check_pre_conditions(self): """ diff --git a/scripts/check_dependencies.py b/scripts/check_dependencies.py index 242b28477..c8743ab8e 100755 --- a/scripts/check_dependencies.py +++ b/scripts/check_dependencies.py @@ -99,7 +99,8 @@ MODULES = [ 'six', 'webob', 'requests', - 'qtawesome' + 'qtawesome', + 'pymediainfo' ] diff --git a/tests/functional/openlp_core/test_state.py b/tests/functional/openlp_core/test_state.py index fc5df9703..04925f9f4 100644 --- a/tests/functional/openlp_core/test_state.py +++ b/tests/functional/openlp_core/test_state.py @@ -72,9 +72,24 @@ class TestState(TestCase, TestMixin): # WHEN I add a new service twice State().add_service("test", 1, PluginStatus.Active) State().add_service("test1", 1, PluginStatus.Active, "test") + State().add_service("test1", 1, PluginStatus.Active, "test") - # THEN I have a single saved service + # THEN I have a single saved service and one dependancy assert len(State().modules) == 2 + assert len(State().modules['test'].required_by) == 1 + + def test_add_service_multiple_depends(self): + # GIVEN a new state + State().load_settings() + + # WHEN I add a new service twice + State().add_service("test", 1, PluginStatus.Active) + State().add_service("test1", 1, PluginStatus.Active, "test") + State().add_service("test2", 1, PluginStatus.Active, "test") + + # THEN I have a 3 modules and 2 dependancies + assert len(State().modules) == 3 + assert len(State().modules['test'].required_by) == 2 def test_active_service(self): # GIVEN a new state @@ -84,7 +99,7 @@ class TestState(TestCase, TestMixin): State().add_service("test", 1, PluginStatus.Active) # THEN I have a single saved service - assert State().is_service_active('test') is True + assert State().is_module_active('test') is True def test_inactive_service(self): # GIVEN a new state @@ -94,4 +109,23 @@ class TestState(TestCase, TestMixin): State().add_service("test", 1, PluginStatus.Inactive) # THEN I have a single saved service - assert State().is_service_active('test') is False + assert State().is_module_active('test') is False + + def test_basic_preconditions(self): + # GIVEN a new state + State().load_settings() + + # WHEN I add a new services with dependancies and a failed pre condition + State().add_service("test", 1, PluginStatus.Inactive) + State().add_service("test2", 1, PluginStatus.Inactive) + State().add_service("test1", 1, PluginStatus.Inactive, 'test') + State().update_pre_conditions('test1', False) + + # THEN correct the state when I flush the preconditions + assert State().modules['test'].pass_preconditions == True + assert State().modules['test2'].pass_preconditions == True + assert State().modules['test1'].pass_preconditions == False + State().flush_preconditions() + assert State().modules['test'].pass_preconditions == False + assert State().modules['test2'].pass_preconditions == True + assert State().modules['test1'].pass_preconditions == False diff --git a/tests/functional/openlp_core/ui/media/test_mediacontroller.py b/tests/functional/openlp_core/ui/media/test_mediacontroller.py index 8fb141c13..dca3417d2 100644 --- a/tests/functional/openlp_core/ui/media/test_mediacontroller.py +++ b/tests/functional/openlp_core/ui/media/test_mediacontroller.py @@ -30,6 +30,12 @@ from openlp.core.ui.media.mediacontroller import MediaController from openlp.core.ui.media.mediaplayer import MediaPlayer from tests.helpers.testmixin import TestMixin +from tests.utils.constants import RESOURCE_PATH + + +TEST_PATH = RESOURCE_PATH / 'media' +TEST_MEDIA = [['avi_file.avi', 61495], ['mp3_file.mp3', 134426], ['mpg_file.mpg', 9404], ['mp4_file.mp4', 188336]] + class TestMediaController(TestCase, TestMixin): @@ -254,3 +260,18 @@ class TestMediaController(TestCase, TestMixin): # THEN: The underlying method is called mocked_media_seek.assert_called_with(1, 800) + + def test_media_length(self): + """ + Test the Media Info basic functionality + """ + for test_data in TEST_MEDIA: + # GIVEN: a media file + full_path = str(TEST_PATH / test_data[0]) + media_controller = MediaController() + + # WHEN the media data is retrieved + results = media_controller.media_length(full_path) + + # THEN you can determine the run time + assert results.tracks[0].duration == test_data[1], 'The correct duration is returned for ' + test_data[0] diff --git a/tests/interfaces/openlp_core/ui/media/__init__.py b/tests/interfaces/openlp_core/ui/media/__init__.py deleted file mode 100644 index 711ded4ae..000000000 --- a/tests/interfaces/openlp_core/ui/media/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- 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 # -############################################################################### diff --git a/tests/interfaces/openlp_core/ui/media/vendor/__init__.py b/tests/interfaces/openlp_core/ui/media/vendor/__init__.py deleted file mode 100644 index 711ded4ae..000000000 --- a/tests/interfaces/openlp_core/ui/media/vendor/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- 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 # -############################################################################### diff --git a/tests/interfaces/openlp_core/ui/media/vendor/test_mediainfoWrapper.py b/tests/interfaces/openlp_core/ui/media/vendor/test_mediainfoWrapper.py deleted file mode 100644 index 1d106e1d4..000000000 --- a/tests/interfaces/openlp_core/ui/media/vendor/test_mediainfoWrapper.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- 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 # -############################################################################### -""" -Package to test the openlp.core.ui.media package. -""" -from unittest import TestCase - -from openlp.core.ui.media.vendor.mediainfoWrapper import MediaInfoWrapper -from tests.utils.constants import RESOURCE_PATH - -TEST_PATH = RESOURCE_PATH / 'media' -TEST_MEDIA = [['avi_file.avi', 61495], ['mp3_file.mp3', 134426], ['mpg_file.mpg', 9404], ['mp4_file.mp4', 188336]] - - -class TestMediainfoWrapper(TestCase): - - def test_media_length(self): - """ - Test the Media Info basic functionality - """ - for test_data in TEST_MEDIA: - # GIVEN: a media file - full_path = str(TEST_PATH / test_data[0]) - - # WHEN the media data is retrieved - results = MediaInfoWrapper.parse(full_path) - - # THEN you can determine the run time - assert results.tracks[0].duration == test_data[1], 'The correct duration is returned for ' + test_data[0]