diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py
index 8410d4d28..e45aa6e61 100644
--- a/openlp/core/lib/serviceitem.py
+++ b/openlp/core/lib/serviceitem.py
@@ -610,7 +610,7 @@ class ServiceItem(RegistryProperties):
str(datetime.timedelta(seconds=self.start_time))
if self.media_length != 0:
end = translate('OpenLP.ServiceItem', 'Length: %s') % \
- str(datetime.timedelta(seconds=self.media_length))
+ str(datetime.timedelta(seconds=self.media_length // 1000))
if not start and not end:
return ''
elif start and not end:
diff --git a/openlp/core/ui/__init__.py b/openlp/core/ui/__init__.py
index 82426539b..c6777c756 100644
--- a/openlp/core/ui/__init__.py
+++ b/openlp/core/ui/__init__.py
@@ -68,7 +68,6 @@ class DisplayControllerType(object):
"""
Live = 0
Preview = 1
- Plugin = 2
class SingleColumnTableWidget(QtWidgets.QTableWidget):
diff --git a/openlp/core/ui/media/__init__.py b/openlp/core/ui/media/__init__.py
index ecd4b98bd..07e2a73fb 100644
--- a/openlp/core/ui/media/__init__.py
+++ b/openlp/core/ui/media/__init__.py
@@ -60,12 +60,14 @@ class MediaInfo(object):
"""
file_info = None
volume = 100
- is_flash = False
is_background = False
+ can_loop_playback = False
length = 0
start_time = 0
end_time = 0
title_track = 0
+ is_playing = False
+ timer = 1000
audio_track = 0
subtitle_track = 0
media_type = MediaType()
@@ -104,15 +106,15 @@ def set_media_players(players_list, overridden_player='auto'):
Settings().setValue('media/players', players)
-def parse_optical_path(input):
+def parse_optical_path(input_string):
"""
Split the optical path info.
- :param input: The string to parse
+ :param input_string: The string to parse
:return: The elements extracted from the string: filename, title, audio_track, subtitle_track, start, end
"""
- log.debug('parse_optical_path, about to parse: "%s"' % input)
- clip_info = input.split(sep=':')
+ log.debug('parse_optical_path, about to parse: "%s"' % input_string)
+ clip_info = input_string.split(sep=':')
title = int(clip_info[1])
audio_track = int(clip_info[2])
subtitle_track = int(clip_info[3])
diff --git a/openlp/core/ui/media/mediacontroller.py b/openlp/core/ui/media/mediacontroller.py
index 343ce0dd4..cf116e861 100644
--- a/openlp/core/ui/media/mediacontroller.py
+++ b/openlp/core/ui/media/mediacontroller.py
@@ -33,12 +33,15 @@ from openlp.core.lib import OpenLPToolbar, ItemCapabilities
from openlp.core.lib.ui import critical_error_message_box
from openlp.core.ui.media import MediaState, MediaInfo, MediaType, get_media_players, set_media_players,\
parse_optical_path
+from openlp.core.ui.media.vendor.mediainfoWrapper import MediaInfoWrapper
from openlp.core.ui.media.mediaplayer import MediaPlayer
from openlp.core.common import AppLocation
from openlp.core.ui import DisplayControllerType
log = logging.getLogger(__name__)
+TICK_TIME = 200
+
class MediaSlider(QtWidgets.QSlider):
"""
@@ -51,10 +54,13 @@ class MediaSlider(QtWidgets.QSlider):
super(MediaSlider, self).__init__(direction)
self.manager = manager
self.controller = controller
+ self.no_matching_player = translate('MediaPlugin.MediaItem', 'File %s not supported using player %s')
def mouseMoveEvent(self, event):
"""
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)))
@@ -63,12 +69,16 @@ class MediaSlider(QtWidgets.QSlider):
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)
@@ -96,13 +106,17 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
self.display_controllers = {}
self.current_media_players = {}
# Timer for video state
- self.timer = QtCore.QTimer()
- self.timer.setInterval(200)
+ self.live_timer = QtCore.QTimer()
+ self.live_timer.setInterval(TICK_TIME)
+ self.preview_timer = QtCore.QTimer()
+ self.preview_timer.setInterval(TICK_TIME)
# Signals
- self.timer.timeout.connect(self.media_state)
+ self.live_timer.timeout.connect(self.media_state_live)
+ self.preview_timer.timeout.connect(self.media_state_preview)
Registry().register_function('playbackPlay', self.media_play_msg)
Registry().register_function('playbackPause', self.media_pause_msg)
Registry().register_function('playbackStop', self.media_stop_msg)
+ Registry().register_function('playbackLoop', self.media_loop_msg)
Registry().register_function('seek_slider', self.media_seek_msg)
Registry().register_function('volume_slider', self.media_volume_msg)
Registry().register_function('media_hide', self.media_hide)
@@ -172,8 +186,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
log.warning('Failed to import %s on path %s', module_name, path)
player_classes = MediaPlayer.__subclasses__()
for player_class in player_classes:
- player = player_class(self)
- self.register_players(player)
+ self.register_players(player_class(self))
if not self.media_players:
return False
saved_players, overridden_player = get_media_players()
@@ -188,31 +201,39 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
self._generate_extensions_lists()
return True
- def media_state(self):
+ def media_state_live(self):
"""
- Check if there is a running media Player and do updating stuff (e.g. update the UI)
+ Check if there is a running Live media Player and do updating stuff (e.g. update the UI)
"""
- if not list(self.current_media_players.keys()):
- self.timer.stop()
+ 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])
+ if self.current_media_players[DisplayControllerType.Live].get_live_state() is not MediaState.Playing:
+ self.live_timer.stop()
else:
- any_active = False
- for source in list(self.current_media_players.keys()):
- display = self._define_display(self.display_controllers[source])
- self.current_media_players[source].resize(display)
- self.current_media_players[source].update_ui(display)
- if self.current_media_players[source].state == MediaState.Playing:
- any_active = True
- # There are still any active players - no need to stop timer.
- if any_active:
- return
- # no players are active anymore
- for source in list(self.current_media_players.keys()):
- if self.current_media_players[source].state != MediaState.Paused:
- display = self._define_display(self.display_controllers[source])
- display.controller.seek_slider.setSliderPosition(0)
- display.controller.mediabar.actions['playbackPlay'].setVisible(True)
- display.controller.mediabar.actions['playbackPause'].setVisible(False)
- self.timer.stop()
+ 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)
+
+ 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])
+ 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])
+ 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 get_media_display_css(self):
"""
@@ -274,6 +295,15 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
icon=':/slides/media_playback_stop.png',
tooltip=translate('OpenLP.SlideController', 'Stop playing media.'),
triggers=controller.send_to_plugins)
+ controller.mediabar.add_toolbar_action('playbackLoop', text='media_playback_loop',
+ icon=':/slides/media_playback_stop.png', 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.setToolTip(translate('OpenLP.SlideController', 'Video timer.'))
+ 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)
@@ -297,6 +327,8 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
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)
@@ -335,7 +367,8 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
if self.current_media_players[controller.controller_type] != self.media_players['webkit']:
controller.display.set_transparency(False)
- def resize(self, display, player):
+ @staticmethod
+ def resize(display, player):
"""
After Mainwindow changes or Splitter moved all related media widgets have to be resized
@@ -353,7 +386,6 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
:param hidden: The player which is doing the playing
:param video_behind_text: Is the video to be played behind text.
"""
- log.debug('video')
is_valid = False
controller = self.display_controllers[source]
# stop running videos
@@ -361,6 +393,8 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
controller.media_info = MediaInfo()
controller.media_info.volume = controller.volume_slider.value()
controller.media_info.is_background = video_behind_text
+ # background will always loop video.
+ controller.media_info.can_loop_playback = video_behind_text
controller.media_info.file_info = QtCore.QFileInfo(service_item.get_frame_path())
display = self._define_display(controller)
if controller.is_live:
@@ -373,6 +407,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
controller)
else:
log.debug('video is not optical and live')
+ controller.media_info.length = service_item.media_length
is_valid = self._check_file_type(controller, display, service_item)
display.override['theme'] = ''
display.override['video'] = True
@@ -392,6 +427,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
controller)
else:
log.debug('video is not optical and preview')
+ controller.media_info.length = service_item.media_length
is_valid = self._check_file_type(controller, display, service_item)
if not is_valid:
# Media could not be loaded correctly
@@ -428,26 +464,22 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
:param service_item: The ServiceItem containing the details to be played.
"""
- controller = self.display_controllers[DisplayControllerType.Plugin]
- log.debug('media_length')
- # stop running videos
- self.media_reset(controller)
- controller.media_info = MediaInfo()
- controller.media_info.volume = 0
- controller.media_info.file_info = QtCore.QFileInfo(service_item.get_frame_path())
- display = controller.preview_display
- if not self._check_file_type(controller, display, service_item):
+ media_info = MediaInfo()
+ media_info.volume = 0
+ media_info.file_info = QtCore.QFileInfo(service_item.get_frame_path())
+ # display = controller.preview_display
+ suffix = '*.%s' % media_info.file_info.suffix().lower()
+ used_players = get_media_players()[0]
+ player = self.media_players[used_players[0]]
+ if suffix not in player.video_extensions_list and suffix not in player.audio_extensions_list:
# Media could not be loaded correctly
- critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported File'),
- translate('MediaPlugin.MediaItem', 'Unsupported File'))
+ critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported Media File'),
+ translate('MediaPlugin.MediaItem', 'File %s not supported using player %s') %
+ (service_item.get_frame_path(), used_players[0]))
return False
- if not self.media_play(controller):
- critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported File'),
- translate('MediaPlugin.MediaItem', 'Unsupported File'))
- return False
- service_item.set_media_length(controller.media_info.length)
- self.media_stop(controller)
- log.debug('use %s controller' % self.current_media_players[controller.controller_type])
+ media_data = MediaInfoWrapper.parse(service_item.get_frame_path())
+ # duration returns in milli seconds
+ service_item.set_media_length(media_data.tracks[0].duration)
return True
def media_setup_optical(self, filename, title, audio_track, subtitle_track, start, end, display, controller):
@@ -458,13 +490,12 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
:param title: The main/title track to play.
:param audio_track: The audio track to play.
:param subtitle_track: The subtitle track to play.
- :param start: Start position in miliseconds.
- :param end: End position in miliseconds.
+ :param start: Start position in milliseconds.
+ :param end: End position in milliseconds.
:param display: The display to play the media.
- :param controller: The media contraoller.
- :return: True if setup succeded else False.
+ :param controller: The media controller.
+ :return: True if setup succeeded else False.
"""
- log.debug('media_setup_optical')
if controller is None:
controller = self.display_controllers[DisplayControllerType.Plugin]
# stop running videos
@@ -476,9 +507,9 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
controller.media_info.media_type = MediaType.CD
else:
controller.media_info.media_type = MediaType.DVD
- controller.media_info.start_time = start / 1000
- controller.media_info.end_time = end / 1000
- controller.media_info.length = (end - start) / 1000
+ controller.media_info.start_time = start // 1000
+ controller.media_info.end_time = end // 1000
+ controller.media_info.length = (end - start) // 1000
controller.media_info.title_track = title
controller.media_info.audio_track = audio_track
controller.media_info.subtitle_track = subtitle_track
@@ -506,13 +537,13 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
controller.media_info.media_type = MediaType.DVD
return True
- def _check_file_type(self, controller, display, service_item):
+ @staticmethod
+ def _get_used_players(service_item):
"""
- Select the correct media Player type from the prioritized Player list
+ Find the player for a given service item
- :param controller: First element is the controller which should be used
- :param display: Which display to use
- :param service_item: The ServiceItem containing the details to be played.
+ :param service_item: where the information is about the media and required player
+ :return: player description
"""
used_players = get_media_players()[0]
# If no player, we can't play
@@ -525,6 +556,17 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
used_players = default_player
else:
used_players = [service_item.processor.lower()]
+ return used_players
+
+ def _check_file_type(self, controller, display, service_item):
+ """
+ Select the correct media Player type from the prioritized Player list
+
+ :param controller: First element is the controller which should be used
+ :param display: Which display to use
+ :param service_item: The ServiceItem containing the details to be played.
+ """
+ used_players = self._get_used_players(service_item)
if controller.media_info.file_info.isFile():
suffix = '*.%s' % controller.media_info.file_info.suffix().lower()
for title in used_players:
@@ -573,17 +615,15 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
:param msg: First element is the controller which should be used
:param status:
"""
- log.debug('media_play_msg')
self.media_play(msg[0], status)
- def media_play(self, controller, status=True):
+ def media_play(self, controller, first_time=True):
"""
Responds to the request to play a loaded video
:param controller: The controller to be played
- :param status:
+ :param first_time:
"""
- log.debug('media_play')
controller.seek_slider.blockSignals(True)
controller.volume_slider.blockSignals(True)
display = self._define_display(controller)
@@ -595,35 +635,60 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
self.media_volume(controller, 0)
else:
self.media_volume(controller, controller.media_info.volume)
- if status:
+ if first_time:
if not controller.media_info.is_background:
display.frame.evaluateJavaScript('show_blank("desktop");')
self.current_media_players[controller.controller_type].set_visible(display, True)
- # Flash needs to be played and will not AutoPlay
- if controller.media_info.is_flash:
- controller.mediabar.actions['playbackPlay'].setVisible(True)
- controller.mediabar.actions['playbackPause'].setVisible(False)
- else:
- controller.mediabar.actions['playbackPlay'].setVisible(False)
- controller.mediabar.actions['playbackPause'].setVisible(True)
+ controller.mediabar.actions['playbackPlay'].setVisible(False)
+ controller.mediabar.actions['playbackPause'].setVisible(True)
controller.mediabar.actions['playbackStop'].setDisabled(False)
- if controller.is_live:
- if controller.hide_menu.defaultAction().isChecked() and not controller.media_info.is_background:
- controller.hide_menu.defaultAction().trigger()
- # Start Timer for ui updates
- if not self.timer.isActive():
- self.timer.start()
+ if controller.is_live:
+ if controller.hide_menu.defaultAction().isChecked() and not controller.media_info.is_background:
+ controller.hide_menu.defaultAction().trigger()
+ # Start Timer for ui updates
+ if not self.live_timer.isActive():
+ self.live_timer.start()
+ else:
+ # Start Timer for ui updates
+ if not self.preview_timer.isActive():
+ self.preview_timer.start()
controller.seek_slider.blockSignals(False)
controller.volume_slider.blockSignals(False)
+ controller.media_info.is_playing = True
+ display = self._define_display(controller)
+ display.setVisible(True)
return True
+ def tick(self, controller):
+ """
+ Add a tick while the media is playing but only count if not paused
+
+ :param controller: The Controller to be processed
+ """
+ start_again = False
+ if controller.media_info.is_playing and controller.media_info.length > 0:
+ if controller.media_info.timer > controller.media_info.length:
+ self.media_stop(controller, True)
+ if controller.media_info.can_loop_playback:
+ start_again = True
+ controller.media_info.timer += TICK_TIME
+ seconds = controller.media_info.timer // 1000
+ minutes = seconds // 60
+ seconds %= 60
+ total_seconds = controller.media_info.length // 1000
+ total_minutes = total_seconds // 60
+ total_seconds %= 60
+ controller.position_label.setText(' %02d:%02d / %02d:%02d' %
+ (minutes, seconds, total_minutes, total_seconds))
+ if start_again:
+ self.media_play(controller, True)
+
def media_pause_msg(self, msg):
"""
Responds to the request to pause a loaded video
:param msg: First element is the controller which should be used
"""
- log.debug('media_pause_msg')
self.media_pause(msg[0])
def media_pause(self, controller):
@@ -632,12 +697,31 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
:param controller: The Controller to be paused
"""
- log.debug('media_pause')
display = self._define_display(controller)
- self.current_media_players[controller.controller_type].pause(display)
- controller.mediabar.actions['playbackPlay'].setVisible(True)
- controller.mediabar.actions['playbackStop'].setDisabled(False)
- controller.mediabar.actions['playbackPause'].setVisible(False)
+ if controller.controller_type in self.current_media_players:
+ self.current_media_players[controller.controller_type].pause(display)
+ controller.mediabar.actions['playbackPlay'].setVisible(True)
+ controller.mediabar.actions['playbackStop'].setDisabled(False)
+ controller.mediabar.actions['playbackPause'].setVisible(False)
+ controller.media_info.is_playing = False
+
+ def media_loop_msg(self, msg):
+ """
+ Responds to the request to loop a loaded video
+
+ :param msg: First element is the controller which should be used
+ """
+ self.media_loop(msg[0])
+
+ @staticmethod
+ def media_loop(controller):
+ """
+ Responds to the request to loop a loaded video
+
+ :param controller: The controller that needs to be stopped
+ """
+ controller.media_info.can_loop_playback = not controller.media_info.can_loop_playback
+ controller.mediabar.actions['playbackLoop'].setChecked(controller.media_info.can_loop_playback)
def media_stop_msg(self, msg):
"""
@@ -645,25 +729,28 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
:param msg: First element is the controller which should be used
"""
- log.debug('media_stop_msg')
self.media_stop(msg[0])
- def media_stop(self, controller):
+ def media_stop(self, controller, looping_background=False):
"""
Responds to the request to stop a loaded video
:param controller: The controller that needs to be stopped
+ :param looping_background: The background is looping so do not blank.
"""
- log.debug('media_stop')
display = self._define_display(controller)
if controller.controller_type in self.current_media_players:
- display.frame.evaluateJavaScript('show_blank("black");')
+ if not looping_background:
+ display.frame.evaluateJavaScript('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)
controller.mediabar.actions['playbackPlay'].setVisible(True)
controller.mediabar.actions['playbackStop'].setDisabled(True)
controller.mediabar.actions['playbackPause'].setVisible(False)
+ controller.media_info.is_playing = False
+ controller.media_info.timer = 1000
+ controller.media_timer = 0
def media_volume_msg(self, msg):
"""
@@ -694,7 +781,6 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
:param msg: First element is the controller which should be used
Second element is a list with the seek value as first element
"""
- log.debug('media_seek')
controller = msg[0]
seek_value = msg[1][0]
self.media_seek(controller, seek_value)
@@ -706,15 +792,15 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
:param controller: The controller to use.
:param seek_value: The value to set.
"""
- log.debug('media_seek')
display = self._define_display(controller)
self.current_media_players[controller.controller_type].seek(display, seek_value)
+ controller.media_info.timer = seek_value
def media_reset(self, controller):
"""
Responds to the request to reset a loaded video
+ :param controller: The controller to use.
"""
- log.debug('media_reset')
self.set_controls_visible(controller, False)
display = self._define_display(controller)
if controller.controller_type in self.current_media_players:
@@ -735,7 +821,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
return
display = self._define_display(self.live_controller)
if self.live_controller.controller_type in self.current_media_players and \
- self.current_media_players[self.live_controller.controller_type].state == MediaState.Playing:
+ self.current_media_players[self.live_controller.controller_type].get_live_state() == MediaState.Playing:
self.current_media_players[self.live_controller.controller_type].pause(display)
self.current_media_players[self.live_controller.controller_type].set_visible(display, False)
@@ -753,7 +839,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
Registry().execute('live_display_hide', hide_mode)
display = self._define_display(self.live_controller)
if self.live_controller.controller_type in self.current_media_players and \
- self.current_media_players[self.live_controller.controller_type].state == MediaState.Playing:
+ self.current_media_players[self.live_controller.controller_type].get_live_state() == MediaState.Playing:
self.current_media_players[self.live_controller.controller_type].pause(display)
self.current_media_players[self.live_controller.controller_type].set_visible(display, False)
@@ -770,22 +856,25 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
return
display = self._define_display(self.live_controller)
if self.live_controller.controller_type in self.current_media_players and \
- self.current_media_players[self.live_controller.controller_type].state != MediaState.Playing:
+ self.current_media_players[self.live_controller.controller_type].get_live_state() != \
+ MediaState.Playing:
if self.current_media_players[self.live_controller.controller_type].play(display):
self.current_media_players[self.live_controller.controller_type].set_visible(display, True)
# Start Timer for ui updates
- if not self.timer.isActive():
- self.timer.start()
+ if not self.live_timer.isActive():
+ self.live_timer.start()
def finalise(self):
"""
Reset all the media controllers when OpenLP shuts down
"""
- self.timer.stop()
+ self.live_timer.stop()
+ self.preview_timer.stop()
for controller in self.display_controllers:
self.media_reset(self.display_controllers[controller])
- def _define_display(self, controller):
+ @staticmethod
+ def _define_display(controller):
"""
Extract the correct display for a given controller
diff --git a/openlp/core/ui/media/mediaplayer.py b/openlp/core/ui/media/mediaplayer.py
index b25916372..d9c7ad321 100644
--- a/openlp/core/ui/media/mediaplayer.py
+++ b/openlp/core/ui/media/mediaplayer.py
@@ -41,7 +41,7 @@ class MediaPlayer(RegistryProperties):
self.is_active = False
self.can_background = False
self.can_folder = False
- self.state = MediaState.Off
+ self.state = {0: MediaState.Off, 1: MediaState.Off}
self.has_own_widget = False
self.audio_extensions_list = []
self.video_extensions_list = []
@@ -55,12 +55,16 @@ class MediaPlayer(RegistryProperties):
def setup(self, display):
"""
Create the related widgets for the current display
+
+ :param display: The display to be updated.
"""
pass
def load(self, display):
"""
Load a new media file and check if it is valid
+
+ :param display: The display to be updated.
"""
return True
@@ -68,54 +72,75 @@ class MediaPlayer(RegistryProperties):
"""
If the main display size or position is changed, the media widgets
should also resized
+
+ :param display: The display to be updated.
"""
pass
def play(self, display):
"""
Starts playing of current Media File
+
+ :param display: The display to be updated.
"""
pass
def pause(self, display):
"""
Pause of current Media File
+
+ :param display: The display to be updated.
"""
pass
def stop(self, display):
"""
Stop playing of current Media File
+
+ :param display: The display to be updated.
"""
pass
- def volume(self, display, vol):
+ def volume(self, display, volume):
"""
Change volume of current Media File
+
+ :param display: The display to be updated.
+ :param volume: The volume to set.
"""
pass
def seek(self, display, seek_value):
"""
Change playing position of current Media File
+
+ :param display: The display to be updated.
+ :param seek_value: The where to seek to.
"""
pass
def reset(self, display):
"""
Remove the current loaded video
+
+ :param display: The display to be updated.
"""
pass
def set_visible(self, display, status):
"""
Show/Hide the media widgets
+
+ :param display: The display to be updated.
+ :param status: The status to be set.
"""
pass
def update_ui(self, display):
"""
Do some ui related stuff (e.g. update the seek slider)
+
+ :param display: The display to be updated.
"""
pass
@@ -142,3 +167,45 @@ class MediaPlayer(RegistryProperties):
Returns Information about the player
"""
return ''
+
+ def get_live_state(self):
+ """
+ Get the state of the live player
+ :return: Live state
+ """
+ return self.state[0]
+
+ def set_live_state(self, state):
+ """
+ Set the State of the Live player
+ :param state: State to be set
+ :return: None
+ """
+ self.state[0] = state
+
+ def get_preview_state(self):
+ """
+ Get the state of the preview player
+ :return: Preview State
+ """
+ return self.state[1]
+
+ def set_preview_state(self, state):
+ """
+ Set the state of the Preview Player
+ :param state: State to be set
+ :return: None
+ """
+ self.state[1] = state
+
+ def set_state(self, state, display):
+ """
+ Set the State based on the display being processed
+ :param state: State to be set
+ :param display: Identify the Display type
+ :return: None
+ """
+ if display.controller.is_live:
+ self.set_live_state(state)
+ else:
+ self.set_preview_state(state)
diff --git a/openlp/core/ui/media/playertab.py b/openlp/core/ui/media/playertab.py
index 3db5e06b4..ed34993ca 100644
--- a/openlp/core/ui/media/playertab.py
+++ b/openlp/core/ui/media/playertab.py
@@ -133,12 +133,16 @@ class PlayerTab(SettingsTab):
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:
diff --git a/openlp/core/ui/media/systemplayer.py b/openlp/core/ui/media/systemplayer.py
index 79069f9c9..ad1907044 100644
--- a/openlp/core/ui/media/systemplayer.py
+++ b/openlp/core/ui/media/systemplayer.py
@@ -4,14 +4,7 @@
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
-# Copyright (c) 2008-2014 Raoul Snyman #
-# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
-# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
-# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
-# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
-# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
-# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
-# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
+# Copyright (c) 2008-2016 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 #
@@ -124,7 +117,8 @@ class SystemPlayer(MediaPlayer):
def load(self, display):
"""
Load a video into the display
- :param display:
+
+ :param display: The display where the media is
"""
log.debug('load vid in System Controller')
controller = display.controller
@@ -141,93 +135,122 @@ class SystemPlayer(MediaPlayer):
def resize(self, display):
"""
Resize the display
- :param 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:
+
+ :param display: The display where the media is
"""
log.info('Play the current item')
controller = display.controller
start_time = 0
- if display.media_player.state() != QtMultimedia.QMediaPlayer.PausedState and \
- controller.media_info.start_time > 0:
- start_time = controller.media_info.start_time
+ 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)
display.media_player.durationChanged.connect(functools.partial(self.set_duration, controller))
- self.state = MediaState.Playing
+ 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.media_player.state() == QtMultimedia.QMediaPlayer.PausedState:
- self.state = MediaState.Paused
+ 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.blockSignals(True)
- display.media_player.durationChanged.disconnect()
- display.media_player.blockSignals(False)
display.media_player.stop()
self.set_visible(display, False)
- self.state = MediaState.Stopped
+ self.set_state(MediaState.Stopped, display)
- def volume(self, display, vol):
+ 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(vol)
+ 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.state = MediaState.Off
+ 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):
- controller.media_info.length = int(duration / 1000)
- controller.seek_slider.setMaximum(controller.media_info.length * 1000)
+ """
+
+ :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.stop(display)
controller = display.controller
if controller.media_info.end_time > 0:
- if display.media_player.position() > controller.media_info.end_time * 1000:
+ if display.media_player.position() > controller.media_info.end_time:
self.stop(display)
self.set_visible(display, False)
if not controller.seek_slider.isSliderDown():
diff --git a/openlp/core/ui/media/vendor/mediainfoWrapper.py b/openlp/core/ui/media/vendor/mediainfoWrapper.py
new file mode 100644
index 000000000..35f16667d
--- /dev/null
+++ b/openlp/core/ui/media/vendor/mediainfoWrapper.py
@@ -0,0 +1,140 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2016 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.mediainfo` module contains code to run mediainfo on a media file and obtain
+information related to the rwquested media.
+"""
+import json
+import os
+from subprocess import Popen
+from tempfile import mkstemp
+
+import six
+from bs4 import BeautifulSoup, NavigableString
+
+ENV_DICT = os.environ
+
+
+class Track(object):
+
+ def __getattribute__(self, name):
+ try:
+ return object.__getattribute__(self, name)
+ except:
+ pass
+ return None
+
+ def __init__(self, xml_dom_fragment):
+ self.xml_dom_fragment = xml_dom_fragment
+ self.track_type = xml_dom_fragment.attrs['type']
+ for el in self.xml_dom_fragment.children:
+ if not isinstance(el, NavigableString):
+ node_name = el.name.lower().strip().strip('_')
+ if node_name == 'id':
+ node_name = 'track_id'
+ node_value = el.string
+ other_node_name = "other_%s" % node_name
+ if getattr(self, node_name) is None:
+ setattr(self, node_name, node_value)
+ else:
+ if getattr(self, other_node_name) is None:
+ setattr(self, other_node_name, [node_value, ])
+ else:
+ getattr(self, other_node_name).append(node_value)
+
+ for o in [d for d in self.__dict__.keys() if d.startswith('other_')]:
+ try:
+ primary = o.replace('other_', '')
+ setattr(self, primary, int(getattr(self, primary)))
+ except:
+ for v in getattr(self, o):
+ try:
+ current = getattr(self, primary)
+ setattr(self, primary, int(v))
+ getattr(self, o).append(current)
+ break
+ except:
+ pass
+
+ def __repr__(self):
+ return "