← Back to team overview

openlp-core team mailing list archive

[Merge] lp:~trb143/openlp/media_state into lp:openlp

 

Tim Bentley has proposed merging lp:~trb143/openlp/media_state into lp:openlp.

Commit message:
Fix up VLC so it works and plays.
Still not finished but getting there.

Requested reviews:
  Phill (phill-ridout)
Related bugs:
  Bug #1823575 in OpenLP: "PluginManager still has plugin list"
  https://bugs.launchpad.net/openlp/+bug/1823575

For more details, see:
https://code.launchpad.net/~trb143/openlp/media_state/+merge/365821

VLC plays and handles missing live display gracefully.  Preview still works.
Added experimental setting to remove UI incomplete stuff.
various bug fixes and improvments,
-- 
Your team OpenLP Core is subscribed to branch lp:openlp.
=== modified file '.bzrignore'
--- .bzrignore	2019-03-04 20:37:11 +0000
+++ .bzrignore	2019-04-10 19:57:31 +0000
@@ -15,6 +15,7 @@
 *.e4*
 *eric[1-9]project
 .git
+env
 # Git files
 .gitignore
 htmlcov

=== modified file 'openlp/core/common/settings.py'
--- openlp/core/common/settings.py	2019-03-03 13:32:31 +0000
+++ openlp/core/common/settings.py	2019-04-10 19:57:31 +0000
@@ -129,6 +129,9 @@
         ``advanced/slide limits`` to ``SlideLimits.Wrap``. **NOTE**, this means that the rules have to cover all cases!
         So, if the type of the old value is bool, then there must be two rules.
     """
+    on_monitor_default = True
+    if log.isEnabledFor(logging.DEBUG):
+        on_monitor_default = False
     __default_settings__ = {
         'settings/version': 0,
         'advanced/add page break': False,
@@ -185,6 +188,7 @@
         'core/click live slide to unblank': False,
         'core/blank warning': False,
         'core/ccli number': '',
+        'core/experimental': False,
         'core/has run wizard': False,
         'core/language': '[en]',
         'core/last version test': '',
@@ -202,13 +206,13 @@
         'core/view mode': 'default',
         # The other display settings (display position and dimensions) are defined in the ScreenList class due to a
         # circular dependency.
-        'core/display on monitor': True,
+        'core/display on monitor': on_monitor_default,
         'core/override position': False,
         'core/monitor': {},
         'core/application version': '0.0',
         'images/background color': '#000000',
-        'media/players': 'system,webkit',
-        'media/override player': QtCore.Qt.Unchecked,
+        'media/media auto start': QtCore.Qt.Unchecked,
+        'media/stream command': '',
         'remotes/download version': '0.0',
         'players/background color': '#000000',
         'servicemanager/last directory': None,
@@ -311,7 +315,9 @@
         ('bibles/proxy name', '', []),  # Just remove these bible proxy settings. They weren't used in 2.4!
         ('bibles/proxy address', '', []),
         ('bibles/proxy username', '', []),
-        ('bibles/proxy password', '', [])
+        ('bibles/proxy password', '', []),
+        ('media/players', '', []),
+        ('media/override player', '', [])
     ]
 
     @staticmethod

=== modified file 'openlp/core/lib/theme.py'
--- openlp/core/lib/theme.py	2019-02-14 15:09:09 +0000
+++ openlp/core/lib/theme.py	2019-04-10 19:57:31 +0000
@@ -46,6 +46,7 @@
     Image = 2
     Transparent = 3
     Video = 4
+    Stream = 5
 
     @staticmethod
     def to_string(background_type):
@@ -62,6 +63,8 @@
             return 'transparent'
         elif background_type == BackgroundType.Video:
             return 'video'
+        elif background_type == BackgroundType.Stream:
+            return 'stream'
 
     @staticmethod
     def from_string(type_string):
@@ -78,6 +81,8 @@
             return BackgroundType.Transparent
         elif type_string == 'video':
             return BackgroundType.Video
+        elif type_string == 'stream':
+            return BackgroundType.Stream
 
 
 class BackgroundGradientType(object):

=== modified file 'openlp/core/ui/media/__init__.py'
--- openlp/core/ui/media/__init__.py	2019-03-17 10:01:52 +0000
+++ openlp/core/ui/media/__init__.py	2019-04-10 19:57:31 +0000
@@ -48,6 +48,7 @@
     CD = 3
     DVD = 4
     Folder = 5
+    Stream = 6
 
 
 class ItemMediaInfo(object):

=== modified file 'openlp/core/ui/media/mediacontroller.py'
--- openlp/core/ui/media/mediacontroller.py	2019-02-14 15:09:09 +0000
+++ openlp/core/ui/media/mediacontroller.py	2019-04-10 19:57:31 +0000
@@ -23,7 +23,6 @@
 The :mod:`~openlp.core.ui.media.mediacontroller` module contains a base class for media components and other widgets
 related to playing media, such as sliders.
 """
-import datetime
 import logging
 
 try:
@@ -33,7 +32,7 @@
     pymediainfo_available = False
 
 from subprocess import check_output
-from PyQt5 import QtCore, QtWidgets
+from PyQt5 import QtCore
 
 from openlp.core.state import State
 from openlp.core.api.http import register_endpoint
@@ -44,11 +43,9 @@
 from openlp.core.lib.serviceitem import ItemCapabilities
 from openlp.core.lib.ui import critical_error_message_box
 from openlp.core.ui import DisplayControllerType
-from openlp.core.ui.icons import UiIcons
 from openlp.core.ui.media import MediaState, ItemMediaInfo, MediaType, parse_optical_path
 from openlp.core.ui.media.endpoint import media_endpoint
 from openlp.core.ui.media.vlcplayer import VlcPlayer, get_vlc
-from openlp.core.widgets.toolbar import OpenLPToolbar
 
 
 log = logging.getLogger(__name__)
@@ -56,45 +53,6 @@
 TICK_TIME = 200
 
 
-class MediaSlider(QtWidgets.QSlider):
-    """
-    Allows the mouse events of a slider to be overridden and extra functionality added
-    """
-    def __init__(self, direction, manager, controller):
-        """
-        Constructor
-        """
-        super(MediaSlider, self).__init__(direction)
-        self.manager = manager
-        self.controller = controller
-
-    def mouseMoveEvent(self, event):
-        """
-        Override event to allow hover time to be displayed.
-
-        :param event: The triggering event
-        """
-        time_value = QtWidgets.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), event.x(), self.width())
-        self.setToolTip('%s' % datetime.timedelta(seconds=int(time_value / 1000)))
-        QtWidgets.QSlider.mouseMoveEvent(self, event)
-
-    def mousePressEvent(self, event):
-        """
-        Mouse Press event no new functionality
-        :param event: The triggering event
-        """
-        QtWidgets.QSlider.mousePressEvent(self, event)
-
-    def mouseReleaseEvent(self, event):
-        """
-        Set the slider position when the mouse is clicked and released on the slider.
-
-        :param event: The triggering event
-        """
-        self.setValue(QtWidgets.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), event.x(), self.width()))
-        QtWidgets.QSlider.mouseReleaseEvent(self, event)
-
-
 class MediaController(RegistryBase, LogMixin, RegistryProperties):
     """
     The implementation of the Media Controller. The Media Controller adds an own class for every Player.
@@ -116,7 +74,6 @@
 
     def setup(self):
         self.vlc_player = None
-        self.display_controllers = {}
         self.current_media_players = {}
         # Timer for video state
         self.live_timer = QtCore.QTimer()
@@ -168,117 +125,71 @@
         self.setup()
         self.vlc_player = VlcPlayer(self)
         State().add_service("mediacontroller", 0)
+        State().add_service("media_live", 0, requires="mediacontroller")
         if get_vlc() and pymediainfo_available:
             State().update_pre_conditions("mediacontroller", True)
+            State().update_pre_conditions('media_live', True)
         else:
             State().missing_text("mediacontroller", translate('OpenLP.SlideController',
                                  "VLC or pymediainfo are missing, so you are unable to play any media"))
         self._generate_extensions_lists()
         return True
 
+    def bootstrap_post_set_up(self):
+        """
+        Set up the controllers.
+        :return:
+        """
+        try:
+            self.setup_display(self.live_controller.display, False)
+        except AttributeError:
+            State().update_pre_conditions('media_live', False)
+        self.setup_display(self.preview_controller.preview_display, True)
+
+    def display_controllers(self, controller_type):
+        """
+        Decides which controller to use.
+
+        :param controller_type: The controller type where a player will be placed
+        """
+        if controller_type == DisplayControllerType.Live:
+            return self.live_controller
+        else:
+            return self.preview_controller
+
     def media_state_live(self):
         """
         Check if there is a running Live media Player and do updating stuff (e.g. update the UI)
         """
-        display = self._define_display(self.display_controllers[DisplayControllerType.Live])
+        display = self._define_display(self.display_controllers(DisplayControllerType.Live))
         if DisplayControllerType.Live in self.current_media_players:
             self.current_media_players[DisplayControllerType.Live].resize(display)
-            self.current_media_players[DisplayControllerType.Live].update_ui(display)
-            self.tick(self.display_controllers[DisplayControllerType.Live])
+            self.current_media_players[DisplayControllerType.Live].update_ui(self.live_controller, display)
+            self.tick(self.display_controllers(DisplayControllerType.Live))
             if self.current_media_players[DisplayControllerType.Live].get_live_state() is not MediaState.Playing:
                 self.live_timer.stop()
         else:
             self.live_timer.stop()
-            self.media_stop(self.display_controllers[DisplayControllerType.Live])
-            if self.display_controllers[DisplayControllerType.Live].media_info.can_loop_playback:
-                self.media_play(self.display_controllers[DisplayControllerType.Live], True)
+            self.media_stop(self.display_controllers(DisplayControllerType.Live))
+            if self.display_controllers(DisplayControllerType.Live).media_info.can_loop_playback:
+                self.media_play(self.display_controllers(DisplayControllerType.Live), True)
 
     def media_state_preview(self):
         """
         Check if there is a running Preview media Player and do updating stuff (e.g. update the UI)
         """
-        display = self._define_display(self.display_controllers[DisplayControllerType.Preview])
+        display = self._define_display(self.display_controllers(DisplayControllerType.Preview))
         if DisplayControllerType.Preview in self.current_media_players:
             self.current_media_players[DisplayControllerType.Preview].resize(display)
-            self.current_media_players[DisplayControllerType.Preview].update_ui(display)
-            self.tick(self.display_controllers[DisplayControllerType.Preview])
+            self.current_media_players[DisplayControllerType.Preview].update_ui(self.preview_controller, display)
+            self.tick(self.display_controllers(DisplayControllerType.Preview))
             if self.current_media_players[DisplayControllerType.Preview].get_preview_state() is not MediaState.Playing:
                 self.preview_timer.stop()
         else:
             self.preview_timer.stop()
-            self.media_stop(self.display_controllers[DisplayControllerType.Preview])
-            if self.display_controllers[DisplayControllerType.Preview].media_info.can_loop_playback:
-                self.media_play(self.display_controllers[DisplayControllerType.Preview], True)
-
-    def register_controller(self, controller):
-        """
-        Registers media controls where the players will be placed to run.
-
-        :param controller: The controller where a player will be placed
-        """
-        self.display_controllers[controller.controller_type] = controller
-        self.setup_generic_controls(controller)
-
-    def setup_generic_controls(self, controller):
-        """
-        Set up controls on the control_panel for a given controller
-
-        :param controller:  First element is the controller which should be used
-        """
-        controller.media_info = ItemMediaInfo()
-        # Build a Media ToolBar
-        controller.mediabar = OpenLPToolbar(controller)
-        controller.mediabar.add_toolbar_action('playbackPlay', text='media_playback_play',
-                                               icon=UiIcons().play,
-                                               tooltip=translate('OpenLP.SlideController', 'Start playing media.'),
-                                               triggers=controller.send_to_plugins)
-        controller.mediabar.add_toolbar_action('playbackPause', text='media_playback_pause',
-                                               icon=UiIcons().pause,
-                                               tooltip=translate('OpenLP.SlideController', 'Pause playing media.'),
-                                               triggers=controller.send_to_plugins)
-        controller.mediabar.add_toolbar_action('playbackStop', text='media_playback_stop',
-                                               icon=UiIcons().stop,
-                                               tooltip=translate('OpenLP.SlideController', 'Stop playing media.'),
-                                               triggers=controller.send_to_plugins)
-        controller.mediabar.add_toolbar_action('playbackLoop', text='media_playback_loop',
-                                               icon=UiIcons().repeat, checked=False,
-                                               tooltip=translate('OpenLP.SlideController', 'Loop playing media.'),
-                                               triggers=controller.send_to_plugins)
-        controller.position_label = QtWidgets.QLabel()
-        controller.position_label.setText(' 00:00 / 00:00')
-        controller.position_label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
-        controller.position_label.setToolTip(translate('OpenLP.SlideController', 'Video timer.'))
-        controller.position_label.setMinimumSize(90, 0)
-        controller.position_label.setObjectName('position_label')
-        controller.mediabar.add_toolbar_widget(controller.position_label)
-        # Build the seek_slider.
-        controller.seek_slider = MediaSlider(QtCore.Qt.Horizontal, self, controller)
-        controller.seek_slider.setMaximum(1000)
-        controller.seek_slider.setTracking(True)
-        controller.seek_slider.setMouseTracking(True)
-        controller.seek_slider.setToolTip(translate('OpenLP.SlideController', 'Video position.'))
-        controller.seek_slider.setGeometry(QtCore.QRect(90, 260, 221, 24))
-        controller.seek_slider.setObjectName('seek_slider')
-        controller.mediabar.add_toolbar_widget(controller.seek_slider)
-        # Build the volume_slider.
-        controller.volume_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
-        controller.volume_slider.setTickInterval(10)
-        controller.volume_slider.setTickPosition(QtWidgets.QSlider.TicksAbove)
-        controller.volume_slider.setMinimum(0)
-        controller.volume_slider.setMaximum(100)
-        controller.volume_slider.setTracking(True)
-        controller.volume_slider.setToolTip(translate('OpenLP.SlideController', 'Audio Volume.'))
-        controller.volume_slider.setValue(controller.media_info.volume)
-        controller.volume_slider.setGeometry(QtCore.QRect(90, 160, 221, 24))
-        controller.volume_slider.setObjectName('volume_slider')
-        controller.mediabar.add_toolbar_widget(controller.volume_slider)
-        controller.controller_layout.addWidget(controller.mediabar)
-        controller.mediabar.setVisible(False)
-        if not controller.is_live:
-            controller.volume_slider.setEnabled(False)
-        # Signals
-        controller.seek_slider.valueChanged.connect(controller.send_to_plugins)
-        controller.volume_slider.valueChanged.connect(controller.send_to_plugins)
+            self.media_stop(self.display_controllers(DisplayControllerType.Preview))
+            if self.display_controllers(DisplayControllerType.Preview).media_info.can_loop_playback:
+                self.media_play(self.display_controllers(DisplayControllerType.Preview), True)
 
     def setup_display(self, display, preview):
         """
@@ -287,14 +198,13 @@
         :param display:  Display on which the output is to be played
         :param preview: Whether the display is a main or preview display
         """
-        # clean up possible running old media files
-        self.finalise()
+        display.media_info = ItemMediaInfo()
         display.has_audio = True
-        if display.is_live and preview:
-            return
+        # if display.is_live and preview:
+        #     return
         if preview:
             display.has_audio = False
-        self.vlc_player.setup(display)
+        self.vlc_player.setup(display, preview)
 
     def set_controls_visible(self, controller, value):
         """
@@ -305,9 +215,9 @@
         """
         # Generic controls
         controller.mediabar.setVisible(value)
-        if controller.is_live and controller.display:
-            if self.current_media_players and value:
-                controller.display.set_transparency(False)
+        # if controller.is_live and controller.display:
+        #    if self.current_media_players and value:
+        #       controller.display.set_transparency(False)
 
     @staticmethod
     def resize(display, player):
@@ -319,7 +229,7 @@
         """
         player.resize(display)
 
-    def video(self, source, service_item, hidden=False, video_behind_text=False):
+    def load_video(self, source, service_item, hidden=False, video_behind_text=False):
         """
         Loads and starts a video to run with the option of sound
 
@@ -329,7 +239,7 @@
         :param video_behind_text: Is the video to be played behind text.
         """
         is_valid = True
-        controller = self.display_controllers[source]
+        controller = self.display_controllers(source)
         # stop running videos
         self.media_reset(controller)
         controller.media_info = ItemMediaInfo()
@@ -354,8 +264,8 @@
                 log.debug('video is not optical and live')
                 controller.media_info.length = service_item.media_length
                 is_valid = self._check_file_type(controller, display)
-            display.override['theme'] = ''
-            display.override['video'] = True
+            # display.override['theme'] = ''
+            # display.override['video'] = True
             if controller.media_info.is_background:
                 # ignore start/end time
                 controller.media_info.start_time = 0
@@ -379,10 +289,10 @@
             critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported File'),
                                        translate('MediaPlugin.MediaItem', 'Unsupported File'))
             return False
-        log.debug('video mediatype: ' + str(controller.media_info.media_type))
+        log.debug('video media type: ' + str(controller.media_info.media_type))
         # dont care about actual theme, set a black background
-        if controller.is_live and not controller.media_info.is_background:
-            display.frame.runJavaScript('show_video("setBackBoard", null, null,"visible");')
+        # if controller.is_live and not controller.media_info.is_background:
+        #    display.frame.runJavaScript('show_video("setBackBoard", null, null,"visible");')
         # now start playing - Preview is autoplay!
         autoplay = False
         # Preview requested
@@ -471,28 +381,26 @@
         for file in controller.media_info.file_info:
             if file.is_file:
                 suffix = '*%s' % file.suffix.lower()
-                player = self.vlc_player
                 file = str(file)
-                if suffix in player.video_extensions_list:
+                if suffix in self.vlc_player.video_extensions_list:
                     if not controller.media_info.is_background or controller.media_info.is_background and \
-                            player.can_background:
-                        self.resize(display, player)
-                        if player.load(display, file):
-                            self.current_media_players[controller.controller_type] = player
+                            self.vlc_player.can_background:
+                        self.resize(display, self.vlc_player)
+                        if self.vlc_player.load(display, file):
+                            self.current_media_players[controller.controller_type] = self.vlc_player
                             controller.media_info.media_type = MediaType.Video
                             return True
-                if suffix in player.audio_extensions_list:
-                    if player.load(display, file):
-                        self.current_media_players[controller.controller_type] = player
+                if suffix in self.vlc_player.audio_extensions_list:
+                    if self.vlc_player.load(display, file):
+                        self.current_media_players[controller.controller_type] = self.vlc_player
                         controller.media_info.media_type = MediaType.Audio
                         return True
             else:
-                player = self.vlc_player
                 file = str(file)
-                if player.can_folder:
-                    self.resize(display, player)
-                    if player.load(display, file):
-                        self.current_media_players[controller.controller_type] = player
+                if self.vlc_player.can_folder:
+                    self.resize(display, self.vlc_player)
+                    if self.vlc_player.load(display, file):
+                        self.current_media_players[controller.controller_type] = self.vlc_player
                         controller.media_info.media_type = MediaType.Video
                         return True
         return False
@@ -509,8 +417,6 @@
     def on_media_play(self):
         """
         Responds to the request to play a loaded video from the web.
-
-        :param msg: First element is the controller which should be used
         """
         self.media_play(Registry().get('live_controller'), False)
 
@@ -524,7 +430,7 @@
         controller.seek_slider.blockSignals(True)
         controller.volume_slider.blockSignals(True)
         display = self._define_display(controller)
-        if not self.current_media_players[controller.controller_type].play(display):
+        if not self.current_media_players[controller.controller_type].play(controller, display):
             controller.seek_slider.blockSignals(False)
             controller.volume_slider.blockSignals(False)
             return False
@@ -533,8 +439,8 @@
         else:
             self.media_volume(controller, controller.media_info.volume)
         if first_time:
-            if not controller.media_info.is_background:
-                display.frame.runJavaScript('show_blank("desktop");')
+            # if not controller.media_info.is_background:
+            #    display.frame.runJavaScript('show_blank("desktop");')
             self.current_media_players[controller.controller_type].set_visible(display, True)
             controller.mediabar.actions['playbackPlay'].setVisible(False)
             controller.mediabar.actions['playbackPause'].setVisible(True)
@@ -591,8 +497,6 @@
     def on_media_pause(self):
         """
         Responds to the request to pause a loaded video from the web.
-
-        :param msg: First element is the controller which should be used
         """
         self.media_pause(Registry().get('live_controller'))
 
@@ -639,8 +543,6 @@
     def on_media_stop(self):
         """
         Responds to the request to stop a loaded video from the web.
-
-        :param msg: First element is the controller which should be used
         """
         self.media_stop(Registry().get('live_controller'))
 
@@ -653,8 +555,8 @@
         """
         display = self._define_display(controller)
         if controller.controller_type in self.current_media_players:
-            if not looping_background:
-                display.frame.runJavaScript('show_blank("black");')
+            # if not looping_background:
+            #    display.frame.runJavaScript('show_blank("black");')
             self.current_media_players[controller.controller_type].stop(display)
             self.current_media_players[controller.controller_type].set_visible(display, False)
             controller.seek_slider.setSliderPosition(0)
@@ -725,7 +627,7 @@
             display.override = {}
             self.current_media_players[controller.controller_type].reset(display)
             self.current_media_players[controller.controller_type].set_visible(display, False)
-            display.frame.runJavaScript('show_video("setBackBoard", null, null, "hidden");')
+            # display.frame.runJavaScript('show_video("setBackBoard", null, null, "hidden");')
             del self.current_media_players[controller.controller_type]
 
     def media_hide(self, msg):
@@ -788,8 +690,8 @@
         """
         self.live_timer.stop()
         self.preview_timer.stop()
-        for controller in self.display_controllers:
-            self.media_reset(self.display_controllers[controller])
+        self.media_reset(self.display_controllers(DisplayControllerType.Live))
+        self.media_reset(self.display_controllers(DisplayControllerType.Preview))
 
     @staticmethod
     def _define_display(controller):

=== modified file 'openlp/core/ui/media/mediaplayer.py'
--- openlp/core/ui/media/mediaplayer.py	2019-02-14 15:09:09 +0000
+++ openlp/core/ui/media/mediaplayer.py	2019-04-10 19:57:31 +0000
@@ -52,11 +52,12 @@
         """
         return False
 
-    def setup(self, display):
+    def setup(self, display, live_display):
         """
         Create the related widgets for the current display
 
         :param display: The display to be updated.
+        :param live_display: Is the display a live one.
         """
         pass
 
@@ -78,10 +79,11 @@
         """
         pass
 
-    def play(self, display):
+    def play(self, controller, display):
         """
         Starts playing of current Media File
 
+        :param controller: Which Controller is running the show.
         :param display: The display to be updated.
         """
         pass
@@ -206,7 +208,7 @@
         :param display: Identify the Display type
         :return: None
         """
-        if display.controller.is_live:
+        if display.is_display:
             self.set_live_state(state)
         else:
             self.set_preview_state(state)

=== added file 'openlp/core/ui/media/mediatab.py'
--- openlp/core/ui/media/mediatab.py	1970-01-01 00:00:00 +0000
+++ openlp/core/ui/media/mediatab.py	2019-04-10 19:57:31 +0000
@@ -0,0 +1,148 @@
+# -*- 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                          #
+###############################################################################
+"""
+The :mod:`~openlp.core.ui.media.playertab` module holds the configuration tab for the media stuff.
+"""
+
+from PyQt5 import QtWidgets
+# from PyQt5.QtMultimedia import QCameraInfo, QAudioDeviceInfo, QAudio
+
+from openlp.core.common import is_linux, is_win
+from openlp.core.common.i18n import translate
+from openlp.core.common.settings import Settings
+from openlp.core.lib.settingstab import SettingsTab
+from openlp.core.ui.icons import UiIcons
+
+LINUX_STREAM = 'v4l2:///dev/video0'
+WIN_STREAM = 'dshow:// :dshow-vdev='
+
+
+class MediaTab(SettingsTab):
+    """
+    MediaTab is the Media settings tab in the settings dialog.
+    """
+    def __init__(self, parent):
+        """
+        Constructor
+        """
+        # self.media_players = Registry().get('media_controller').media_players
+        # self.saved_used_players = None
+        self.icon_path = UiIcons().video
+        player_translated = translate('OpenLP.MediaTab', 'Media')
+        super(MediaTab, self).__init__(parent, 'Media', player_translated)
+
+    def setup_ui(self):
+        """
+        Set up the UI
+        """
+        self.setObjectName('MediaTab')
+        super(MediaTab, self).setup_ui()
+        self.live_media_group_box = QtWidgets.QGroupBox(self.left_column)
+        self.live_media_group_box.setObjectName('live_media_group_box')
+        self.media_layout = QtWidgets.QVBoxLayout(self.live_media_group_box)
+        self.media_layout.setObjectName('live_media_layout')
+        self.auto_start_check_box = QtWidgets.QCheckBox(self.live_media_group_box)
+        self.auto_start_check_box.setObjectName('auto_start_check_box')
+        self.media_layout.addWidget(self.auto_start_check_box)
+        self.left_layout.addWidget(self.live_media_group_box)
+        self.stream_media_group_box = QtWidgets.QGroupBox(self.left_column)
+        self.stream_media_group_box.setObjectName('stream_media_group_box')
+        self.stream_media_layout = QtWidgets.QHBoxLayout(self.stream_media_group_box)
+        self.stream_media_layout.setObjectName('live_media_layout')
+        self.stream_media_layout.setContentsMargins(0, 0, 0, 0)
+        self.stream_edit = QtWidgets.QPlainTextEdit(self)
+        self.stream_media_layout.addWidget(self.stream_edit)
+        self.browse_button = QtWidgets.QToolButton(self)
+        self.browse_button.setIcon(UiIcons().undo)
+        self.stream_media_layout.addWidget(self.browse_button)
+        self.left_layout.addWidget(self.stream_media_group_box)
+        self.left_layout.addWidget(self.stream_media_group_box)
+        self.left_layout.addStretch()
+        self.right_layout.addStretch()
+        # # Signals and slots
+        self.browse_button.clicked.connect(self.on_revert)
+
+    def retranslateUi(self):
+        """
+        Translate the UI on the fly
+        """
+        self.live_media_group_box.setTitle(translate('MediaPlugin.MediaTab', 'Live Media'))
+        self.stream_media_group_box.setTitle(translate('MediaPlugin.MediaTab', 'Stream Media Command'))
+        self.auto_start_check_box.setText(translate('MediaPlugin.MediaTab', 'Start automatically'))
+
+    def load(self):
+        """
+        Load the settings
+        """
+        self.auto_start_check_box.setChecked(Settings().value(self.settings_section + '/media auto start'))
+        self.stream_edit.setPlainText(Settings().value(self.settings_section + '/stream command'))
+        if not self.stream_edit.toPlainText():
+            if is_linux:
+                self.stream_edit.setPlainText(LINUX_STREAM)
+            elif is_win:
+                self.stream_edit.setPlainText(WIN_STREAM)
+
+    def save(self):
+        """
+        Save the settings
+        """
+        setting_key = self.settings_section + '/media auto start'
+        if Settings().value(setting_key) != self.auto_start_check_box.checkState():
+            Settings().setValue(setting_key, self.auto_start_check_box.checkState())
+        # settings = Settings()
+        # settings.beginGroup(self.settings_section)
+        # settings.setValue('background color', self.background_color)
+        # settings.endGroup()
+        # old_players, override_player = get_media_players()
+        # if self.used_players != old_players:
+        #     # clean old Media stuff
+        #     set_media_players(self.used_players, override_player)
+        #     self.settings_form.register_post_process('mediaitem_suffix_reset')
+        #     self.settings_form.register_post_process('mediaitem_media_rebuild')
+        #     self.settings_form.register_post_process('config_screen_changed')
+
+    def post_set_up(self, post_update=False):
+        """
+        Late setup for players as the MediaController has to be initialised first.
+
+        :param post_update: Indicates if called before or after updates.
+        """
+        pass
+        # for key, player in self.media_players.items():
+        #     player = self.media_players[key]
+        #     checkbox = MediaQCheckBox(self.media_player_group_box)
+        #     checkbox.setEnabled(player.available)
+        #     checkbox.setObjectName(player.name + '_check_box')
+        #     checkbox.setToolTip(player.get_info())
+        #     checkbox.set_player_name(player.name)
+        #     self.player_check_boxes[player.name] = checkbox
+        #     checkbox.stateChanged.connect(self.on_player_check_box_changed)
+        #     self.media_player_layout.addWidget(checkbox)
+        #     if player.available and player.name in self.used_players:
+        #         checkbox.setChecked(True)
+        #     else:
+        #         checkbox.setChecked(False)
+        # self.update_player_list()
+        # self.retranslate_players()
+
+    def on_revert(self):
+        pass

=== removed file 'openlp/core/ui/media/playertab.py'
--- openlp/core/ui/media/playertab.py	2019-02-14 15:09:09 +0000
+++ openlp/core/ui/media/playertab.py	1970-01-01 00:00:00 +0000
@@ -1,269 +0,0 @@
-# -*- 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                          #
-###############################################################################
-"""
-The :mod:`~openlp.core.ui.media.playertab` module holds the configuration tab for the media stuff.
-"""
-import platform
-
-from PyQt5 import QtCore, QtWidgets
-
-from openlp.core.common.i18n import UiStrings, translate
-# from openlp.core.common.registry import Registry
-from openlp.core.common.settings import Settings
-from openlp.core.lib.settingstab import SettingsTab
-from openlp.core.lib.ui import create_button
-from openlp.core.ui.icons import UiIcons
-from openlp.core.widgets.buttons import ColorButton
-
-
-class MediaQCheckBox(QtWidgets.QCheckBox):
-    """
-    MediaQCheckBox adds an extra property, player_name to the QCheckBox class.
-    """
-    def set_player_name(self, name):
-        """
-        Set the player name
-        """
-        self.player_name = name
-
-
-class PlayerTab(SettingsTab):
-    """
-    MediaTab is the Media settings tab in the settings dialog.
-    """
-    def __init__(self, parent):
-        """
-        Constructor
-        """
-        # self.media_players = Registry().get('media_controller').media_players
-        self.saved_used_players = None
-        self.icon_path = UiIcons().player
-        player_translated = translate('OpenLP.PlayerTab', 'Players')
-        super(PlayerTab, self).__init__(parent, 'Players', player_translated)
-
-    def setup_ui(self):
-        """
-        Set up the UI
-        """
-        self.setObjectName('MediaTab')
-        super(PlayerTab, self).setup_ui()
-        self.background_color_group_box = QtWidgets.QGroupBox(self.left_column)
-        self.background_color_group_box.setObjectName('background_color_group_box')
-        self.form_layout = QtWidgets.QFormLayout(self.background_color_group_box)
-        self.form_layout.setObjectName('form_layout')
-        self.color_layout = QtWidgets.QHBoxLayout()
-        self.background_color_label = QtWidgets.QLabel(self.background_color_group_box)
-        self.background_color_label.setObjectName('background_color_label')
-        self.color_layout.addWidget(self.background_color_label)
-        self.background_color_button = ColorButton(self.background_color_group_box)
-        self.background_color_button.setObjectName('background_color_button')
-        self.color_layout.addWidget(self.background_color_button)
-        self.form_layout.addRow(self.color_layout)
-        self.information_label = QtWidgets.QLabel(self.background_color_group_box)
-        self.information_label.setObjectName('information_label')
-        self.information_label.setWordWrap(True)
-        self.form_layout.addRow(self.information_label)
-        self.left_layout.addWidget(self.background_color_group_box)
-        self.right_column.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
-        self.media_player_group_box = QtWidgets.QGroupBox(self.left_column)
-        self.media_player_group_box.setObjectName('media_player_group_box')
-        self.media_player_layout = QtWidgets.QVBoxLayout(self.media_player_group_box)
-        self.media_player_layout.setObjectName('media_player_layout')
-        self.player_check_boxes = {}
-        self.left_layout.addWidget(self.media_player_group_box)
-        self.player_order_group_box = QtWidgets.QGroupBox(self.left_column)
-        self.player_order_group_box.setObjectName('player_order_group_box')
-        self.player_order_layout = QtWidgets.QHBoxLayout(self.player_order_group_box)
-        self.player_order_layout.setObjectName('player_order_layout')
-        self.player_order_list_widget = QtWidgets.QListWidget(self.player_order_group_box)
-        size_policy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
-        size_policy.setHorizontalStretch(0)
-        size_policy.setVerticalStretch(0)
-        size_policy.setHeightForWidth(self.player_order_list_widget.sizePolicy().hasHeightForWidth())
-        self.player_order_list_widget.setSizePolicy(size_policy)
-        self.player_order_list_widget.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
-        self.player_order_list_widget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
-        self.player_order_list_widget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
-        self.player_order_list_widget.setObjectName('player_order_list_widget')
-        self.player_order_layout.addWidget(self.player_order_list_widget)
-        self.ordering_button_layout = QtWidgets.QVBoxLayout()
-        self.ordering_button_layout.setObjectName('ordering_button_layout')
-        self.ordering_button_layout.addStretch(1)
-        self.ordering_up_button = create_button(self, 'ordering_up_button', role='up',
-                                                click=self.on_up_button_clicked)
-        self.ordering_down_button = create_button(self, 'ordering_down_button', role='down',
-                                                  click=self.on_down_button_clicked)
-        self.ordering_button_layout.addWidget(self.ordering_up_button)
-        self.ordering_button_layout.addWidget(self.ordering_down_button)
-        self.ordering_button_layout.addStretch(1)
-        self.player_order_layout.addLayout(self.ordering_button_layout)
-        self.left_layout.addWidget(self.player_order_group_box)
-        self.left_layout.addStretch()
-        self.right_layout.addStretch()
-        # Signals and slots
-        self.background_color_button.colorChanged.connect(self.on_background_color_changed)
-
-    def retranslate_ui(self):
-        """
-        Translate the UI on the fly
-        """
-        self.media_player_group_box.setTitle(translate('OpenLP.PlayerTab', 'Available Media Players'))
-        self.player_order_group_box.setTitle(translate('OpenLP.PlayerTab', 'Player Search Order'))
-        self.background_color_group_box.setTitle(UiStrings().BackgroundColor)
-        self.background_color_label.setText(UiStrings().BackgroundColorColon)
-        self.information_label.setText(translate('OpenLP.PlayerTab',
-                                       'Visible background for videos with aspect ratio different to screen.'))
-        self.retranslate_players()
-
-    def on_background_color_changed(self, color):
-        """
-        Set the background color
-
-        :param color: The color to be set.
-        """
-        self.background_color = color
-
-    def on_player_check_box_changed(self, check_state):
-        """
-        Add or remove players depending on their status
-
-        :param check_state: The requested status.
-        """
-        player = self.sender().player_name
-        if check_state == QtCore.Qt.Checked:
-            if player not in self.used_players:
-                self.used_players.append(player)
-        else:
-            if player in self.used_players:
-                self.used_players.remove(player)
-        self.update_player_list()
-
-    def update_player_list(self):
-        """
-        Update the list of media players
-        """
-        self.player_order_list_widget.clear()
-        for player in self.used_players:
-            if player in list(self.player_check_boxes.keys()):
-                if len(self.used_players) == 1:
-                    # At least one media player has to stay active
-                    self.player_check_boxes['%s' % player].setEnabled(False)
-                else:
-                    self.player_check_boxes['%s' % player].setEnabled(True)
-                self.player_order_list_widget.addItem(self.media_players[str(player)].original_name)
-
-    def on_up_button_clicked(self):
-        """
-        Move a media player up in the order
-        """
-        row = self.player_order_list_widget.currentRow()
-        if row <= 0:
-            return
-        item = self.player_order_list_widget.takeItem(row)
-        self.player_order_list_widget.insertItem(row - 1, item)
-        self.player_order_list_widget.setCurrentRow(row - 1)
-        self.used_players.insert(row - 1, self.used_players.pop(row))
-
-    def on_down_button_clicked(self):
-        """
-        Move a media player down in the order
-        """
-        row = self.player_order_list_widget.currentRow()
-        if row == -1 or row > self.player_order_list_widget.count() - 1:
-            return
-        item = self.player_order_list_widget.takeItem(row)
-        self.player_order_list_widget.insertItem(row + 1, item)
-        self.player_order_list_widget.setCurrentRow(row + 1)
-        self.used_players.insert(row + 1, self.used_players.pop(row))
-
-    def load(self):
-        """
-        Load the settings
-        """
-        if self.saved_used_players:
-            self.used_players = self.saved_used_players
-        # self.used_players = get_media_players()[0]
-        self.saved_used_players = self.used_players
-        settings = Settings()
-        settings.beginGroup(self.settings_section)
-        self.update_player_list()
-        self.background_color = settings.value('background color')
-        self.initial_color = self.background_color
-        settings.endGroup()
-        self.background_color_button.color = self.background_color
-
-    def save(self):
-        """
-        Save the settings
-        """
-        settings = Settings()
-        settings.beginGroup(self.settings_section)
-        settings.setValue('background color', self.background_color)
-        settings.endGroup()
-        # old_players, override_player = get_media_players()
-        # if self.used_players != old_players:
-        #     # clean old Media stuff
-        #     set_media_players(self.used_players, override_player)
-        #     self.settings_form.register_post_process('mediaitem_suffix_reset')
-        #     self.settings_form.register_post_process('mediaitem_media_rebuild')
-        #     self.settings_form.register_post_process('config_screen_changed')
-
-    def post_set_up(self, post_update=False):
-        """
-        Late setup for players as the MediaController has to be initialised first.
-
-        :param post_update: Indicates if called before or after updates.
-        """
-        for key, player in self.media_players.items():
-            player = self.media_players[key]
-            checkbox = MediaQCheckBox(self.media_player_group_box)
-            checkbox.setEnabled(player.available)
-            checkbox.setObjectName(player.name + '_check_box')
-            checkbox.setToolTip(player.get_info())
-            checkbox.set_player_name(player.name)
-            self.player_check_boxes[player.name] = checkbox
-            checkbox.stateChanged.connect(self.on_player_check_box_changed)
-            self.media_player_layout.addWidget(checkbox)
-            if player.available and player.name in self.used_players:
-                checkbox.setChecked(True)
-            else:
-                checkbox.setChecked(False)
-        self.update_player_list()
-        self.retranslate_players()
-
-    def retranslate_players(self):
-        """
-        Translations for players is dependent on  their setup as well
-         """
-        for key in self.media_players and self.player_check_boxes:
-            player = self.media_players[key]
-            checkbox = self.player_check_boxes[player.name]
-            checkbox.set_player_name(player.name)
-            if player.available:
-                checkbox.setText(player.display_name)
-            else:
-                checkbox_text = translate('OpenLP.PlayerTab', '%s (unavailable)') % player.display_name
-                if player.name == 'vlc':
-                    checkbox_text += ' ' + translate('OpenLP.PlayerTab',
-                                                     'NOTE: To use VLC you must install the %s version',
-                                                     'Will insert "32bit" or "64bit"') % platform.architecture()[0]
-                checkbox.setText(checkbox_text)

=== removed file 'openlp/core/ui/media/systemplayer.py'
--- openlp/core/ui/media/systemplayer.py	2019-02-14 15:09:09 +0000
+++ openlp/core/ui/media/systemplayer.py	1970-01-01 00:00:00 +0000
@@ -1,332 +0,0 @@
-# -*- 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                          #
-###############################################################################
-"""
-The :mod:`~openlp.core.ui.media.systemplayer` contains the system (aka QtMultimedia) player component.
-"""
-import functools
-import logging
-import mimetypes
-
-from PyQt5 import QtCore, QtMultimedia, QtMultimediaWidgets
-
-from openlp.core.common.i18n import translate
-from openlp.core.threading import ThreadWorker, is_thread_finished, run_thread
-from openlp.core.ui.media import MediaState
-from openlp.core.ui.media.mediaplayer import MediaPlayer
-
-
-log = logging.getLogger(__name__)
-
-ADDITIONAL_EXT = {
-    'audio/ac3': ['.ac3'],
-    'audio/flac': ['.flac'],
-    'audio/x-m4a': ['.m4a'],
-    'audio/midi': ['.mid', '.midi'],
-    'audio/x-mp3': ['.mp3'],
-    'audio/mpeg': ['.mp3', '.mp2', '.mpga', '.mpega', '.m4a'],
-    'audio/qcelp': ['.qcp'],
-    'audio/x-wma': ['.wma'],
-    'audio/x-ms-wma': ['.wma'],
-    'video/x-flv': ['.flv'],
-    'video/x-matroska': ['.mpv', '.mkv'],
-    'video/x-wmv': ['.wmv'],
-    'video/x-mpg': ['.mpg'],
-    'video/mpeg': ['.mp4', '.mts', '.mov'],
-    'video/x-ms-wmv': ['.wmv']
-}
-
-
-class SystemPlayer(MediaPlayer):
-    """
-    A specialised version of the MediaPlayer class, which provides a QtMultimedia display.
-    """
-
-    def __init__(self, parent):
-        """
-        Constructor
-        """
-        super(SystemPlayer, self).__init__(parent, 'system')
-        self.original_name = 'System'
-        self.display_name = '&System'
-        self.parent = parent
-        self.additional_extensions = ADDITIONAL_EXT
-        self.media_player = QtMultimedia.QMediaPlayer(None, QtMultimedia.QMediaPlayer.VideoSurface)
-        mimetypes.init()
-        media_service = self.media_player.service()
-        log.info(media_service.__class__.__name__)
-        # supportedMimeTypes doesn't return anything on Linux and Windows and
-        # the mimetypes it returns on Mac OS X may not be playable.
-        supported_codecs = self.media_player.supportedMimeTypes()
-        for mime_type in supported_codecs:
-            mime_type = str(mime_type)
-            log.info(mime_type)
-            if mime_type.startswith('audio/'):
-                self._add_to_list(self.audio_extensions_list, mime_type)
-            elif mime_type.startswith('video/'):
-                self._add_to_list(self.video_extensions_list, mime_type)
-
-    def _add_to_list(self, mime_type_list, mime_type):
-        """
-        Add mimetypes to the provided list
-        """
-        # Add all extensions which mimetypes provides us for supported types.
-        extensions = mimetypes.guess_all_extensions(mime_type)
-        for extension in extensions:
-            ext = '*%s' % extension
-            if ext not in mime_type_list:
-                mime_type_list.append(ext)
-        log.info('MediaPlugin: %s extensions: %s', mime_type, ' '.join(extensions))
-
-    def disconnect_slots(self, signal):
-        """
-        Safely disconnect the slots from `signal`
-        """
-        try:
-            signal.disconnect()
-        except TypeError:
-            # If disconnect() is called on a signal without slots, it throws a TypeError
-            pass
-
-    def setup(self, display):
-        """
-        Set up the player widgets
-        :param display:
-        """
-        display.video_widget = QtMultimediaWidgets.QVideoWidget(display)
-        display.video_widget.resize(display.size())
-        display.media_player = QtMultimedia.QMediaPlayer(display)
-        display.media_player.setVideoOutput(display.video_widget)
-        display.video_widget.raise_()
-        display.video_widget.hide()
-        self.has_own_widget = True
-
-    def check_available(self):
-        """
-        Check if the player is available
-        """
-        return True
-
-    def load(self, display):
-        """
-        Load a video into the display
-
-        :param display: The display where the media is
-        """
-        log.debug('load vid in System Controller')
-        controller = display.controller
-        volume = controller.media_info.volume
-        path = controller.media_info.file_info.absoluteFilePath()
-        # Check if file is playable due to mimetype filters being nonexistent on Linux and Windows
-        if self.check_media(path):
-            display.media_player.setMedia(QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile(path)))
-            self.volume(display, volume)
-            return True
-        else:
-            return False
-
-    def resize(self, display):
-        """
-        Resize the display
-
-        :param display: The display where the media is
-        """
-        display.video_widget.resize(display.size())
-
-    def play(self, display):
-        """
-        Play the current media item
-
-        :param display: The display where the media is
-        """
-        log.info('Play the current item')
-        controller = display.controller
-        start_time = 0
-        if display.controller.is_live:
-            if self.get_live_state() != QtMultimedia.QMediaPlayer.PausedState and controller.media_info.start_time > 0:
-                start_time = controller.media_info.start_time
-        else:
-            if self.get_preview_state() != QtMultimedia.QMediaPlayer.PausedState and \
-                    controller.media_info.start_time > 0:
-                start_time = controller.media_info.start_time
-        display.media_player.play()
-        if start_time > 0:
-            self.seek(display, controller.media_info.start_time * 1000)
-        self.volume(display, controller.media_info.volume)
-        self.disconnect_slots(display.media_player.durationChanged)
-        display.media_player.durationChanged.connect(functools.partial(self.set_duration, controller))
-        self.set_state(MediaState.Playing, display)
-        display.video_widget.raise_()
-        return True
-
-    def pause(self, display):
-        """
-        Pause the current media item
-
-        :param display: The display where the media is
-        """
-        display.media_player.pause()
-        if display.controller.is_live:
-            if self.get_live_state() == QtMultimedia.QMediaPlayer.PausedState:
-                self.set_state(MediaState.Paused, display)
-        else:
-            if self.get_preview_state() == QtMultimedia.QMediaPlayer.PausedState:
-                self.set_state(MediaState.Paused, display)
-
-    def stop(self, display):
-        """
-        Stop the current media item
-
-        :param display: The display where the media is
-        """
-        display.media_player.stop()
-        self.set_visible(display, False)
-        self.set_state(MediaState.Stopped, display)
-
-    def volume(self, display, volume):
-        """
-        Set the volume
-
-        :param display: The display where the media is
-        :param volume: The volume to be set
-        """
-        if display.has_audio:
-            display.media_player.setVolume(volume)
-
-    def seek(self, display, seek_value):
-        """
-        Go to a particular point in the current media item
-
-        :param display: The display where the media is
-        :param seek_value: The where to seek to
-        """
-        display.media_player.setPosition(seek_value)
-
-    def reset(self, display):
-        """
-        Reset the media player
-
-        :param display: The display where the media is
-        """
-        display.media_player.stop()
-        display.media_player.setMedia(QtMultimedia.QMediaContent())
-        self.set_visible(display, False)
-        display.video_widget.setVisible(False)
-        self.set_state(MediaState.Off, display)
-
-    def set_visible(self, display, status):
-        """
-        Set the visibility of the widget
-
-        :param display: The display where the media is
-        :param status: The visibility status to be set
-        """
-        if self.has_own_widget:
-            display.video_widget.setVisible(status)
-
-    @staticmethod
-    def set_duration(controller, duration):
-        """
-
-        :param controller: the controller displaying the media
-        :param duration: how long is the media
-        :return:
-        """
-        controller.seek_slider.setMaximum(controller.media_info.length)
-
-    def update_ui(self, display):
-        """
-        Update the UI
-
-        :param display: The display where the media is
-        """
-        if display.media_player.state() == QtMultimedia.QMediaPlayer.PausedState and self.state != MediaState.Paused:
-            self.pause(display)
-        controller = display.controller
-        if controller.media_info.end_time > 0:
-            if display.media_player.position() > controller.media_info.end_time:
-                self.stop(display)
-                self.set_visible(display, False)
-        if not controller.seek_slider.isSliderDown():
-            controller.seek_slider.blockSignals(True)
-            controller.seek_slider.setSliderPosition(display.media_player.position())
-            controller.seek_slider.blockSignals(False)
-
-    def get_media_display_css(self):
-        """
-        Add css style sheets to htmlbuilder
-        """
-        return ''
-
-    def get_info(self):
-        """
-        Return some info about this player
-        """
-        return (translate('Media.player', 'This media player uses your operating system '
-                                          'to provide media capabilities.') +
-                '<br/> <strong>' + translate('Media.player', 'Audio') +
-                '</strong><br/>' + str(self.audio_extensions_list) +
-                '<br/><strong>' + translate('Media.player', 'Video') +
-                '</strong><br/>' + str(self.video_extensions_list) + '<br/>')
-
-    def check_media(self, path):
-        """
-        Check if a file can be played
-        Uses a separate QMediaPlayer in a thread
-
-        :param path: Path to file to be checked
-        :return: True if file can be played otherwise False
-        """
-        check_media_worker = CheckMediaWorker(path)
-        check_media_worker.setVolume(0)
-        run_thread(check_media_worker, 'check_media')
-        while not is_thread_finished('check_media'):
-            self.application.processEvents()
-        return check_media_worker.result
-
-
-class CheckMediaWorker(QtMultimedia.QMediaPlayer, ThreadWorker):
-    """
-    Class used to check if a media file is playable
-    """
-    def __init__(self, path):
-        super(CheckMediaWorker, self).__init__(None, QtMultimedia.QMediaPlayer.VideoSurface)
-        self.path = path
-
-    def start(self):
-        """
-        Start the thread worker
-        """
-        self.result = None
-        self.error.connect(functools.partial(self.signals, 'error'))
-        self.mediaStatusChanged.connect(functools.partial(self.signals, 'media'))
-        self.setMedia(QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile(self.path)))
-        self.play()
-
-    def signals(self, origin, status):
-        if origin == 'media' and status == self.BufferedMedia:
-            self.result = True
-            self.stop()
-            self.quit.emit()
-        elif origin == 'error' and status != self.NoError:
-            self.result = False
-            self.stop()
-            self.quit.emit()

=== modified file 'openlp/core/ui/media/vlcplayer.py'
--- openlp/core/ui/media/vlcplayer.py	2019-03-17 20:35:11 +0000
+++ openlp/core/ui/media/vlcplayer.py	2019-04-10 19:57:31 +0000
@@ -152,43 +152,42 @@
         self.audio_extensions_list = AUDIO_EXT
         self.video_extensions_list = VIDEO_EXT
 
-    def setup(self, display):
+    def setup(self, output_display, live_display):
         """
         Set up the media player
 
-        :param display: The display where the media is
+        :param output_display: The display where the media is
+        :param live_display: Is the display a live one.
         :return:
         """
         vlc = get_vlc()
-        display.vlc_widget = QtWidgets.QFrame(display)
-        display.vlc_widget.setFrameStyle(QtWidgets.QFrame.NoFrame)
+        output_display.vlc_widget = QtWidgets.QFrame(output_display)
+        output_display.vlc_widget.setFrameStyle(QtWidgets.QFrame.NoFrame)
         # creating a basic vlc instance
         command_line_options = '--no-video-title-show'
-        if not display.has_audio:
-            command_line_options += ' --no-audio --no-video-title-show'
-        if Settings().value('advanced/hide mouse') and display.controller.is_live:
+        if Settings().value('advanced/hide mouse') and live_display:
             command_line_options += ' --mouse-hide-timeout=0'
-        display.vlc_instance = vlc.Instance(command_line_options)
+        output_display.vlc_instance = vlc.Instance(command_line_options)
         # creating an empty vlc media player
-        display.vlc_media_player = display.vlc_instance.media_player_new()
-        display.vlc_widget.resize(display.size())
-        display.vlc_widget.raise_()
-        display.vlc_widget.hide()
+        output_display.vlc_media_player = output_display.vlc_instance.media_player_new()
+        output_display.vlc_widget.resize(output_display.size())
+        output_display.vlc_widget.raise_()
+        output_display.vlc_widget.hide()
         # The media player has to be 'connected' to the QFrame.
         # (otherwise a video would be displayed in it's own window)
         # This is platform specific!
         # You have to give the id of the QFrame (or similar object)
         # to vlc, different platforms have different functions for this.
-        win_id = int(display.vlc_widget.winId())
+        win_id = int(output_display.vlc_widget.winId())
         if is_win():
-            display.vlc_media_player.set_hwnd(win_id)
+            output_display.vlc_media_player.set_hwnd(win_id)
         elif is_macosx():
             # We have to use 'set_nsobject' since Qt5 on OSX uses Cocoa
             # framework and not the old Carbon.
-            display.vlc_media_player.set_nsobject(win_id)
+            output_display.vlc_media_player.set_nsobject(win_id)
         else:
             # for Linux/*BSD using the X Server
-            display.vlc_media_player.set_xwindow(win_id)
+            output_display.vlc_media_player.set_xwindow(win_id)
         self.has_own_widget = True
 
     def check_available(self):
@@ -197,43 +196,45 @@
         """
         return get_vlc() is not None
 
-    def load(self, display, file):
+    def load(self, output_display, file):
         """
         Load a video into VLC
 
-        :param display: The display where the media is
+        :param output_display: The display where the media is
         :param file: file to be played
         :return:
         """
         vlc = get_vlc()
         log.debug('load vid in Vlc Controller')
-        controller = display.controller
+        controller = output_display
         volume = controller.media_info.volume
         path = os.path.normcase(file)
         # create the media
         if controller.media_info.media_type == MediaType.CD:
             if is_win():
                 path = '/' + path
-            display.vlc_media = display.vlc_instance.media_new_location('cdda://' + path)
-            display.vlc_media_player.set_media(display.vlc_media)
-            display.vlc_media_player.play()
+            output_display.vlc_media = output_display.vlc_instance.media_new_location('cdda://' + path)
+            output_display.vlc_media_player.set_media(output_display.vlc_media)
+            output_display.vlc_media_player.play()
             # Wait for media to start playing. In this case VLC actually returns an error.
-            self.media_state_wait(display, vlc.State.Playing)
+            self.media_state_wait(output_display, vlc.State.Playing)
             # If subitems exists, this is a CD
-            audio_cd_tracks = display.vlc_media.subitems()
+            audio_cd_tracks = output_display.vlc_media.subitems()
             if not audio_cd_tracks or audio_cd_tracks.count() < 1:
                 return False
-            display.vlc_media = audio_cd_tracks.item_at_index(controller.media_info.title_track)
+            output_display.vlc_media = audio_cd_tracks.item_at_index(controller.media_info.title_track)
+        elif controller.media_info.media_type == MediaType.Stream:
+            output_display.vlc_media = output_display.vlc_instance.media_new_location('ZZZZZZ')
         else:
-            display.vlc_media = display.vlc_instance.media_new_path(path)
+            output_display.vlc_media = output_display.vlc_instance.media_new_path(path)
         # put the media in the media player
-        display.vlc_media_player.set_media(display.vlc_media)
+        output_display.vlc_media_player.set_media(output_display.vlc_media)
         # parse the metadata of the file
-        display.vlc_media.parse()
-        self.volume(display, volume)
+        output_display.vlc_media.parse()
+        self.volume(output_display, volume)
         return True
 
-    def media_state_wait(self, display, media_state):
+    def media_state_wait(self, output_display, media_state):
         """
         Wait for the video to change its state
         Wait no longer than 60 seconds. (loading an iso file needs a long time)
@@ -244,171 +245,171 @@
         """
         vlc = get_vlc()
         start = datetime.now()
-        while media_state != display.vlc_media.get_state():
-            if display.vlc_media.get_state() == vlc.State.Error:
+        while media_state != output_display.vlc_media.get_state():
+            if output_display.vlc_media.get_state() == vlc.State.Error:
                 return False
             self.application.process_events()
             if (datetime.now() - start).seconds > 60:
                 return False
         return True
 
-    def resize(self, display):
+    def resize(self, output_display):
         """
         Resize the player
 
-        :param display: The display where the media is
+        :param output_display: The display where the media is
         :return:
         """
-        display.vlc_widget.resize(display.size())
+        output_display.vlc_widget.resize(output_display.size())
 
-    def play(self, display):
+    def play(self, controller, output_display):
         """
         Play the current item
 
-        :param display: The display where the media is
+        :param controller: Which Controller is running the show.
+        :param output_display: The display where the media is
         :return:
         """
         vlc = get_vlc()
-        controller = display.controller
         start_time = 0
         log.debug('vlc play')
-        if display.controller.is_live:
-            if self.get_live_state() != MediaState.Paused and controller.media_info.start_time > 0:
-                start_time = controller.media_info.start_time
+        if output_display.is_display:
+            if self.get_live_state() != MediaState.Paused and output_display.media_info.start_time > 0:
+                start_time = output_display.media_info.start_time
         else:
-            if self.get_preview_state() != MediaState.Paused and controller.media_info.start_time > 0:
-                start_time = controller.media_info.start_time
-        threading.Thread(target=display.vlc_media_player.play).start()
-        if not self.media_state_wait(display, vlc.State.Playing):
+            if self.get_preview_state() != MediaState.Paused and output_display.media_info.start_time > 0:
+                start_time = output_display.media_info.start_time
+        threading.Thread(target=output_display.vlc_media_player.play).start()
+        if not self.media_state_wait(output_display, vlc.State.Playing):
             return False
-        if display.controller.is_live:
-            if self.get_live_state() != MediaState.Paused and controller.media_info.start_time > 0:
+        if output_display.is_display:
+            if self.get_live_state() != MediaState.Paused and output_display.media_info.start_time > 0:
                 log.debug('vlc play, start time set')
-                start_time = controller.media_info.start_time
+                start_time = output_display.media_info.start_time
         else:
-            if self.get_preview_state() != MediaState.Paused and controller.media_info.start_time > 0:
+            if self.get_preview_state() != MediaState.Paused and output_display.media_info.start_time > 0:
                 log.debug('vlc play, start time set')
-                start_time = controller.media_info.start_time
-        log.debug('mediatype: ' + str(controller.media_info.media_type))
+                start_time = output_display.media_info.start_time
+        log.debug('mediatype: ' + str(output_display.media_info.media_type))
         # Set tracks for the optical device
-        if controller.media_info.media_type == MediaType.DVD and \
+        if output_display.media_info.media_type == MediaType.DVD and \
                 self.get_live_state() != MediaState.Paused and self.get_preview_state() != MediaState.Paused:
             log.debug('vlc play, playing started')
-            if controller.media_info.title_track > 0:
-                log.debug('vlc play, title_track set: ' + str(controller.media_info.title_track))
-                display.vlc_media_player.set_title(controller.media_info.title_track)
-            display.vlc_media_player.play()
-            if not self.media_state_wait(display, vlc.State.Playing):
+            if output_display.media_info.title_track > 0:
+                log.debug('vlc play, title_track set: ' + str(output_display.media_info.title_track))
+                output_display.vlc_media_player.set_title(output_display.media_info.title_track)
+            output_display.vlc_media_player.play()
+            if not self.media_state_wait(output_display, vlc.State.Playing):
                 return False
-            if controller.media_info.audio_track > 0:
-                display.vlc_media_player.audio_set_track(controller.media_info.audio_track)
-                log.debug('vlc play, audio_track set: ' + str(controller.media_info.audio_track))
-            if controller.media_info.subtitle_track > 0:
-                display.vlc_media_player.video_set_spu(controller.media_info.subtitle_track)
-                log.debug('vlc play, subtitle_track set: ' + str(controller.media_info.subtitle_track))
-            if controller.media_info.start_time > 0:
-                log.debug('vlc play, starttime set: ' + str(controller.media_info.start_time))
-                start_time = controller.media_info.start_time
-            controller.media_info.length = controller.media_info.end_time - controller.media_info.start_time
-        self.volume(display, controller.media_info.volume)
-        if start_time > 0 and display.vlc_media_player.is_seekable():
-            display.vlc_media_player.set_time(int(start_time))
-        controller.seek_slider.setMaximum(controller.media_info.length)
-        self.set_state(MediaState.Playing, display)
-        display.vlc_widget.raise_()
+            if output_display.media_info.audio_track > 0:
+                output_display.vlc_media_player.audio_set_track(output_display.media_info.audio_track)
+                log.debug('vlc play, audio_track set: ' + str(output_display.media_info.audio_track))
+            if output_display.media_info.subtitle_track > 0:
+                output_display.vlc_media_player.video_set_spu(output_display.media_info.subtitle_track)
+                log.debug('vlc play, subtitle_track set: ' + str(output_display.media_info.subtitle_track))
+            if output_display.media_info.start_time > 0:
+                log.debug('vlc play, starttime set: ' + str(output_display.media_info.start_time))
+                start_time = output_display.media_info.start_time
+            output_display.media_info.length = output_display.media_info.end_time - output_display.media_info.start_time
+        self.volume(output_display, output_display.media_info.volume)
+        if start_time > 0 and output_display.vlc_media_player.is_seekable():
+            output_display.vlc_media_player.set_time(int(start_time))
+        controller.seek_slider.setMaximum(output_display.media_info.length)
+        self.set_state(MediaState.Playing, output_display)
+        output_display.vlc_widget.raise_()
         return True
 
-    def pause(self, display):
+    def pause(self, output_display):
         """
         Pause the current item
 
-        :param display: The display where the media is
+        :param output_display: The display where the media is
         :return:
         """
         vlc = get_vlc()
-        if display.vlc_media.get_state() != vlc.State.Playing:
+        if output_display.vlc_media.get_state() != vlc.State.Playing:
             return
-        display.vlc_media_player.pause()
-        if self.media_state_wait(display, vlc.State.Paused):
-            self.set_state(MediaState.Paused, display)
+        output_display.vlc_media_player.pause()
+        if self.media_state_wait(output_display, vlc.State.Paused):
+            self.set_state(MediaState.Paused, output_display)
 
-    def stop(self, display):
+    def stop(self, output_display):
         """
         Stop the current item
 
-        :param display: The display where the media is
+        :param output_display: The display where the media is
         :return:
         """
-        threading.Thread(target=display.vlc_media_player.stop).start()
-        self.set_state(MediaState.Stopped, display)
+        threading.Thread(target=output_display.vlc_media_player.stop).start()
+        self.set_state(MediaState.Stopped, output_display)
 
-    def volume(self, display, vol):
+    def volume(self, output_display, vol):
         """
         Set the volume
 
         :param vol: The volume to be sets
-        :param display: The display where the media is
+        :param output_display: The display where the media is
         :return:
         """
-        if display.has_audio:
-            display.vlc_media_player.audio_set_volume(vol)
+        if output_display.has_audio:
+            output_display.vlc_media_player.audio_set_volume(vol)
 
-    def seek(self, display, seek_value):
+    def seek(self, output_display, seek_value):
         """
         Go to a particular position
 
         :param seek_value: The position of where a seek goes to
-        :param display: The display where the media is
+        :param output_display: The display where the media is
         """
-        if display.controller.media_info.media_type == MediaType.CD \
-                or display.controller.media_info.media_type == MediaType.DVD:
-            seek_value += int(display.controller.media_info.start_time)
-        if display.vlc_media_player.is_seekable():
-            display.vlc_media_player.set_time(seek_value)
+        if output_display.controller.media_info.media_type == MediaType.CD \
+                or output_display.controller.media_info.media_type == MediaType.DVD:
+            seek_value += int(output_display.controller.media_info.start_time)
+        if output_display.vlc_media_player.is_seekable():
+            output_display.vlc_media_player.set_time(seek_value)
 
-    def reset(self, display):
+    def reset(self, output_display):
         """
         Reset the player
 
-        :param display: The display where the media is
+        :param output_display: The display where the media is
         """
-        display.vlc_media_player.stop()
-        display.vlc_widget.setVisible(False)
-        self.set_state(MediaState.Off, display)
+        output_display.vlc_media_player.stop()
+        output_display.vlc_widget.setVisible(False)
+        self.set_state(MediaState.Off, output_display)
 
-    def set_visible(self, display, status):
+    def set_visible(self, output_display, status):
         """
         Set the visibility
 
-        :param display: The display where the media is
+        :param output_display: The display where the media is
         :param status: The visibility status
         """
         if self.has_own_widget:
-            display.vlc_widget.setVisible(status)
+            output_display.vlc_widget.setVisible(status)
 
-    def update_ui(self, display):
+    def update_ui(self, controller, output_display):
         """
         Update the UI
 
-        :param display: The display where the media is
+        :param controller: Which Controller is running the show.
+        :param output_display: The display where the media is
         """
         vlc = get_vlc()
         # Stop video if playback is finished.
-        if display.vlc_media.get_state() == vlc.State.Ended:
-            self.stop(display)
-        controller = display.controller
+        if output_display.vlc_media.get_state() == vlc.State.Ended:
+            self.stop(output_display)
         if controller.media_info.end_time > 0:
-            if display.vlc_media_player.get_time() > controller.media_info.end_time:
-                self.stop(display)
-                self.set_visible(display, False)
+            if output_display.vlc_media_player.get_time() > controller.media_info.end_time:
+                self.stop(output_display)
+                self.set_visible(output_display, False)
         if not controller.seek_slider.isSliderDown():
             controller.seek_slider.blockSignals(True)
-            if display.controller.media_info.media_type == MediaType.CD \
-                    or display.controller.media_info.media_type == MediaType.DVD:
+            if controller.media_info.media_type == MediaType.CD \
+                    or controller.media_info.media_type == MediaType.DVD:
                 controller.seek_slider.setSliderPosition(
-                    display.vlc_media_player.get_time() - int(display.controller.media_info.start_time))
+                    output_display.vlc_media_player.get_time() - int(output_display.controller.media_info.start_time))
             else:
-                controller.seek_slider.setSliderPosition(display.vlc_media_player.get_time())
+                controller.seek_slider.setSliderPosition(output_display.vlc_media_player.get_time())
             controller.seek_slider.blockSignals(False)
 
     def get_info(self):

=== modified file 'openlp/core/ui/screenstab.py'
--- openlp/core/ui/screenstab.py	2019-02-14 15:09:09 +0000
+++ openlp/core/ui/screenstab.py	2019-04-10 19:57:31 +0000
@@ -41,7 +41,7 @@
         """
         Initialise the screen settings tab
         """
-        self.icon_path = UiIcons().settings
+        self.icon_path = UiIcons().desktop
         screens_translated = translate('OpenLP.ScreensTab', 'Screens')
         super(ScreensTab, self).__init__(parent, 'Screens', screens_translated)
         self.settings_section = 'core'

=== modified file 'openlp/core/ui/settingsform.py'
--- openlp/core/ui/settingsform.py	2019-02-14 15:09:09 +0000
+++ openlp/core/ui/settingsform.py	2019-04-10 19:57:31 +0000
@@ -30,13 +30,14 @@
 from openlp.core.api.tab import ApiTab
 from openlp.core.common.mixins import RegistryProperties
 from openlp.core.common.registry import Registry
+from openlp.core.common.settings import Settings
 from openlp.core.lib import build_icon
 from openlp.core.projectors.tab import ProjectorTab
 from openlp.core.ui.advancedtab import AdvancedTab
 from openlp.core.ui.generaltab import GeneralTab
 from openlp.core.ui.screenstab import ScreensTab
 from openlp.core.ui.themestab import ThemesTab
-# from openlp.core.ui.media.playertab import PlayerTab
+from openlp.core.ui.media.mediatab import MediaTab
 from openlp.core.ui.settingsdialog import Ui_SettingsDialog
 
 
@@ -78,8 +79,8 @@
         self.insert_tab(self.advanced_tab)
         self.insert_tab(self.screens_tab)
         self.insert_tab(self.themes_tab)
-        self.insert_tab(self.advanced_tab)
-        # self.insert_tab(self.player_tab)
+        if Settings().value('core/experimental'):
+            self.insert_tab(self.player_tab)
         self.insert_tab(self.projector_tab)
         self.insert_tab(self.api_tab)
         for plugin in State().list_plugins():
@@ -160,15 +161,17 @@
             self.themes_tab = ThemesTab(self)
             self.projector_tab = ProjectorTab(self)
             self.advanced_tab = AdvancedTab(self)
-            # self.player_tab = PlayerTab(self)
+            if Settings().value('core/experimental'):
+                self.player_tab = MediaTab(self)
             self.api_tab = ApiTab(self)
             self.screens_tab = ScreensTab(self)
         except Exception as e:
             print(e)
-        # Post setup
         self.general_tab.post_set_up()
         self.themes_tab.post_set_up()
         self.advanced_tab.post_set_up()
+        if Settings().value('core/experimental'):
+            self.player_tab.post_set_up()
         self.api_tab.post_set_up()
         for plugin in State().list_plugins():
             if plugin.settings_tab:

=== modified file 'openlp/core/ui/slidecontroller.py'
--- openlp/core/ui/slidecontroller.py	2019-02-14 15:09:09 +0000
+++ openlp/core/ui/slidecontroller.py	2019-04-10 19:57:31 +0000
@@ -23,6 +23,7 @@
 The :mod:`slidecontroller` module contains the most important part of OpenLP - the slide controller
 """
 import copy
+import datetime
 from collections import deque
 from threading import Lock
 
@@ -70,6 +71,45 @@
 ]
 
 
+class MediaSlider(QtWidgets.QSlider):
+    """
+    Allows the mouse events of a slider to be overridden and extra functionality added
+    """
+    def __init__(self, direction, manager, controller):
+        """
+        Constructor
+        """
+        super(MediaSlider, self).__init__(direction)
+        self.manager = manager
+        self.controller = controller
+
+    def mouseMoveEvent(self, event):
+        """
+        Override event to allow hover time to be displayed.
+
+        :param event: The triggering event
+        """
+        time_value = QtWidgets.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), event.x(), self.width())
+        self.setToolTip('%s' % datetime.timedelta(seconds=int(time_value / 1000)))
+        QtWidgets.QSlider.mouseMoveEvent(self, event)
+
+    def mousePressEvent(self, event):
+        """
+        Mouse Press event no new functionality
+        :param event: The triggering event
+        """
+        QtWidgets.QSlider.mousePressEvent(self, event)
+
+    def mouseReleaseEvent(self, event):
+        """
+        Set the slider position when the mouse is clicked and released on the slider.
+
+        :param event: The triggering event
+        """
+        self.setValue(QtWidgets.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), event.x(), self.width()))
+        QtWidgets.QSlider.mouseReleaseEvent(self, event)
+
+
 class InfoLabel(QtWidgets.QLabel):
     """
     InfoLabel is a subclassed QLabel. Created to provide the ablilty to add a ellipsis if the text is cut off. Original
@@ -316,8 +356,59 @@
                                                               'Clear'),
                                             triggers=self.on_clear)
         self.controller_layout.addWidget(self.toolbar)
-        # Build the Media Toolbar
-        self.media_controller.register_controller(self)
+        # Build a Media ToolBar
+        self.mediabar = OpenLPToolbar(self)
+        self.mediabar.add_toolbar_action('playbackPlay', text='media_playback_play',
+                                         icon=UiIcons().play,
+                                         tooltip=translate('OpenLP.SlideController', 'Start playing media.'),
+                                         triggers=self.send_to_plugins)
+        self.mediabar.add_toolbar_action('playbackPause', text='media_playback_pause',
+                                         icon=UiIcons().pause,
+                                         tooltip=translate('OpenLP.SlideController', 'Pause playing media.'),
+                                         triggers=self.send_to_plugins)
+        self.mediabar.add_toolbar_action('playbackStop', text='media_playback_stop',
+                                         icon=UiIcons().stop,
+                                         tooltip=translate('OpenLP.SlideController', 'Stop playing media.'),
+                                         triggers=self.send_to_plugins)
+        self.mediabar.add_toolbar_action('playbackLoop', text='media_playback_loop',
+                                         icon=UiIcons().repeat, checked=False,
+                                         tooltip=translate('OpenLP.SlideController', 'Loop playing media.'),
+                                         triggers=self.send_to_plugins)
+        self.position_label = QtWidgets.QLabel()
+        self.position_label.setText(' 00:00 / 00:00')
+        self.position_label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+        self.position_label.setToolTip(translate('OpenLP.SlideController', 'Video timer.'))
+        self.position_label.setMinimumSize(90, 0)
+        self.position_label.setObjectName('position_label')
+        self.mediabar.add_toolbar_widget(self.position_label)
+        # Build the seek_slider.
+        self.seek_slider = MediaSlider(QtCore.Qt.Horizontal, self, self)
+        self.seek_slider.setMaximum(1000)
+        self.seek_slider.setTracking(True)
+        self.seek_slider.setMouseTracking(True)
+        self.seek_slider.setToolTip(translate('OpenLP.SlideController', 'Video position.'))
+        self.seek_slider.setGeometry(QtCore.QRect(90, 260, 221, 24))
+        self.seek_slider.setObjectName('seek_slider')
+        self.mediabar.add_toolbar_widget(self.seek_slider)
+        # Build the volume_slider.
+        self.volume_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
+        self.volume_slider.setTickInterval(10)
+        self.volume_slider.setTickPosition(QtWidgets.QSlider.TicksAbove)
+        self.volume_slider.setMinimum(0)
+        self.volume_slider.setMaximum(100)
+        self.volume_slider.setTracking(True)
+        self.volume_slider.setToolTip(translate('OpenLP.SlideController', 'Audio Volume.'))
+        # self.volume_slider.setValue(self.media_info.volume)
+        self.volume_slider.setGeometry(QtCore.QRect(90, 160, 221, 24))
+        self.volume_slider.setObjectName('volume_slider')
+        self.mediabar.add_toolbar_widget(self.volume_slider)
+        self.controller_layout.addWidget(self.mediabar)
+        self.mediabar.setVisible(False)
+        if not self.is_live:
+            self.volume_slider.setEnabled(False)
+        # Signals
+        self.seek_slider.valueChanged.connect(self.send_to_plugins)
+        self.volume_slider.valueChanged.connect(self.send_to_plugins)
         if self.is_live:
             # Build the Song Toolbar
             self.song_menu = QtWidgets.QToolButton(self.toolbar)
@@ -556,8 +647,6 @@
         #     self.__add_actions_to_widget(self.display)
         # The SlidePreview's ratio.
 
-        # TODO: Need to basically update everything
-
     def __add_actions_to_widget(self, widget):
         """
         Add actions to the widget specified by `widget`
@@ -1398,10 +1487,10 @@
         :param item: The service item to be processed
         """
         if self.is_live and self.hide_mode() == HideMode.Theme:
-            self.media_controller.video(self.controller_type, item, HideMode.Blank)
+            self.media_controller.load_video(self.controller_type, item, HideMode.Blank)
             self.on_blank_display(True)
         else:
-            self.media_controller.video(self.controller_type, item, self.hide_mode())
+            self.media_controller.load_video(self.controller_type, item, self.hide_mode())
         if not self.is_live:
             self.preview_display.show()
 
@@ -1491,7 +1580,7 @@
         self.type_prefix = 'preview'
         self.category = 'Preview Toolbar'
 
-    def bootstrap_post_set_up(self):
+    def bootstrap_initialise(self):
         """
         process the bootstrap post setup request
         """
@@ -1523,7 +1612,7 @@
         self.category = UiStrings().LiveToolbar
         ActionList.get_instance().add_category(str(self.category), CategoryOrder.standard_toolbar)
 
-    def bootstrap_post_set_up(self):
+    def bootstrap_initialise(self):
         """
         process the bootstrap post setup request
         """

=== modified file 'openlp/core/ui/themeform.py'
--- openlp/core/ui/themeform.py	2019-02-14 15:09:09 +0000
+++ openlp/core/ui/themeform.py	2019-04-10 19:57:31 +0000
@@ -330,6 +330,8 @@
             self.video_color_button.color = self.theme.background_border_color
             self.video_path_edit.path = self.theme.background_filename
             self.setField('background_type', 4)
+        elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Stream):
+            self.setField('background_type', 5)
         elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Transparent):
             self.setField('background_type', 3)
         if self.theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.Horizontal):

=== modified file 'openlp/core/ui/themewizard.py'
--- openlp/core/ui/themewizard.py	2019-02-14 15:09:09 +0000
+++ openlp/core/ui/themewizard.py	2019-04-10 19:57:31 +0000
@@ -64,7 +64,7 @@
         self.background_label = QtWidgets.QLabel(self.background_page)
         self.background_label.setObjectName('background_label')
         self.background_combo_box = QtWidgets.QComboBox(self.background_page)
-        self.background_combo_box.addItems(['', '', '', '', ''])
+        self.background_combo_box.addItems(['', '', '', '', '', ''])
         self.background_combo_box.setObjectName('background_combo_box')
         self.background_type_layout.addRow(self.background_label, self.background_combo_box)
         self.background_type_layout.setItem(1, QtWidgets.QFormLayout.LabelRole, self.spacer)
@@ -410,6 +410,8 @@
         self.background_combo_box.setItemText(BackgroundType.Video, UiStrings().Video)
         self.background_combo_box.setItemText(BackgroundType.Transparent,
                                               translate('OpenLP.ThemeWizard', 'Transparent'))
+        self.background_combo_box.setItemText(BackgroundType.Stream,
+                                              translate('OpenLP.ThemeWizard', 'Live Stream'))
         self.color_label.setText(translate('OpenLP.ThemeWizard', 'color:'))
         self.gradient_start_label.setText(translate('OpenLP.ThemeWizard', 'Starting color:'))
         self.gradient_end_label.setText(translate('OpenLP.ThemeWizard', 'Ending color:'))

=== modified file 'openlp/core/widgets/views.py'
--- openlp/core/widgets/views.py	2019-02-14 15:09:09 +0000
+++ openlp/core/widgets/views.py	2019-04-10 19:57:31 +0000
@@ -203,7 +203,10 @@
                     if self.service_item.is_capable(ItemCapabilities.HasThumbnails):
                         pixmap = QtGui.QPixmap(remove_url_prefix(slide['thumbnail']))
                     else:
-                        pixmap = QtGui.QPixmap(remove_url_prefix(slide['image']))
+                        if isinstance(slide['image'], QtGui.QIcon):
+                            pixmap = slide['image'].pixmap(QtCore.QSize(32, 32))
+                        else:
+                            pixmap = QtGui.QPixmap(remove_url_prefix(slide['image']))
                 else:
                     pixmap = QtGui.QPixmap(remove_url_prefix(slide['path']))
                 label.setPixmap(pixmap)

=== modified file 'openlp/plugins/media/lib/mediaitem.py'
--- openlp/plugins/media/lib/mediaitem.py	2019-03-17 20:35:11 +0000
+++ openlp/plugins/media/lib/mediaitem.py	2019-04-10 19:57:31 +0000
@@ -116,6 +116,8 @@
             self.can_preview = False
             self.can_make_live = False
             self.can_add_to_service = False
+        if State().check_preconditions('media_live'):
+            self.can_make_live = False
 
     def add_list_view_to_toolbar(self):
         """
@@ -264,7 +266,8 @@
         :param media: The media
         :param target_group:
         """
-        media.sort(key=lambda file_path: get_natural_key(file_path.name))
+        # TODO needs to be fixed as no idea why this fails
+        # media.sort(key=lambda file_path: get_natural_key(file_path.name))
         for track in media:
             track_info = QtCore.QFileInfo(track)
             item_name = None

=== removed file 'openlp/plugins/media/lib/mediatab.py'
--- openlp/plugins/media/lib/mediatab.py	2019-02-14 15:09:09 +0000
+++ openlp/plugins/media/lib/mediatab.py	1970-01-01 00:00:00 +0000
@@ -1,73 +0,0 @@
-# -*- 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                          #
-###############################################################################
-
-from PyQt5 import QtWidgets
-
-from openlp.core.common.i18n import UiStrings, translate
-from openlp.core.common.settings import Settings
-from openlp.core.lib.settingstab import SettingsTab
-
-
-class MediaTab(SettingsTab):
-    """
-    MediaTab is the Media settings tab in the settings dialog.
-    """
-    def __init__(self, parent, title, visible_title, icon_path):
-        self.parent = parent
-        super(MediaTab, self).__init__(parent, title, visible_title, icon_path)
-
-    def setup_ui(self):
-        self.setObjectName('MediaTab')
-        super(MediaTab, self).setup_ui()
-        self.advanced_group_box = QtWidgets.QGroupBox(self.left_column)
-        self.advanced_group_box.setObjectName('advanced_group_box')
-        self.advanced_layout = QtWidgets.QVBoxLayout(self.advanced_group_box)
-        self.advanced_layout.setObjectName('advanced_layout')
-        self.override_player_check_box = QtWidgets.QCheckBox(self.advanced_group_box)
-        self.override_player_check_box.setObjectName('override_player_check_box')
-        self.advanced_layout.addWidget(self.override_player_check_box)
-        self.auto_start_check_box = QtWidgets.QCheckBox(self.advanced_group_box)
-        self.auto_start_check_box.setObjectName('auto_start_check_box')
-        self.advanced_layout.addWidget(self.auto_start_check_box)
-        self.left_layout.addWidget(self.advanced_group_box)
-        self.left_layout.addStretch()
-        self.right_layout.addStretch()
-
-    def retranslate_ui(self):
-        self.advanced_group_box.setTitle(UiStrings().Advanced)
-        self.override_player_check_box.setText(translate('MediaPlugin.MediaTab', 'Allow media player to be overridden'))
-        self.auto_start_check_box.setText(translate('MediaPlugin.MediaTab', 'Start new Live media automatically'))
-
-    def load(self):
-        self.override_player_check_box.setChecked(Settings().value(self.settings_section + '/override player'))
-        self.auto_start_check_box.setChecked(Settings().value(self.settings_section + '/media auto start'))
-
-    def save(self):
-        setting_key = self.settings_section + '/override player'
-        if Settings().value(setting_key) != self.override_player_check_box.checkState():
-            Settings().setValue(setting_key, self.override_player_check_box.checkState())
-            self.settings_form.register_post_process('mediaitem_suffix_reset')
-            self.settings_form.register_post_process('mediaitem_media_rebuild')
-            self.settings_form.register_post_process('mediaitem_suffixes')
-        setting_key = self.settings_section + '/media auto start'
-        if Settings().value(setting_key) != self.auto_start_check_box.checkState():
-            Settings().setValue(setting_key, self.auto_start_check_box.checkState())

=== modified file 'openlp/plugins/media/mediaplugin.py'
--- openlp/plugins/media/mediaplugin.py	2019-02-14 15:09:09 +0000
+++ openlp/plugins/media/mediaplugin.py	2019-04-10 19:57:31 +0000
@@ -24,8 +24,6 @@
 """
 import logging
 
-from PyQt5 import QtCore
-
 from openlp.core.state import State
 from openlp.core.api.http import register_endpoint
 from openlp.core.common.i18n import translate
@@ -34,7 +32,6 @@
 from openlp.core.lib.plugin import Plugin, StringContent
 from openlp.plugins.media.endpoint import api_media_endpoint, media_endpoint
 from openlp.plugins.media.lib.mediaitem import MediaMediaItem
-from openlp.plugins.media.lib.mediatab import MediaTab
 
 
 log = logging.getLogger(__name__)
@@ -42,7 +39,6 @@
 
 # Some settings starting with "media" are in core, because they are needed for core functionality.
 __default_settings__ = {
-    'media/media auto start': QtCore.Qt.Unchecked,
     'media/media files': [],
     'media/last directory': None
 }
@@ -78,15 +74,6 @@
         """
         pass
 
-    def create_settings_tab(self, parent):
-        """
-        Create the settings Tab
-
-        :param parent:
-        """
-        visible_name = self.get_string(StringContent.VisibleName)
-        self.settings_tab = MediaTab(parent, self.name, visible_name['title'], self.icon_path)
-
     @staticmethod
     def about():
         """

=== modified file 'tests/functional/openlp_core/common/test_common.py'
--- tests/functional/openlp_core/common/test_common.py	2019-02-14 15:09:09 +0000
+++ tests/functional/openlp_core/common/test_common.py	2019-04-10 19:57:31 +0000
@@ -139,13 +139,13 @@
         Test `path_to_module` when supplied with a `Path` object
         """
         # GIVEN: A `Path` object
-        path = Path('core', 'ui', 'media', 'webkitplayer.py')
+        path = Path('core', 'ui', 'media', 'vlcplayer.py')
 
         # WHEN: Calling path_to_module with the `Path` object
         result = path_to_module(path)
 
         # THEN: path_to_module should return the module name
-        assert result == 'openlp.core.ui.media.webkitplayer'
+        assert result == 'openlp.core.ui.media.vlcplayer'
 
     def test_trace_error_handler(self):
         """

=== removed file 'tests/functional/openlp_core/ui/media/test_systemplayer.py'
--- tests/functional/openlp_core/ui/media/test_systemplayer.py	2019-02-14 15:09:09 +0000
+++ tests/functional/openlp_core/ui/media/test_systemplayer.py	1970-01-01 00:00:00 +0000
@@ -1,567 +0,0 @@
-# -*- 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                          #
-###############################################################################
-"""
-Package to test the openlp.core.ui.media.systemplayer package.
-"""
-from unittest import TestCase
-from unittest.mock import MagicMock, call, patch
-
-from PyQt5 import QtCore, QtMultimedia
-
-from openlp.core.common.registry import Registry
-from openlp.core.ui.media import MediaState
-from openlp.core.ui.media.systemplayer import ADDITIONAL_EXT, CheckMediaWorker, SystemPlayer
-
-
-class TestSystemPlayer(TestCase):
-    """
-    Test the system media player
-    """
-    @patch('openlp.core.ui.media.systemplayer.mimetypes')
-    @patch('openlp.core.ui.media.systemplayer.QtMultimedia.QMediaPlayer')
-    def test_constructor(self, MockQMediaPlayer, mocked_mimetypes):
-        """
-        Test the SystemPlayer constructor
-        """
-        # GIVEN: The SystemPlayer class and a mockedQMediaPlayer
-        mocked_media_player = MagicMock()
-        mocked_media_player.supportedMimeTypes.return_value = [
-            'application/postscript',
-            'audio/aiff',
-            'audio/x-aiff',
-            'text/html',
-            'video/animaflex',
-            'video/x-ms-asf'
-        ]
-        mocked_mimetypes.guess_all_extensions.side_effect = [
-            ['.aiff'],
-            ['.aiff'],
-            ['.afl'],
-            ['.asf']
-        ]
-        MockQMediaPlayer.return_value = mocked_media_player
-
-        # WHEN: An object is created from it
-        player = SystemPlayer(self)
-
-        # THEN: The correct initial values should be set up
-        assert 'system' == player.name
-        assert 'System' == player.original_name
-        assert '&System' == player.display_name
-        assert self == player.parent
-        assert ADDITIONAL_EXT == player.additional_extensions
-        MockQMediaPlayer.assert_called_once_with(None, QtMultimedia.QMediaPlayer.VideoSurface)
-        mocked_mimetypes.init.assert_called_once_with()
-        mocked_media_player.service.assert_called_once_with()
-        mocked_media_player.supportedMimeTypes.assert_called_once_with()
-        assert ['*.aiff'] == player.audio_extensions_list
-        assert ['*.afl', '*.asf'] == player.video_extensions_list
-
-    @patch('openlp.core.ui.media.systemplayer.QtMultimediaWidgets.QVideoWidget')
-    @patch('openlp.core.ui.media.systemplayer.QtMultimedia.QMediaPlayer')
-    def test_setup(self, MockQMediaPlayer, MockQVideoWidget):
-        """
-        Test the setup() method of SystemPlayer
-        """
-        # GIVEN: A SystemPlayer instance and a mock display
-        player = SystemPlayer(self)
-        mocked_display = MagicMock()
-        mocked_display.size.return_value = [1, 2, 3, 4]
-        mocked_video_widget = MagicMock()
-        mocked_media_player = MagicMock()
-        MockQVideoWidget.return_value = mocked_video_widget
-        MockQMediaPlayer.return_value = mocked_media_player
-
-        # WHEN: setup() is run
-        player.setup(mocked_display)
-
-        # THEN: The player should have a display widget
-        MockQVideoWidget.assert_called_once_with(mocked_display)
-        assert mocked_video_widget == mocked_display.video_widget
-        mocked_display.size.assert_called_once_with()
-        mocked_video_widget.resize.assert_called_once_with([1, 2, 3, 4])
-        MockQMediaPlayer.assert_called_with(mocked_display)
-        assert mocked_media_player == mocked_display.media_player
-        mocked_media_player.setVideoOutput.assert_called_once_with(mocked_video_widget)
-        mocked_video_widget.raise_.assert_called_once_with()
-        mocked_video_widget.hide.assert_called_once_with()
-        assert player.has_own_widget is True
-
-    def test_disconnect_slots(self):
-        """
-        Test that we the disconnect slots method catches the TypeError
-        """
-        # GIVEN: A SystemPlayer class and a signal that throws a TypeError
-        player = SystemPlayer(self)
-        mocked_signal = MagicMock()
-        mocked_signal.disconnect.side_effect = \
-            TypeError('disconnect() failed between \'durationChanged\' and all its connections')
-
-        # WHEN: disconnect_slots() is called
-        player.disconnect_slots(mocked_signal)
-
-        # THEN: disconnect should have been called and the exception should have been ignored
-        mocked_signal.disconnect.assert_called_once_with()
-
-    def test_check_available(self):
-        """
-        Test the check_available() method on SystemPlayer
-        """
-        # GIVEN: A SystemPlayer instance
-        player = SystemPlayer(self)
-
-        # WHEN: check_available is run
-        result = player.check_available()
-
-        # THEN: it should be available
-        assert result is True
-
-    def test_load_valid_media(self):
-        """
-        Test the load() method of SystemPlayer with a valid media file
-        """
-        # GIVEN: A SystemPlayer instance and a mocked display
-        player = SystemPlayer(self)
-        mocked_display = MagicMock()
-        mocked_display.controller.media_info.volume = 1
-        mocked_display.controller.media_info.file_info.absoluteFilePath.return_value = '/path/to/file'
-
-        # WHEN: The load() method is run
-        with patch.object(player, 'check_media') as mocked_check_media, \
-                patch.object(player, 'volume') as mocked_volume:
-            mocked_check_media.return_value = True
-            result = player.load(mocked_display)
-
-        # THEN: the file is sent to the video widget
-        mocked_display.controller.media_info.file_info.absoluteFilePath.assert_called_once_with()
-        mocked_check_media.assert_called_once_with('/path/to/file')
-        mocked_display.media_player.setMedia.assert_called_once_with(
-            QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile('/path/to/file')))
-        mocked_volume.assert_called_once_with(mocked_display, 1)
-        assert result is True
-
-    def test_load_invalid_media(self):
-        """
-        Test the load() method of SystemPlayer with an invalid media file
-        """
-        # GIVEN: A SystemPlayer instance and a mocked display
-        player = SystemPlayer(self)
-        mocked_display = MagicMock()
-        mocked_display.controller.media_info.volume = 1
-        mocked_display.controller.media_info.file_info.absoluteFilePath.return_value = '/path/to/file'
-
-        # WHEN: The load() method is run
-        with patch.object(player, 'check_media') as mocked_check_media, \
-                patch.object(player, 'volume'):
-            mocked_check_media.return_value = False
-            result = player.load(mocked_display)
-
-        # THEN: stuff
-        mocked_display.controller.media_info.file_info.absoluteFilePath.assert_called_once_with()
-        mocked_check_media.assert_called_once_with('/path/to/file')
-        assert result is False
-
-    def test_resize(self):
-        """
-        Test the resize() method of the SystemPlayer
-        """
-        # GIVEN: A SystemPlayer instance and a mocked display
-        player = SystemPlayer(self)
-        mocked_display = MagicMock()
-        mocked_display.size.return_value = [1, 2, 3, 4]
-
-        # WHEN: The resize() method is called
-        player.resize(mocked_display)
-
-        # THEN: The player is resized
-        mocked_display.size.assert_called_once_with()
-        mocked_display.video_widget.resize.assert_called_once_with([1, 2, 3, 4])
-
-    @patch('openlp.core.ui.media.systemplayer.functools')
-    def test_play_is_live(self, mocked_functools):
-        """
-        Test the play() method of the SystemPlayer on the live display
-        """
-        # GIVEN: A SystemPlayer instance and a mocked display
-        mocked_functools.partial.return_value = 'function'
-        player = SystemPlayer(self)
-        mocked_display = MagicMock()
-        mocked_display.controller.is_live = True
-        mocked_display.controller.media_info.start_time = 1
-        mocked_display.controller.media_info.volume = 1
-
-        # WHEN: play() is called
-        with patch.object(player, 'get_live_state') as mocked_get_live_state, \
-                patch.object(player, 'seek') as mocked_seek, \
-                patch.object(player, 'volume') as mocked_volume, \
-                patch.object(player, 'set_state') as mocked_set_state, \
-                patch.object(player, 'disconnect_slots') as mocked_disconnect_slots:
-            mocked_get_live_state.return_value = QtMultimedia.QMediaPlayer.PlayingState
-            result = player.play(mocked_display)
-
-        # THEN: the media file is played
-        mocked_get_live_state.assert_called_once_with()
-        mocked_display.media_player.play.assert_called_once_with()
-        mocked_seek.assert_called_once_with(mocked_display, 1000)
-        mocked_volume.assert_called_once_with(mocked_display, 1)
-        mocked_disconnect_slots.assert_called_once_with(mocked_display.media_player.durationChanged)
-        mocked_display.media_player.durationChanged.connect.assert_called_once_with('function')
-        mocked_set_state.assert_called_once_with(MediaState.Playing, mocked_display)
-        mocked_display.video_widget.raise_.assert_called_once_with()
-        assert result is True
-
-    @patch('openlp.core.ui.media.systemplayer.functools')
-    def test_play_is_preview(self, mocked_functools):
-        """
-        Test the play() method of the SystemPlayer on the preview display
-        """
-        # GIVEN: A SystemPlayer instance and a mocked display
-        mocked_functools.partial.return_value = 'function'
-        player = SystemPlayer(self)
-        mocked_display = MagicMock()
-        mocked_display.controller.is_live = False
-        mocked_display.controller.media_info.start_time = 1
-        mocked_display.controller.media_info.volume = 1
-
-        # WHEN: play() is called
-        with patch.object(player, 'get_preview_state') as mocked_get_preview_state, \
-                patch.object(player, 'seek') as mocked_seek, \
-                patch.object(player, 'volume') as mocked_volume, \
-                patch.object(player, 'set_state') as mocked_set_state:
-            mocked_get_preview_state.return_value = QtMultimedia.QMediaPlayer.PlayingState
-            result = player.play(mocked_display)
-
-        # THEN: the media file is played
-        mocked_get_preview_state.assert_called_once_with()
-        mocked_display.media_player.play.assert_called_once_with()
-        mocked_seek.assert_called_once_with(mocked_display, 1000)
-        mocked_volume.assert_called_once_with(mocked_display, 1)
-        mocked_display.media_player.durationChanged.connect.assert_called_once_with('function')
-        mocked_set_state.assert_called_once_with(MediaState.Playing, mocked_display)
-        mocked_display.video_widget.raise_.assert_called_once_with()
-        assert result is True
-
-    def test_pause_is_live(self):
-        """
-        Test the pause() method of the SystemPlayer on the live display
-        """
-        # GIVEN: A SystemPlayer instance
-        player = SystemPlayer(self)
-        mocked_display = MagicMock()
-        mocked_display.controller.is_live = True
-
-        # WHEN: The pause method is called
-        with patch.object(player, 'get_live_state') as mocked_get_live_state, \
-                patch.object(player, 'set_state') as mocked_set_state:
-            mocked_get_live_state.return_value = QtMultimedia.QMediaPlayer.PausedState
-            player.pause(mocked_display)
-
-        # THEN: The video is paused
-        mocked_display.media_player.pause.assert_called_once_with()
-        mocked_get_live_state.assert_called_once_with()
-        mocked_set_state.assert_called_once_with(MediaState.Paused, mocked_display)
-
-    def test_pause_is_preview(self):
-        """
-        Test the pause() method of the SystemPlayer on the preview display
-        """
-        # GIVEN: A SystemPlayer instance
-        player = SystemPlayer(self)
-        mocked_display = MagicMock()
-        mocked_display.controller.is_live = False
-
-        # WHEN: The pause method is called
-        with patch.object(player, 'get_preview_state') as mocked_get_preview_state, \
-                patch.object(player, 'set_state') as mocked_set_state:
-            mocked_get_preview_state.return_value = QtMultimedia.QMediaPlayer.PausedState
-            player.pause(mocked_display)
-
-        # THEN: The video is paused
-        mocked_display.media_player.pause.assert_called_once_with()
-        mocked_get_preview_state.assert_called_once_with()
-        mocked_set_state.assert_called_once_with(MediaState.Paused, mocked_display)
-
-    def test_stop(self):
-        """
-        Test the stop() method of the SystemPlayer
-        """
-        # GIVEN: A SystemPlayer instance
-        player = SystemPlayer(self)
-        mocked_display = MagicMock()
-
-        # WHEN: The stop method is called
-        with patch.object(player, 'set_visible') as mocked_set_visible, \
-                patch.object(player, 'set_state') as mocked_set_state:
-            player.stop(mocked_display)
-
-        # THEN: The video is stopped
-        mocked_display.media_player.stop.assert_called_once_with()
-        mocked_set_visible.assert_called_once_with(mocked_display, False)
-        mocked_set_state.assert_called_once_with(MediaState.Stopped, mocked_display)
-
-    def test_volume(self):
-        """
-        Test the volume() method of the SystemPlayer
-        """
-        # GIVEN: A SystemPlayer instance
-        player = SystemPlayer(self)
-        mocked_display = MagicMock()
-        mocked_display.has_audio = True
-
-        # WHEN: The stop method is called
-        player.volume(mocked_display, 2)
-
-        # THEN: The video is stopped
-        mocked_display.media_player.setVolume.assert_called_once_with(2)
-
-    def test_seek(self):
-        """
-        Test the seek() method of the SystemPlayer
-        """
-        # GIVEN: A SystemPlayer instance
-        player = SystemPlayer(self)
-        mocked_display = MagicMock()
-
-        # WHEN: The stop method is called
-        player.seek(mocked_display, 2)
-
-        # THEN: The video is stopped
-        mocked_display.media_player.setPosition.assert_called_once_with(2)
-
-    def test_reset(self):
-        """
-        Test the reset() method of the SystemPlayer
-        """
-        # GIVEN: A SystemPlayer instance
-        player = SystemPlayer(self)
-        mocked_display = MagicMock()
-
-        # WHEN: reset() is called
-        with patch.object(player, 'set_state') as mocked_set_state, \
-                patch.object(player, 'set_visible') as mocked_set_visible:
-            player.reset(mocked_display)
-
-        # THEN: The media player is reset
-        mocked_display.media_player.stop()
-        mocked_display.media_player.setMedia.assert_called_once_with(QtMultimedia.QMediaContent())
-        mocked_set_visible.assert_called_once_with(mocked_display, False)
-        mocked_display.video_widget.setVisible.assert_called_once_with(False)
-        mocked_set_state.assert_called_once_with(MediaState.Off, mocked_display)
-
-    def test_set_visible(self):
-        """
-        Test the set_visible() method on the SystemPlayer
-        """
-        # GIVEN: A SystemPlayer instance and a mocked display
-        player = SystemPlayer(self)
-        player.has_own_widget = True
-        mocked_display = MagicMock()
-
-        # WHEN: set_visible() is called
-        player.set_visible(mocked_display, True)
-
-        # THEN: The widget should be visible
-        mocked_display.video_widget.setVisible.assert_called_once_with(True)
-
-    def test_set_duration(self):
-        """
-        Test the set_duration() method of the SystemPlayer
-        """
-        # GIVEN: a mocked controller
-        mocked_controller = MagicMock()
-        mocked_controller.media_info.length = 5
-
-        # WHEN: The set_duration() is called. NB: the 10 here is ignored by the code
-        SystemPlayer.set_duration(mocked_controller, 10)
-
-        # THEN: The maximum length of the slider should be set
-        mocked_controller.seek_slider.setMaximum.assert_called_once_with(5)
-
-    def test_update_ui(self):
-        """
-        Test the update_ui() method on the SystemPlayer
-        """
-        # GIVEN: A SystemPlayer instance
-        player = SystemPlayer(self)
-        player.state = [MediaState.Playing, MediaState.Playing]
-        mocked_display = MagicMock()
-        mocked_display.media_player.state.return_value = QtMultimedia.QMediaPlayer.PausedState
-        mocked_display.controller.media_info.end_time = 1
-        mocked_display.media_player.position.return_value = 2
-        mocked_display.controller.seek_slider.isSliderDown.return_value = False
-
-        # WHEN: update_ui() is called
-        with patch.object(player, 'stop') as mocked_stop, \
-                patch.object(player, 'set_visible') as mocked_set_visible:
-            player.update_ui(mocked_display)
-
-        # THEN: The UI is updated
-        expected_stop_calls = [call(mocked_display)]
-        expected_position_calls = [call(), call()]
-        expected_block_signals_calls = [call(True), call(False)]
-        mocked_display.media_player.state.assert_called_once_with()
-        assert 1 == mocked_stop.call_count
-        assert expected_stop_calls == mocked_stop.call_args_list
-        assert 2 == mocked_display.media_player.position.call_count
-        assert expected_position_calls == mocked_display.media_player.position.call_args_list
-        mocked_set_visible.assert_called_once_with(mocked_display, False)
-        mocked_display.controller.seek_slider.isSliderDown.assert_called_once_with()
-        assert expected_block_signals_calls == mocked_display.controller.seek_slider.blockSignals.call_args_list
-        mocked_display.controller.seek_slider.setSliderPosition.assert_called_once_with(2)
-
-    def test_get_media_display_css(self):
-        """
-        Test the get_media_display_css() method of the SystemPlayer
-        """
-        # GIVEN: A SystemPlayer instance
-        player = SystemPlayer(self)
-
-        # WHEN: get_media_display_css() is called
-        result = player.get_media_display_css()
-
-        # THEN: The css should be empty
-        assert '' == result
-
-    @patch('openlp.core.ui.media.systemplayer.QtMultimedia.QMediaPlayer')
-    def test_get_info(self, MockQMediaPlayer):
-        """
-        Test the get_info() method of the SystemPlayer
-        """
-        # GIVEN: A SystemPlayer instance
-        mocked_media_player = MagicMock()
-        mocked_media_player.supportedMimeTypes.return_value = []
-        MockQMediaPlayer.return_value = mocked_media_player
-        player = SystemPlayer(self)
-
-        # WHEN: get_info() is called
-        result = player.get_info()
-
-        # THEN: The info should be correct
-        expected_info = 'This media player uses your operating system to provide media capabilities.<br/> ' \
-            '<strong>Audio</strong><br/>[]<br/><strong>Video</strong><br/>[]<br/>'
-        assert expected_info == result
-
-    @patch('openlp.core.ui.media.systemplayer.CheckMediaWorker')
-    @patch('openlp.core.ui.media.systemplayer.run_thread')
-    @patch('openlp.core.ui.media.systemplayer.is_thread_finished')
-    def test_check_media(self, mocked_is_thread_finished, mocked_run_thread, MockCheckMediaWorker):
-        """
-        Test the check_media() method of the SystemPlayer
-        """
-        # GIVEN: A SystemPlayer instance and a mocked thread
-        valid_file = '/path/to/video.ogv'
-        mocked_application = MagicMock()
-        Registry().create()
-        Registry().register('application', mocked_application)
-        player = SystemPlayer(self)
-        mocked_is_thread_finished.side_effect = [False, True]
-        mocked_check_media_worker = MagicMock()
-        mocked_check_media_worker.result = True
-        MockCheckMediaWorker.return_value = mocked_check_media_worker
-
-        # WHEN: check_media() is called with a valid media file
-        result = player.check_media(valid_file)
-
-        # THEN: It should return True
-        MockCheckMediaWorker.assert_called_once_with(valid_file)
-        mocked_check_media_worker.setVolume.assert_called_once_with(0)
-        mocked_run_thread.assert_called_once_with(mocked_check_media_worker, 'check_media')
-        mocked_is_thread_finished.assert_called_with('check_media')
-        assert mocked_is_thread_finished.call_count == 2, 'is_thread_finished() should have been called twice'
-        mocked_application.processEvents.assert_called_once_with()
-        assert result is True
-
-
-class TestCheckMediaWorker(TestCase):
-    """
-    Test the CheckMediaWorker class
-    """
-    def test_constructor(self):
-        """
-        Test the constructor of the CheckMediaWorker class
-        """
-        # GIVEN: A file path
-        path = 'file.ogv'
-
-        # WHEN: The CheckMediaWorker object is instantiated
-        worker = CheckMediaWorker(path)
-
-        # THEN: The correct values should be set up
-        assert worker is not None
-
-    @patch('openlp.core.ui.media.systemplayer.functools.partial')
-    @patch('openlp.core.ui.media.systemplayer.QtMultimedia.QMediaContent')
-    def test_start(self, MockQMediaContent, mocked_partial):
-        """
-        Test the start method
-        """
-        # GIVEN: A CheckMediaWorker instance
-        worker = CheckMediaWorker('file.ogv')
-        MockQMediaContent.side_effect = lambda x: x
-        mocked_partial.side_effect = lambda x, y: y
-
-        # WHEN: start() is called
-        with patch.object(worker, 'error') as mocked_error, \
-                patch.object(worker, 'mediaStatusChanged') as mocked_status_change, \
-                patch.object(worker, 'setMedia') as mocked_set_media, \
-                patch.object(worker, 'play') as mocked_play:
-            worker.start()
-
-        # THEN: The correct methods should be called
-        mocked_error.connect.assert_called_once_with('error')
-        mocked_status_change.connect.assert_called_once_with('media')
-        mocked_set_media.assert_called_once_with(QtCore.QUrl('file:file.ogv'))
-        mocked_play.assert_called_once_with()
-
-    def test_signals_media(self):
-        """
-        Test the signals() signal of the CheckMediaWorker class with a "media" origin
-        """
-        # GIVEN: A CheckMediaWorker instance
-        worker = CheckMediaWorker('file.ogv')
-
-        # WHEN: signals() is called with media and BufferedMedia
-        with patch.object(worker, 'stop') as mocked_stop, \
-                patch.object(worker, 'quit') as mocked_quit:
-            worker.signals('media', worker.BufferedMedia)
-
-        # THEN: The worker should exit and the result should be True
-        mocked_stop.assert_called_once_with()
-        mocked_quit.emit.assert_called_once_with()
-        assert worker.result is True
-
-    def test_signals_error(self):
-        """
-        Test the signals() signal of the CheckMediaWorker class with a "error" origin
-        """
-        # GIVEN: A CheckMediaWorker instance
-        worker = CheckMediaWorker('file.ogv')
-
-        # WHEN: signals() is called with error and BufferedMedia
-        with patch.object(worker, 'stop') as mocked_stop, \
-                patch.object(worker, 'quit') as mocked_quit:
-            worker.signals('error', None)
-
-        # THEN: The worker should exit and the result should be True
-        mocked_stop.assert_called_once_with()
-        mocked_quit.emit.assert_called_once_with()
-        assert worker.result is False

=== modified file 'tests/functional/openlp_core/ui/media/test_vlcplayer.py'
--- tests/functional/openlp_core/ui/media/test_vlcplayer.py	2019-02-14 15:09:09 +0000
+++ tests/functional/openlp_core/ui/media/test_vlcplayer.py	2019-04-10 19:57:31 +0000
@@ -138,25 +138,24 @@
         mocked_vlc = MagicMock()
         mocked_vlc.Instance.return_value = mocked_instance
         mocked_get_vlc.return_value = mocked_vlc
-        mocked_display = MagicMock()
-        mocked_display.has_audio = False
-        mocked_display.controller.is_live = True
-        mocked_display.size.return_value = (10, 10)
+        mocked_output_display = MagicMock()
+        mocked_controller = MagicMock()
+        mocked_controller.is_live = True
+        mocked_output_display.size.return_value = (10, 10)
         vlc_player = VlcPlayer(None)
 
         # WHEN: setup() is run
-        vlc_player.setup(mocked_display)
+        vlc_player.setup(mocked_output_display, mocked_controller)
 
         # THEN: The VLC widget should be set up correctly
-        assert mocked_display.vlc_widget == mocked_qframe
+        assert mocked_output_display.vlc_widget == mocked_qframe
         mocked_qframe.setFrameStyle.assert_called_with(1)
         mocked_settings.value.assert_called_with('advanced/hide mouse')
-        mocked_vlc.Instance.assert_called_with('--no-video-title-show --no-audio --no-video-title-show '
-                                               '--mouse-hide-timeout=0')
-        assert mocked_display.vlc_instance == mocked_instance
+        mocked_vlc.Instance.assert_called_with('--no-video-title-show --mouse-hide-timeout=0')
+        assert mocked_output_display.vlc_instance == mocked_instance
         mocked_instance.media_player_new.assert_called_with()
-        assert mocked_display.vlc_media_player == mocked_media_player_new
-        mocked_display.size.assert_called_with()
+        assert mocked_output_display.vlc_media_player == mocked_media_player_new
+        mocked_output_display.size.assert_called_with()
         mocked_qframe.resize.assert_called_with((10, 10))
         mocked_qframe.raise_.assert_called_with()
         mocked_qframe.hide.assert_called_with()
@@ -188,14 +187,14 @@
         mocked_vlc = MagicMock()
         mocked_vlc.Instance.return_value = mocked_instance
         mocked_get_vlc.return_value = mocked_vlc
-        mocked_display = MagicMock()
-        mocked_display.has_audio = True
-        mocked_display.controller.is_live = True
-        mocked_display.size.return_value = (10, 10)
+        mocked_output_display = MagicMock()
+        mocked_controller = MagicMock()
+        mocked_controller.is_live = True
+        mocked_output_display.size.return_value = (10, 10)
         vlc_player = VlcPlayer(None)
 
         # WHEN: setup() is run
-        vlc_player.setup(mocked_display)
+        vlc_player.setup(mocked_output_display, mocked_controller)
 
         # THEN: The VLC instance should be created with the correct options
         mocked_vlc.Instance.assert_called_with('--no-video-title-show --mouse-hide-timeout=0')
@@ -226,17 +225,17 @@
         mocked_vlc = MagicMock()
         mocked_vlc.Instance.return_value = mocked_instance
         mocked_get_vlc.return_value = mocked_vlc
-        mocked_display = MagicMock()
-        mocked_display.has_audio = False
-        mocked_display.controller.is_live = True
-        mocked_display.size.return_value = (10, 10)
+        mocked_output_display = MagicMock()
+        mocked_controller = MagicMock()
+        mocked_controller.is_live = True
+        mocked_output_display.size.return_value = (10, 10)
         vlc_player = VlcPlayer(None)
 
         # WHEN: setup() is run
-        vlc_player.setup(mocked_display)
+        vlc_player.setup(mocked_output_display, mocked_controller)
 
         # THEN: The VLC instance should be created with the correct options
-        mocked_vlc.Instance.assert_called_with('--no-video-title-show --no-audio --no-video-title-show')
+        mocked_vlc.Instance.assert_called_with('--no-video-title-show')
 
     @patch('openlp.core.ui.media.vlcplayer.is_win')
     @patch('openlp.core.ui.media.vlcplayer.is_macosx')
@@ -263,14 +262,14 @@
         mocked_vlc = MagicMock()
         mocked_vlc.Instance.return_value = mocked_instance
         mocked_get_vlc.return_value = mocked_vlc
-        mocked_display = MagicMock()
-        mocked_display.has_audio = False
-        mocked_display.controller.is_live = True
-        mocked_display.size.return_value = (10, 10)
+        mocked_output_display = MagicMock()
+        mocked_controller = MagicMock()
+        mocked_controller.is_live = True
+        mocked_output_display.size.return_value = (10, 10)
         vlc_player = VlcPlayer(None)
 
         # WHEN: setup() is run
-        vlc_player.setup(mocked_display)
+        vlc_player.setup(mocked_output_display, mocked_controller)
 
         # THEN: set_hwnd should be called
         mocked_media_player_new.set_hwnd.assert_called_with(2)
@@ -300,14 +299,14 @@
         mocked_vlc = MagicMock()
         mocked_vlc.Instance.return_value = mocked_instance
         mocked_get_vlc.return_value = mocked_vlc
-        mocked_display = MagicMock()
-        mocked_display.has_audio = False
-        mocked_display.controller.is_live = True
-        mocked_display.size.return_value = (10, 10)
+        mocked_output_display = MagicMock()
+        mocked_controller = MagicMock()
+        mocked_controller.is_live = True
+        mocked_output_display.size.return_value = (10, 10)
         vlc_player = VlcPlayer(None)
 
         # WHEN: setup() is run
-        vlc_player.setup(mocked_display)
+        vlc_player.setup(mocked_output_display, mocked_controller)
 
         # THEN: set_nsobject should be called
         mocked_media_player_new.set_nsobject.assert_called_with(2)
@@ -353,15 +352,13 @@
         mocked_normcase.side_effect = lambda x: x
         mocked_vlc = MagicMock()
         mocked_get_vlc.return_value = mocked_vlc
-        mocked_controller = MagicMock()
-        mocked_controller.media_info.volume = 100
-        mocked_controller.media_info.media_type = MediaType.Video
-        mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path
+        mocked_display = MagicMock()
+        mocked_display.media_info.volume = 100
+        mocked_display.media_info.media_type = MediaType.Video
+        mocked_display.media_info.file_info.absoluteFilePath.return_value = media_path
         mocked_vlc_media = MagicMock()
         mocked_media = MagicMock()
         mocked_media.get_duration.return_value = 10000
-        mocked_display = MagicMock()
-        mocked_display.controller = mocked_controller
         mocked_display.vlc_instance.media_new_path.return_value = mocked_vlc_media
         mocked_display.vlc_media_player.get_media.return_value = mocked_media
         vlc_player = VlcPlayer(None)
@@ -392,16 +389,13 @@
         mocked_normcase.side_effect = lambda x: x
         mocked_vlc = MagicMock()
         mocked_get_vlc.return_value = mocked_vlc
-        mocked_controller = MagicMock()
-        mocked_controller.media_info.volume = 100
-        mocked_controller.media_info.media_type = MediaType.CD
-        mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path
-        mocked_controller.media_info.title_track = 1
+        mocked_display = MagicMock()
+        mocked_display.media_info.volume = 100
+        mocked_display.media_info.media_type = MediaType.CD
+        mocked_display.media_info.title_track = 1
         mocked_vlc_media = MagicMock()
         mocked_media = MagicMock()
         mocked_media.get_duration.return_value = 10000
-        mocked_display = MagicMock()
-        mocked_display.controller = mocked_controller
         mocked_display.vlc_instance.media_new_location.return_value = mocked_vlc_media
         mocked_display.vlc_media_player.get_media.return_value = mocked_media
         mocked_subitems = MagicMock()
@@ -437,16 +431,14 @@
         mocked_normcase.side_effect = lambda x: x
         mocked_vlc = MagicMock()
         mocked_get_vlc.return_value = mocked_vlc
-        mocked_controller = MagicMock()
-        mocked_controller.media_info.volume = 100
-        mocked_controller.media_info.media_type = MediaType.CD
-        mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path
-        mocked_controller.media_info.title_track = 1
+        mocked_display = MagicMock()
+        mocked_display.media_info.volume = 100
+        mocked_display.media_info.media_type = MediaType.CD
+        mocked_display.media_info.file_info.absoluteFilePath.return_value = media_path
+        mocked_display.media_info.title_track = 1
         mocked_vlc_media = MagicMock()
         mocked_media = MagicMock()
         mocked_media.get_duration.return_value = 10000
-        mocked_display = MagicMock()
-        mocked_display.controller = mocked_controller
         mocked_display.vlc_instance.media_new_location.return_value = mocked_vlc_media
         mocked_display.vlc_media_player.get_media.return_value = mocked_media
         mocked_subitems = MagicMock()
@@ -482,16 +474,14 @@
         mocked_normcase.side_effect = lambda x: x
         mocked_vlc = MagicMock()
         mocked_get_vlc.return_value = mocked_vlc
-        mocked_controller = MagicMock()
-        mocked_controller.media_info.volume = 100
-        mocked_controller.media_info.media_type = MediaType.CD
-        mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path
-        mocked_controller.media_info.title_track = 1
+        mocked_display = MagicMock()
+        mocked_display.media_info.volume = 100
+        mocked_display.media_info.media_type = MediaType.CD
+        mocked_display.media_info.file_info.absoluteFilePath.return_value = media_path
+        mocked_display.media_info.title_track = 1
         mocked_vlc_media = MagicMock()
         mocked_media = MagicMock()
         mocked_media.get_duration.return_value = 10000
-        mocked_display = MagicMock()
-        mocked_display.controller = mocked_controller
         mocked_display.vlc_instance.media_new_location.return_value = mocked_vlc_media
         mocked_display.vlc_media_player.get_media.return_value = mocked_media
         mocked_subitems = MagicMock()
@@ -611,29 +601,28 @@
         mocked_threading.Thread.return_value = mocked_thread
         mocked_vlc = MagicMock()
         mocked_get_vlc.return_value = mocked_vlc
-        mocked_controller = MagicMock()
-        mocked_controller.media_info.start_time = 0
-        mocked_controller.media_info.media_type = MediaType.Video
-        mocked_controller.media_info.volume = 100
+        mocked_display = MagicMock()
         mocked_media = MagicMock()
         mocked_media.get_duration.return_value = 50000
-        mocked_display = MagicMock()
-        mocked_display.controller = mocked_controller
-        mocked_display.vlc_media_player.get_media.return_value = mocked_media
+        mocked_output_display = MagicMock()
+        mocked_output_display.media_info.start_time = 0
+        mocked_output_display.media_info.media_type = MediaType.Video
+        mocked_output_display.media_info.volume = 100
+        mocked_output_display.vlc_media_player.get_media.return_value = mocked_media
         vlc_player = VlcPlayer(None)
-        vlc_player.set_state(MediaState.Paused, mocked_display)
+        vlc_player.set_state(MediaState.Paused, mocked_output_display)
 
         # WHEN: play() is called
         with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait, \
                 patch.object(vlc_player, 'volume') as mocked_volume:
             mocked_media_state_wait.return_value = True
-            result = vlc_player.play(mocked_display)
+            result = vlc_player.play(mocked_display, mocked_output_display)
 
         # THEN: A bunch of things should happen to play the media
         mocked_thread.start.assert_called_with()
-        mocked_volume.assert_called_with(mocked_display, 100)
+        mocked_volume.assert_called_with(mocked_output_display, 100)
         assert MediaState.Playing == vlc_player.get_live_state()
-        mocked_display.vlc_widget.raise_.assert_called_with()
+        mocked_output_display.vlc_widget.raise_.assert_called_with()
         assert result is True, 'The value returned from play() should be True'
 
     @patch('openlp.core.ui.media.vlcplayer.threading')
@@ -649,16 +638,15 @@
         mocked_get_vlc.return_value = mocked_vlc
         mocked_controller = MagicMock()
         mocked_controller.media_info.start_time = 0
-        mocked_display = MagicMock()
-        mocked_display.controller = mocked_controller
+        mocked_output_display = MagicMock()
         vlc_player = VlcPlayer(None)
-        vlc_player.set_state(MediaState.Paused, mocked_display)
+        vlc_player.set_state(MediaState.Paused, mocked_output_display)
 
         # WHEN: play() is called
         with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait, \
                 patch.object(vlc_player, 'volume'):
             mocked_media_state_wait.return_value = False
-            result = vlc_player.play(mocked_display)
+            result = vlc_player.play(mocked_controller, mocked_output_display)
 
         # THEN: A thread should be started, but the method should return False
         mocked_thread.start.assert_called_with()
@@ -676,33 +664,32 @@
         mocked_vlc = MagicMock()
         mocked_get_vlc.return_value = mocked_vlc
         mocked_controller = MagicMock()
-        mocked_controller.media_info.start_time = 0
-        mocked_controller.media_info.end_time = 50
-        mocked_controller.media_info.media_type = MediaType.DVD
-        mocked_controller.media_info.volume = 100
-        mocked_controller.media_info.title_track = 1
-        mocked_controller.media_info.audio_track = 1
-        mocked_controller.media_info.subtitle_track = 1
-        mocked_display = MagicMock()
-        mocked_display.controller = mocked_controller
+        mocked_output_display = MagicMock()
+        mocked_output_display.media_info.start_time = 0
+        mocked_output_display.media_info.end_time = 50
+        mocked_output_display.media_info.media_type = MediaType.DVD
+        mocked_output_display.media_info.volume = 100
+        mocked_output_display.media_info.title_track = 1
+        mocked_output_display.media_info.audio_track = 1
+        mocked_output_display.media_info.subtitle_track = 1
         vlc_player = VlcPlayer(None)
-        vlc_player.set_state(MediaState.Paused, mocked_display)
+        vlc_player.set_state(MediaState.Paused, mocked_output_display)
 
         # WHEN: play() is called
         with patch.object(vlc_player, 'media_state_wait', return_value=True), \
                 patch.object(vlc_player, 'volume') as mocked_volume, \
                 patch.object(vlc_player, 'get_live_state', return_value=MediaState.Loaded):
-            result = vlc_player.play(mocked_display)
+            result = vlc_player.play(mocked_controller, mocked_output_display)
 
         # THEN: A bunch of things should happen to play the media
         mocked_thread.start.assert_called_with()
-        mocked_display.vlc_media_player.set_title.assert_called_with(1)
-        mocked_display.vlc_media_player.play.assert_called_with()
-        mocked_display.vlc_media_player.audio_set_track.assert_called_with(1)
-        mocked_display.vlc_media_player.video_set_spu.assert_called_with(1)
-        mocked_volume.assert_called_with(mocked_display, 100)
+        mocked_output_display.vlc_media_player.set_title.assert_called_with(1)
+        mocked_output_display.vlc_media_player.play.assert_called_with()
+        mocked_output_display.vlc_media_player.audio_set_track.assert_called_with(1)
+        mocked_output_display.vlc_media_player.video_set_spu.assert_called_with(1)
+        mocked_volume.assert_called_with(mocked_output_display, 100)
         assert MediaState.Playing == vlc_player.get_live_state()
-        mocked_display.vlc_widget.raise_.assert_called_with()
+        mocked_output_display.vlc_widget.raise_.assert_called_with()
         assert result is True, 'The value returned from play() should be True'
 
     @patch('openlp.core.ui.media.vlcplayer.get_vlc')
@@ -937,7 +924,6 @@
         mocked_controller.media_info.end_time = 300
         mocked_controller.seek_slider.isSliderDown.return_value = False
         mocked_display = MagicMock()
-        mocked_display.controller = mocked_controller
         mocked_display.vlc_media.get_state.return_value = 1
         mocked_display.vlc_media_player.get_time.return_value = 400000
         vlc_player = VlcPlayer(None)
@@ -945,7 +931,7 @@
         # WHEN: update_ui() is called
         with patch.object(vlc_player, 'stop') as mocked_stop, \
                 patch.object(vlc_player, 'set_visible') as mocked_set_visible:
-            vlc_player.update_ui(mocked_display)
+            vlc_player.update_ui(mocked_controller, mocked_display)
 
         # THEN: Certain methods should be called
         mocked_stop.assert_called_with(mocked_display)
@@ -970,22 +956,19 @@
         mocked_controller.media_info.end_time = 300
         mocked_controller.seek_slider.isSliderDown.return_value = False
         mocked_display = MagicMock()
-        mocked_display.controller = mocked_controller
         mocked_display.vlc_media.get_state.return_value = 1
-        mocked_display.vlc_media_player.get_time.return_value = 400
+        mocked_display.vlc_media_player.get_time.return_value = 300
         mocked_display.controller.media_info.media_type = MediaType.DVD
         vlc_player = VlcPlayer(None)
 
         # WHEN: update_ui() is called
-        with patch.object(vlc_player, 'stop') as mocked_stop, \
-                patch.object(vlc_player, 'set_visible') as mocked_set_visible:
-            vlc_player.update_ui(mocked_display)
+        with patch.object(vlc_player, 'stop') as mocked_stop:
+            vlc_player.update_ui(mocked_controller, mocked_display)
 
         # THEN: Certain methods should be called
         mocked_stop.assert_called_with(mocked_display)
-        assert 2 == mocked_stop.call_count
+        assert 1 == mocked_stop.call_count
         mocked_display.vlc_media_player.get_time.assert_called_with()
-        mocked_set_visible.assert_called_with(mocked_display, False)
         mocked_controller.seek_slider.setSliderPosition.assert_called_with(300)
         expected_calls = [call(True), call(False)]
         assert expected_calls == mocked_controller.seek_slider.blockSignals.call_args_list


Follow ups