Merged from trunk

This commit is contained in:
Ian Knight 2016-04-16 16:32:36 +09:30
commit c8172d882f
27 changed files with 707 additions and 359 deletions

View File

@ -610,7 +610,7 @@ class ServiceItem(RegistryProperties):
str(datetime.timedelta(seconds=self.start_time)) str(datetime.timedelta(seconds=self.start_time))
if self.media_length != 0: if self.media_length != 0:
end = translate('OpenLP.ServiceItem', '<strong>Length</strong>: %s') % \ end = translate('OpenLP.ServiceItem', '<strong>Length</strong>: %s') % \
str(datetime.timedelta(seconds=self.media_length)) str(datetime.timedelta(seconds=self.media_length // 1000))
if not start and not end: if not start and not end:
return '' return ''
elif start and not end: elif start and not end:

View File

@ -68,7 +68,6 @@ class DisplayControllerType(object):
""" """
Live = 0 Live = 0
Preview = 1 Preview = 1
Plugin = 2
class SingleColumnTableWidget(QtWidgets.QTableWidget): class SingleColumnTableWidget(QtWidgets.QTableWidget):

View File

@ -60,12 +60,14 @@ class MediaInfo(object):
""" """
file_info = None file_info = None
volume = 100 volume = 100
is_flash = False
is_background = False is_background = False
can_loop_playback = False
length = 0 length = 0
start_time = 0 start_time = 0
end_time = 0 end_time = 0
title_track = 0 title_track = 0
is_playing = False
timer = 1000
audio_track = 0 audio_track = 0
subtitle_track = 0 subtitle_track = 0
media_type = MediaType() media_type = MediaType()
@ -104,15 +106,15 @@ def set_media_players(players_list, overridden_player='auto'):
Settings().setValue('media/players', players) Settings().setValue('media/players', players)
def parse_optical_path(input): def parse_optical_path(input_string):
""" """
Split the optical path info. 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 :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) log.debug('parse_optical_path, about to parse: "%s"' % input_string)
clip_info = input.split(sep=':') clip_info = input_string.split(sep=':')
title = int(clip_info[1]) title = int(clip_info[1])
audio_track = int(clip_info[2]) audio_track = int(clip_info[2])
subtitle_track = int(clip_info[3]) subtitle_track = int(clip_info[3])

View File

@ -33,12 +33,15 @@ from openlp.core.lib import OpenLPToolbar, ItemCapabilities
from openlp.core.lib.ui import critical_error_message_box from openlp.core.lib.ui import critical_error_message_box
from openlp.core.ui.media import MediaState, MediaInfo, MediaType, get_media_players, set_media_players,\ from openlp.core.ui.media import MediaState, MediaInfo, MediaType, get_media_players, set_media_players,\
parse_optical_path parse_optical_path
from openlp.core.ui.media.vendor.mediainfoWrapper import MediaInfoWrapper
from openlp.core.ui.media.mediaplayer import MediaPlayer from openlp.core.ui.media.mediaplayer import MediaPlayer
from openlp.core.common import AppLocation from openlp.core.common import AppLocation
from openlp.core.ui import DisplayControllerType from openlp.core.ui import DisplayControllerType
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
TICK_TIME = 200
class MediaSlider(QtWidgets.QSlider): class MediaSlider(QtWidgets.QSlider):
""" """
@ -51,10 +54,13 @@ class MediaSlider(QtWidgets.QSlider):
super(MediaSlider, self).__init__(direction) super(MediaSlider, self).__init__(direction)
self.manager = manager self.manager = manager
self.controller = controller self.controller = controller
self.no_matching_player = translate('MediaPlugin.MediaItem', 'File %s not supported using player %s')
def mouseMoveEvent(self, event): def mouseMoveEvent(self, event):
""" """
Override event to allow hover time to be displayed. 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()) time_value = QtWidgets.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), event.x(), self.width())
self.setToolTip('%s' % datetime.timedelta(seconds=int(time_value / 1000))) self.setToolTip('%s' % datetime.timedelta(seconds=int(time_value / 1000)))
@ -63,12 +69,16 @@ class MediaSlider(QtWidgets.QSlider):
def mousePressEvent(self, event): def mousePressEvent(self, event):
""" """
Mouse Press event no new functionality Mouse Press event no new functionality
:param event: The triggering event
""" """
QtWidgets.QSlider.mousePressEvent(self, event) QtWidgets.QSlider.mousePressEvent(self, event)
def mouseReleaseEvent(self, event): def mouseReleaseEvent(self, event):
""" """
Set the slider position when the mouse is clicked and released on the slider. 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())) self.setValue(QtWidgets.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), event.x(), self.width()))
QtWidgets.QSlider.mouseReleaseEvent(self, event) QtWidgets.QSlider.mouseReleaseEvent(self, event)
@ -96,13 +106,17 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
self.display_controllers = {} self.display_controllers = {}
self.current_media_players = {} self.current_media_players = {}
# Timer for video state # Timer for video state
self.timer = QtCore.QTimer() self.live_timer = QtCore.QTimer()
self.timer.setInterval(200) self.live_timer.setInterval(TICK_TIME)
self.preview_timer = QtCore.QTimer()
self.preview_timer.setInterval(TICK_TIME)
# Signals # 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('playbackPlay', self.media_play_msg)
Registry().register_function('playbackPause', self.media_pause_msg) Registry().register_function('playbackPause', self.media_pause_msg)
Registry().register_function('playbackStop', self.media_stop_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('seek_slider', self.media_seek_msg)
Registry().register_function('volume_slider', self.media_volume_msg) Registry().register_function('volume_slider', self.media_volume_msg)
Registry().register_function('media_hide', self.media_hide) 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) log.warning('Failed to import %s on path %s', module_name, path)
player_classes = MediaPlayer.__subclasses__() player_classes = MediaPlayer.__subclasses__()
for player_class in player_classes: for player_class in player_classes:
player = player_class(self) self.register_players(player_class(self))
self.register_players(player)
if not self.media_players: if not self.media_players:
return False return False
saved_players, overridden_player = get_media_players() saved_players, overridden_player = get_media_players()
@ -188,31 +201,39 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
self._generate_extensions_lists() self._generate_extensions_lists()
return True 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()): display = self._define_display(self.display_controllers[DisplayControllerType.Live])
self.timer.stop() 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: else:
any_active = False self.live_timer.stop()
for source in list(self.current_media_players.keys()): self.media_stop(self.display_controllers[DisplayControllerType.Live])
display = self._define_display(self.display_controllers[source]) if self.display_controllers[DisplayControllerType.Live].media_info.can_loop_playback:
self.current_media_players[source].resize(display) self.media_play(self.display_controllers[DisplayControllerType.Live], True)
self.current_media_players[source].update_ui(display)
if self.current_media_players[source].state == MediaState.Playing: def media_state_preview(self):
any_active = True """
# There are still any active players - no need to stop timer. Check if there is a running Preview media Player and do updating stuff (e.g. update the UI)
if any_active: """
return display = self._define_display(self.display_controllers[DisplayControllerType.Preview])
# no players are active anymore if DisplayControllerType.Preview in self.current_media_players:
for source in list(self.current_media_players.keys()): self.current_media_players[DisplayControllerType.Preview].resize(display)
if self.current_media_players[source].state != MediaState.Paused: self.current_media_players[DisplayControllerType.Preview].update_ui(display)
display = self._define_display(self.display_controllers[source]) self.tick(self.display_controllers[DisplayControllerType.Preview])
display.controller.seek_slider.setSliderPosition(0) if self.current_media_players[DisplayControllerType.Preview].get_preview_state() is not MediaState.Playing:
display.controller.mediabar.actions['playbackPlay'].setVisible(True) self.preview_timer.stop()
display.controller.mediabar.actions['playbackPause'].setVisible(False) else:
self.timer.stop() 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): def get_media_display_css(self):
""" """
@ -274,6 +295,15 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
icon=':/slides/media_playback_stop.png', icon=':/slides/media_playback_stop.png',
tooltip=translate('OpenLP.SlideController', 'Stop playing media.'), tooltip=translate('OpenLP.SlideController', 'Stop playing media.'),
triggers=controller.send_to_plugins) 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. # Build the seek_slider.
controller.seek_slider = MediaSlider(QtCore.Qt.Horizontal, self, controller) controller.seek_slider = MediaSlider(QtCore.Qt.Horizontal, self, controller)
controller.seek_slider.setMaximum(1000) controller.seek_slider.setMaximum(1000)
@ -297,6 +327,8 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
controller.mediabar.add_toolbar_widget(controller.volume_slider) controller.mediabar.add_toolbar_widget(controller.volume_slider)
controller.controller_layout.addWidget(controller.mediabar) controller.controller_layout.addWidget(controller.mediabar)
controller.mediabar.setVisible(False) controller.mediabar.setVisible(False)
if not controller.is_live:
controller.volume_slider.setEnabled(False)
# Signals # Signals
controller.seek_slider.valueChanged.connect(controller.send_to_plugins) controller.seek_slider.valueChanged.connect(controller.send_to_plugins)
controller.volume_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']: if self.current_media_players[controller.controller_type] != self.media_players['webkit']:
controller.display.set_transparency(False) 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 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 hidden: The player which is doing the playing
:param video_behind_text: Is the video to be played behind text. :param video_behind_text: Is the video to be played behind text.
""" """
log.debug('video')
is_valid = False is_valid = False
controller = self.display_controllers[source] controller = self.display_controllers[source]
# stop running videos # stop running videos
@ -361,6 +393,8 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
controller.media_info = MediaInfo() controller.media_info = MediaInfo()
controller.media_info.volume = controller.volume_slider.value() controller.media_info.volume = controller.volume_slider.value()
controller.media_info.is_background = video_behind_text 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()) controller.media_info.file_info = QtCore.QFileInfo(service_item.get_frame_path())
display = self._define_display(controller) display = self._define_display(controller)
if controller.is_live: if controller.is_live:
@ -373,6 +407,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
controller) controller)
else: else:
log.debug('video is not optical and live') 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) is_valid = self._check_file_type(controller, display, service_item)
display.override['theme'] = '' display.override['theme'] = ''
display.override['video'] = True display.override['video'] = True
@ -392,6 +427,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
controller) controller)
else: else:
log.debug('video is not optical and preview') 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) is_valid = self._check_file_type(controller, display, service_item)
if not is_valid: if not is_valid:
# Media could not be loaded correctly # 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. :param service_item: The ServiceItem containing the details to be played.
""" """
controller = self.display_controllers[DisplayControllerType.Plugin] media_info = MediaInfo()
log.debug('media_length') media_info.volume = 0
# stop running videos media_info.file_info = QtCore.QFileInfo(service_item.get_frame_path())
self.media_reset(controller) # display = controller.preview_display
controller.media_info = MediaInfo() suffix = '*.%s' % media_info.file_info.suffix().lower()
controller.media_info.volume = 0 used_players = get_media_players()[0]
controller.media_info.file_info = QtCore.QFileInfo(service_item.get_frame_path()) player = self.media_players[used_players[0]]
display = controller.preview_display if suffix not in player.video_extensions_list and suffix not in player.audio_extensions_list:
if not self._check_file_type(controller, display, service_item):
# Media could not be loaded correctly # Media could not be loaded correctly
critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported File'), critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported Media File'),
translate('MediaPlugin.MediaItem', 'Unsupported File')) translate('MediaPlugin.MediaItem', 'File %s not supported using player %s') %
(service_item.get_frame_path(), used_players[0]))
return False return False
if not self.media_play(controller): media_data = MediaInfoWrapper.parse(service_item.get_frame_path())
critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported File'), # duration returns in milli seconds
translate('MediaPlugin.MediaItem', 'Unsupported File')) service_item.set_media_length(media_data.tracks[0].duration)
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])
return True return True
def media_setup_optical(self, filename, title, audio_track, subtitle_track, start, end, display, controller): 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 title: The main/title track to play.
:param audio_track: The audio track to play. :param audio_track: The audio track to play.
:param subtitle_track: The subtitle track to play. :param subtitle_track: The subtitle track to play.
:param start: Start position in miliseconds. :param start: Start position in milliseconds.
:param end: End position in miliseconds. :param end: End position in milliseconds.
:param display: The display to play the media. :param display: The display to play the media.
:param controller: The media contraoller. :param controller: The media controller.
:return: True if setup succeded else False. :return: True if setup succeeded else False.
""" """
log.debug('media_setup_optical')
if controller is None: if controller is None:
controller = self.display_controllers[DisplayControllerType.Plugin] controller = self.display_controllers[DisplayControllerType.Plugin]
# stop running videos # stop running videos
@ -476,9 +507,9 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
controller.media_info.media_type = MediaType.CD controller.media_info.media_type = MediaType.CD
else: else:
controller.media_info.media_type = MediaType.DVD controller.media_info.media_type = MediaType.DVD
controller.media_info.start_time = start / 1000 controller.media_info.start_time = start // 1000
controller.media_info.end_time = end / 1000 controller.media_info.end_time = end // 1000
controller.media_info.length = (end - start) / 1000 controller.media_info.length = (end - start) // 1000
controller.media_info.title_track = title controller.media_info.title_track = title
controller.media_info.audio_track = audio_track controller.media_info.audio_track = audio_track
controller.media_info.subtitle_track = subtitle_track controller.media_info.subtitle_track = subtitle_track
@ -506,13 +537,13 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
controller.media_info.media_type = MediaType.DVD controller.media_info.media_type = MediaType.DVD
return True 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 service_item: where the information is about the media and required player
:param display: Which display to use :return: player description
:param service_item: The ServiceItem containing the details to be played.
""" """
used_players = get_media_players()[0] used_players = get_media_players()[0]
# If no player, we can't play # If no player, we can't play
@ -525,6 +556,17 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
used_players = default_player used_players = default_player
else: else:
used_players = [service_item.processor.lower()] 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(): if controller.media_info.file_info.isFile():
suffix = '*.%s' % controller.media_info.file_info.suffix().lower() suffix = '*.%s' % controller.media_info.file_info.suffix().lower()
for title in used_players: 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 msg: First element is the controller which should be used
:param status: :param status:
""" """
log.debug('media_play_msg')
self.media_play(msg[0], status) 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 Responds to the request to play a loaded video
:param controller: The controller to be played :param controller: The controller to be played
:param status: :param first_time:
""" """
log.debug('media_play')
controller.seek_slider.blockSignals(True) controller.seek_slider.blockSignals(True)
controller.volume_slider.blockSignals(True) controller.volume_slider.blockSignals(True)
display = self._define_display(controller) display = self._define_display(controller)
@ -595,35 +635,60 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
self.media_volume(controller, 0) self.media_volume(controller, 0)
else: else:
self.media_volume(controller, controller.media_info.volume) self.media_volume(controller, controller.media_info.volume)
if status: if first_time:
if not controller.media_info.is_background: if not controller.media_info.is_background:
display.frame.evaluateJavaScript('show_blank("desktop");') display.frame.evaluateJavaScript('show_blank("desktop");')
self.current_media_players[controller.controller_type].set_visible(display, True) self.current_media_players[controller.controller_type].set_visible(display, True)
# Flash needs to be played and will not AutoPlay controller.mediabar.actions['playbackPlay'].setVisible(False)
if controller.media_info.is_flash: controller.mediabar.actions['playbackPause'].setVisible(True)
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['playbackStop'].setDisabled(False) controller.mediabar.actions['playbackStop'].setDisabled(False)
if controller.is_live: if controller.is_live:
if controller.hide_menu.defaultAction().isChecked() and not controller.media_info.is_background: if controller.hide_menu.defaultAction().isChecked() and not controller.media_info.is_background:
controller.hide_menu.defaultAction().trigger() controller.hide_menu.defaultAction().trigger()
# Start Timer for ui updates # Start Timer for ui updates
if not self.timer.isActive(): if not self.live_timer.isActive():
self.timer.start() 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.seek_slider.blockSignals(False)
controller.volume_slider.blockSignals(False) controller.volume_slider.blockSignals(False)
controller.media_info.is_playing = True
display = self._define_display(controller)
display.setVisible(True)
return 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): def media_pause_msg(self, msg):
""" """
Responds to the request to pause a loaded video Responds to the request to pause a loaded video
:param msg: First element is the controller which should be used :param msg: First element is the controller which should be used
""" """
log.debug('media_pause_msg')
self.media_pause(msg[0]) self.media_pause(msg[0])
def media_pause(self, controller): def media_pause(self, controller):
@ -632,12 +697,31 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
:param controller: The Controller to be paused :param controller: The Controller to be paused
""" """
log.debug('media_pause')
display = self._define_display(controller) display = self._define_display(controller)
self.current_media_players[controller.controller_type].pause(display) if controller.controller_type in self.current_media_players:
controller.mediabar.actions['playbackPlay'].setVisible(True) self.current_media_players[controller.controller_type].pause(display)
controller.mediabar.actions['playbackStop'].setDisabled(False) controller.mediabar.actions['playbackPlay'].setVisible(True)
controller.mediabar.actions['playbackPause'].setVisible(False) 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): 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 :param msg: First element is the controller which should be used
""" """
log.debug('media_stop_msg')
self.media_stop(msg[0]) 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 Responds to the request to stop a loaded video
:param controller: The controller that needs to be stopped :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) display = self._define_display(controller)
if controller.controller_type in self.current_media_players: 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].stop(display)
self.current_media_players[controller.controller_type].set_visible(display, False) self.current_media_players[controller.controller_type].set_visible(display, False)
controller.seek_slider.setSliderPosition(0) controller.seek_slider.setSliderPosition(0)
controller.mediabar.actions['playbackPlay'].setVisible(True) controller.mediabar.actions['playbackPlay'].setVisible(True)
controller.mediabar.actions['playbackStop'].setDisabled(True) controller.mediabar.actions['playbackStop'].setDisabled(True)
controller.mediabar.actions['playbackPause'].setVisible(False) 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): 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 :param msg: First element is the controller which should be used
Second element is a list with the seek value as first element Second element is a list with the seek value as first element
""" """
log.debug('media_seek')
controller = msg[0] controller = msg[0]
seek_value = msg[1][0] seek_value = msg[1][0]
self.media_seek(controller, seek_value) self.media_seek(controller, seek_value)
@ -706,15 +792,15 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
:param controller: The controller to use. :param controller: The controller to use.
:param seek_value: The value to set. :param seek_value: The value to set.
""" """
log.debug('media_seek')
display = self._define_display(controller) display = self._define_display(controller)
self.current_media_players[controller.controller_type].seek(display, seek_value) self.current_media_players[controller.controller_type].seek(display, seek_value)
controller.media_info.timer = seek_value
def media_reset(self, controller): def media_reset(self, controller):
""" """
Responds to the request to reset a loaded video 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) self.set_controls_visible(controller, False)
display = self._define_display(controller) display = self._define_display(controller)
if controller.controller_type in self.current_media_players: if controller.controller_type in self.current_media_players:
@ -735,7 +821,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
return return
display = self._define_display(self.live_controller) display = self._define_display(self.live_controller)
if self.live_controller.controller_type in self.current_media_players and \ 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].pause(display)
self.current_media_players[self.live_controller.controller_type].set_visible(display, False) 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) Registry().execute('live_display_hide', hide_mode)
display = self._define_display(self.live_controller) display = self._define_display(self.live_controller)
if self.live_controller.controller_type in self.current_media_players and \ 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].pause(display)
self.current_media_players[self.live_controller.controller_type].set_visible(display, False) self.current_media_players[self.live_controller.controller_type].set_visible(display, False)
@ -770,22 +856,25 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
return return
display = self._define_display(self.live_controller) display = self._define_display(self.live_controller)
if self.live_controller.controller_type in self.current_media_players and \ 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): 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) self.current_media_players[self.live_controller.controller_type].set_visible(display, True)
# Start Timer for ui updates # Start Timer for ui updates
if not self.timer.isActive(): if not self.live_timer.isActive():
self.timer.start() self.live_timer.start()
def finalise(self): def finalise(self):
""" """
Reset all the media controllers when OpenLP shuts down 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: for controller in self.display_controllers:
self.media_reset(self.display_controllers[controller]) 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 Extract the correct display for a given controller

View File

@ -41,7 +41,7 @@ class MediaPlayer(RegistryProperties):
self.is_active = False self.is_active = False
self.can_background = False self.can_background = False
self.can_folder = False self.can_folder = False
self.state = MediaState.Off self.state = {0: MediaState.Off, 1: MediaState.Off}
self.has_own_widget = False self.has_own_widget = False
self.audio_extensions_list = [] self.audio_extensions_list = []
self.video_extensions_list = [] self.video_extensions_list = []
@ -55,12 +55,16 @@ class MediaPlayer(RegistryProperties):
def setup(self, display): def setup(self, display):
""" """
Create the related widgets for the current display Create the related widgets for the current display
:param display: The display to be updated.
""" """
pass pass
def load(self, display): def load(self, display):
""" """
Load a new media file and check if it is valid Load a new media file and check if it is valid
:param display: The display to be updated.
""" """
return True return True
@ -68,54 +72,75 @@ class MediaPlayer(RegistryProperties):
""" """
If the main display size or position is changed, the media widgets If the main display size or position is changed, the media widgets
should also resized should also resized
:param display: The display to be updated.
""" """
pass pass
def play(self, display): def play(self, display):
""" """
Starts playing of current Media File Starts playing of current Media File
:param display: The display to be updated.
""" """
pass pass
def pause(self, display): def pause(self, display):
""" """
Pause of current Media File Pause of current Media File
:param display: The display to be updated.
""" """
pass pass
def stop(self, display): def stop(self, display):
""" """
Stop playing of current Media File Stop playing of current Media File
:param display: The display to be updated.
""" """
pass pass
def volume(self, display, vol): def volume(self, display, volume):
""" """
Change volume of current Media File Change volume of current Media File
:param display: The display to be updated.
:param volume: The volume to set.
""" """
pass pass
def seek(self, display, seek_value): def seek(self, display, seek_value):
""" """
Change playing position of current Media File Change playing position of current Media File
:param display: The display to be updated.
:param seek_value: The where to seek to.
""" """
pass pass
def reset(self, display): def reset(self, display):
""" """
Remove the current loaded video Remove the current loaded video
:param display: The display to be updated.
""" """
pass pass
def set_visible(self, display, status): def set_visible(self, display, status):
""" """
Show/Hide the media widgets Show/Hide the media widgets
:param display: The display to be updated.
:param status: The status to be set.
""" """
pass pass
def update_ui(self, display): def update_ui(self, display):
""" """
Do some ui related stuff (e.g. update the seek slider) Do some ui related stuff (e.g. update the seek slider)
:param display: The display to be updated.
""" """
pass pass
@ -142,3 +167,45 @@ class MediaPlayer(RegistryProperties):
Returns Information about the player Returns Information about the player
""" """
return '' 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)

View File

@ -133,12 +133,16 @@ class PlayerTab(SettingsTab):
def on_background_color_changed(self, color): def on_background_color_changed(self, color):
""" """
Set the background color Set the background color
:param color: The color to be set.
""" """
self.background_color = color self.background_color = color
def on_player_check_box_changed(self, check_state): def on_player_check_box_changed(self, check_state):
""" """
Add or remove players depending on their status Add or remove players depending on their status
:param check_state: The requested status.
""" """
player = self.sender().player_name player = self.sender().player_name
if check_state == QtCore.Qt.Checked: if check_state == QtCore.Qt.Checked:

View File

@ -4,14 +4,7 @@
############################################################################### ###############################################################################
# OpenLP - Open Source Lyrics Projection # # OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- # # --------------------------------------------------------------------------- #
# Copyright (c) 2008-2014 Raoul Snyman # # Copyright (c) 2008-2016 OpenLP Developers #
# 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 #
# --------------------------------------------------------------------------- # # --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it # # 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 # # 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): def load(self, display):
""" """
Load a video into the display Load a video into the display
:param display:
:param display: The display where the media is
""" """
log.debug('load vid in System Controller') log.debug('load vid in System Controller')
controller = display.controller controller = display.controller
@ -141,93 +135,122 @@ class SystemPlayer(MediaPlayer):
def resize(self, display): def resize(self, display):
""" """
Resize the display Resize the display
:param display:
:param display: The display where the media is
""" """
display.video_widget.resize(display.size()) display.video_widget.resize(display.size())
def play(self, display): def play(self, display):
""" """
Play the current media item Play the current media item
:param display:
:param display: The display where the media is
""" """
log.info('Play the current item') log.info('Play the current item')
controller = display.controller controller = display.controller
start_time = 0 start_time = 0
if display.media_player.state() != QtMultimedia.QMediaPlayer.PausedState and \ if display.controller.is_live:
controller.media_info.start_time > 0: if self.get_live_state() != QtMultimedia.QMediaPlayer.PausedState and controller.media_info.start_time > 0:
start_time = controller.media_info.start_time 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() display.media_player.play()
if start_time > 0: if start_time > 0:
self.seek(display, controller.media_info.start_time * 1000) self.seek(display, controller.media_info.start_time * 1000)
self.volume(display, controller.media_info.volume) self.volume(display, controller.media_info.volume)
display.media_player.durationChanged.connect(functools.partial(self.set_duration, controller)) 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_() display.video_widget.raise_()
return True return True
def pause(self, display): def pause(self, display):
""" """
Pause the current media item Pause the current media item
:param display: The display where the media is
""" """
display.media_player.pause() display.media_player.pause()
if display.media_player.state() == QtMultimedia.QMediaPlayer.PausedState: if display.controller.is_live:
self.state = MediaState.Paused 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): def stop(self, display):
""" """
Stop the current media item 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() display.media_player.stop()
self.set_visible(display, False) 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 Set the volume
:param display: The display where the media is
:param volume: The volume to be set
""" """
if display.has_audio: if display.has_audio:
display.media_player.setVolume(vol) display.media_player.setVolume(volume)
def seek(self, display, seek_value): def seek(self, display, seek_value):
""" """
Go to a particular point in the current media item 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) display.media_player.setPosition(seek_value)
def reset(self, display): def reset(self, display):
""" """
Reset the media player Reset the media player
:param display: The display where the media is
""" """
display.media_player.stop() display.media_player.stop()
display.media_player.setMedia(QtMultimedia.QMediaContent()) display.media_player.setMedia(QtMultimedia.QMediaContent())
self.set_visible(display, False) self.set_visible(display, False)
display.video_widget.setVisible(False) display.video_widget.setVisible(False)
self.state = MediaState.Off self.set_state(MediaState.Off, display)
def set_visible(self, display, status): def set_visible(self, display, status):
""" """
Set the visibility of the widget 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: if self.has_own_widget:
display.video_widget.setVisible(status) display.video_widget.setVisible(status)
@staticmethod @staticmethod
def set_duration(controller, duration): 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): def update_ui(self, display):
""" """
Update the UI Update the UI
:param display: The display where the media is
""" """
if display.media_player.state() == QtMultimedia.QMediaPlayer.PausedState and self.state != MediaState.Paused: if display.media_player.state() == QtMultimedia.QMediaPlayer.PausedState and self.state != MediaState.Paused:
self.stop(display) self.stop(display)
controller = display.controller controller = display.controller
if controller.media_info.end_time > 0: 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.stop(display)
self.set_visible(display, False) self.set_visible(display, False)
if not controller.seek_slider.isSliderDown(): if not controller.seek_slider.isSliderDown():

View File

@ -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 "<Track track_id='{0}', track_type='{1}'>".format(self.track_id, self.track_type)
def to_data(self):
data = {}
for k, v in six.iteritems(self.__dict__):
if k != 'xml_dom_fragment':
data[k] = v
return data
class MediaInfoWrapper(object):
def __init__(self, xml):
self.xml_dom = xml
xml_types = (str,) # no unicode type in python3
if isinstance(xml, xml_types):
self.xml_dom = MediaInfoWrapper.parse_xml_data_into_dom(xml)
@staticmethod
def parse_xml_data_into_dom(xml_data):
return BeautifulSoup(xml_data, "xml")
@staticmethod
def parse(filename, environment=ENV_DICT):
command = ["mediainfo", "-f", "--Output=XML", filename]
fileno_out, fname_out = mkstemp(suffix=".xml", prefix="media-")
fileno_err, fname_err = mkstemp(suffix=".err", prefix="media-")
fp_out = os.fdopen(fileno_out, 'r+b')
fp_err = os.fdopen(fileno_err, 'r+b')
p = Popen(command, stdout=fp_out, stderr=fp_err, env=environment)
p.wait()
fp_out.seek(0)
xml_dom = MediaInfoWrapper.parse_xml_data_into_dom(fp_out.read())
fp_out.close()
fp_err.close()
os.unlink(fname_out)
os.unlink(fname_err)
return MediaInfoWrapper(xml_dom)
def _populate_tracks(self):
if self.xml_dom is None:
return
for xml_track in self.xml_dom.Mediainfo.File.find_all("track"):
self._tracks.append(Track(xml_track))
@property
def tracks(self):
if not hasattr(self, "_tracks"):
self._tracks = []
if len(self._tracks) == 0:
self._populate_tracks()
return self._tracks
def to_data(self):
data = {'tracks': []}
for track in self.tracks:
data['tracks'].append(track.to_data())
return data
def to_json(self):
return json.dumps(self.to_data())

View File

@ -144,6 +144,9 @@ class VlcPlayer(MediaPlayer):
def setup(self, display): def setup(self, display):
""" """
Set up the media player Set up the media player
:param display: The display where the media is
:return:
""" """
vlc = get_vlc() vlc = get_vlc()
display.vlc_widget = QtWidgets.QFrame(display) display.vlc_widget = QtWidgets.QFrame(display)
@ -186,6 +189,9 @@ class VlcPlayer(MediaPlayer):
def load(self, display): def load(self, display):
""" """
Load a video into VLC Load a video into VLC
:param display: The display where the media is
:return:
""" """
vlc = get_vlc() vlc = get_vlc()
log.debug('load vid in Vlc Controller') log.debug('load vid in Vlc Controller')
@ -214,18 +220,16 @@ class VlcPlayer(MediaPlayer):
# parse the metadata of the file # parse the metadata of the file
display.vlc_media.parse() display.vlc_media.parse()
self.volume(display, volume) self.volume(display, volume)
# We need to set media_info.length during load because we want
# to avoid start and stop the video twice. Once for real playback
# and once to just get media length.
#
# Media plugin depends on knowing media length before playback.
controller.media_info.length = int(display.vlc_media_player.get_media().get_duration() / 1000)
return True return True
def media_state_wait(self, display, media_state): def media_state_wait(self, display, media_state):
""" """
Wait for the video to change its state Wait for the video to change its state
Wait no longer than 60 seconds. (loading an iso file needs a long time) Wait no longer than 60 seconds. (loading an iso file needs a long time)
:param media_state: The state of the playing media
:param display: The display where the media is
:return:
""" """
vlc = get_vlc() vlc = get_vlc()
start = datetime.now() start = datetime.now()
@ -240,25 +244,40 @@ class VlcPlayer(MediaPlayer):
def resize(self, display): def resize(self, display):
""" """
Resize the player Resize the player
:param display: The display where the media is
:return:
""" """
display.vlc_widget.resize(display.size()) display.vlc_widget.resize(display.size())
def play(self, display): def play(self, display):
""" """
Play the current item Play the current item
:param display: The display where the media is
:return:
""" """
vlc = get_vlc() vlc = get_vlc()
controller = display.controller controller = display.controller
start_time = 0 start_time = 0
log.debug('vlc play') log.debug('vlc play')
if self.state != MediaState.Paused and controller.media_info.start_time > 0: if display.controller.is_live:
start_time = controller.media_info.start_time if self.get_live_state() != MediaState.Paused and controller.media_info.start_time > 0:
start_time = controller.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() threading.Thread(target=display.vlc_media_player.play).start()
if not self.media_state_wait(display, vlc.State.Playing): if not self.media_state_wait(display, vlc.State.Playing):
return False return False
if self.state != MediaState.Paused and controller.media_info.start_time > 0: if display.controller.is_live:
log.debug('vlc play, starttime set') if self.get_live_state() != MediaState.Paused and controller.media_info.start_time > 0:
start_time = controller.media_info.start_time log.debug('vlc play, start time set')
start_time = controller.media_info.start_time
else:
if self.get_preview_state() != MediaState.Paused and controller.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)) log.debug('mediatype: ' + str(controller.media_info.media_type))
# Set tracks for the optical device # Set tracks for the optical device
if controller.media_info.media_type == MediaType.DVD: if controller.media_info.media_type == MediaType.DVD:
@ -279,37 +298,45 @@ class VlcPlayer(MediaPlayer):
log.debug('vlc play, starttime set: ' + str(controller.media_info.start_time)) log.debug('vlc play, starttime set: ' + str(controller.media_info.start_time))
start_time = 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 controller.media_info.length = controller.media_info.end_time - controller.media_info.start_time
else:
controller.media_info.length = int(display.vlc_media_player.get_media().get_duration() / 1000)
self.volume(display, controller.media_info.volume) self.volume(display, controller.media_info.volume)
if start_time > 0 and display.vlc_media_player.is_seekable(): if start_time > 0 and display.vlc_media_player.is_seekable():
display.vlc_media_player.set_time(int(start_time * 1000)) display.vlc_media_player.set_time(int(start_time))
controller.seek_slider.setMaximum(controller.media_info.length * 1000) controller.seek_slider.setMaximum(controller.media_info.length)
self.state = MediaState.Playing self.set_state(MediaState.Playing, display)
display.vlc_widget.raise_() display.vlc_widget.raise_()
return True return True
def pause(self, display): def pause(self, display):
""" """
Pause the current item Pause the current item
:param display: The display where the media is
:return:
""" """
vlc = get_vlc() vlc = get_vlc()
if display.vlc_media.get_state() != vlc.State.Playing: if display.vlc_media.get_state() != vlc.State.Playing:
return return
display.vlc_media_player.pause() display.vlc_media_player.pause()
if self.media_state_wait(display, vlc.State.Paused): if self.media_state_wait(display, vlc.State.Paused):
self.state = MediaState.Paused self.set_state(MediaState.Paused, display)
def stop(self, display): def stop(self, display):
""" """
Stop the current item Stop the current item
:param display: The display where the media is
:return:
""" """
threading.Thread(target=display.vlc_media_player.stop).start() threading.Thread(target=display.vlc_media_player.stop).start()
self.state = MediaState.Stopped self.set_state(MediaState.Stopped, display)
def volume(self, display, vol): def volume(self, display, vol):
""" """
Set the volume Set the volume
:param vol: The volume to be sets
:param display: The display where the media is
:return:
""" """
if display.has_audio: if display.has_audio:
display.vlc_media_player.audio_set_volume(vol) display.vlc_media_player.audio_set_volume(vol)
@ -317,6 +344,9 @@ class VlcPlayer(MediaPlayer):
def seek(self, display, seek_value): def seek(self, display, seek_value):
""" """
Go to a particular position Go to a particular position
:param seek_value: The position of where a seek goes to
:param display: The display where the media is
""" """
if display.controller.media_info.media_type == MediaType.CD \ if display.controller.media_info.media_type == MediaType.CD \
or display.controller.media_info.media_type == MediaType.DVD: or display.controller.media_info.media_type == MediaType.DVD:
@ -327,14 +357,19 @@ class VlcPlayer(MediaPlayer):
def reset(self, display): def reset(self, display):
""" """
Reset the player Reset the player
:param display: The display where the media is
""" """
display.vlc_media_player.stop() display.vlc_media_player.stop()
display.vlc_widget.setVisible(False) display.vlc_widget.setVisible(False)
self.state = MediaState.Off self.set_state(MediaState.Off, display)
def set_visible(self, display, status): def set_visible(self, display, status):
""" """
Set the visibility Set the visibility
:param display: The display where the media is
:param status: The visibility status
""" """
if self.has_own_widget: if self.has_own_widget:
display.vlc_widget.setVisible(status) display.vlc_widget.setVisible(status)
@ -342,6 +377,8 @@ class VlcPlayer(MediaPlayer):
def update_ui(self, display): def update_ui(self, display):
""" """
Update the UI Update the UI
:param display: The display where the media is
""" """
vlc = get_vlc() vlc = get_vlc()
# Stop video if playback is finished. # Stop video if playback is finished.

View File

@ -99,74 +99,6 @@ VIDEO_HTML = """
<video id="video" class="size" style="visibility:hidden" autobuffer preload></video> <video id="video" class="size" style="visibility:hidden" autobuffer preload></video>
""" """
FLASH_CSS = """
#flash {
z-index:5;
}
"""
FLASH_JS = """
function getFlashMovieObject(movieName)
{
if (window.document[movieName]){
return window.document[movieName];
}
if (document.embeds && document.embeds[movieName]){
return document.embeds[movieName];
}
}
function show_flash(state, path, volume, variable_value){
var text = document.getElementById('flash');
var flashMovie = getFlashMovieObject("OpenLPFlashMovie");
var src = "src = 'file:///" + path + "'";
var view_parm = " wmode='opaque'" + " width='100%%'" + " height='100%%'";
var swf_parm = " name='OpenLPFlashMovie'" + " autostart='true' loop='false' play='true'" +
" hidden='false' swliveconnect='true' allowscriptaccess='always'" + " volume='" + volume + "'";
switch(state){
case 'load':
text.innerHTML = "<embed " + src + view_parm + swf_parm + "/>";
flashMovie = getFlashMovieObject("OpenLPFlashMovie");
flashMovie.Play();
break;
case 'play':
flashMovie.Play();
break;
case 'pause':
flashMovie.StopPlay();
break;
case 'stop':
flashMovie.StopPlay();
tempHtml = text.innerHTML;
text.innerHTML = '';
text.innerHTML = tempHtml;
break;
case 'close':
flashMovie.StopPlay();
text.innerHTML = '';
break;
case 'length':
return flashMovie.TotalFrames();
case 'current_time':
return flashMovie.CurrentFrame();
case 'seek':
// flashMovie.GotoFrame(variable_value);
break;
case 'isEnded':
//TODO check flash end
return false;
case 'setVisible':
text.style.visibility = variable_value;
break;
}
}
"""
FLASH_HTML = """
<div id="flash" class="size" style="visibility:hidden"></div>
"""
VIDEO_EXT = ['*.3gp', '*.3gpp', '*.3g2', '*.3gpp2', '*.aac', '*.flv', '*.f4a', '*.f4b', '*.f4p', '*.f4v', '*.mov', VIDEO_EXT = ['*.3gp', '*.3gpp', '*.3g2', '*.3gpp2', '*.aac', '*.flv', '*.f4a', '*.f4b', '*.f4p', '*.f4v', '*.mov',
'*.m4a', '*.m4b', '*.m4p', '*.m4v', '*.mkv', '*.mp4', '*.ogv', '*.webm', '*.mpg', '*.wmv', '*.mpeg', '*.m4a', '*.m4b', '*.m4p', '*.m4v', '*.mkv', '*.mp4', '*.ogv', '*.webm', '*.mpg', '*.wmv', '*.mpeg',
'*.avi', '*.swf'] '*.avi', '*.swf']
@ -198,23 +130,25 @@ class WebkitPlayer(MediaPlayer):
""" """
background = QtGui.QColor(Settings().value('players/background color')).name() background = QtGui.QColor(Settings().value('players/background color')).name()
css = VIDEO_CSS % {'bgcolor': background} css = VIDEO_CSS % {'bgcolor': background}
return css + FLASH_CSS return css
def get_media_display_javascript(self): def get_media_display_javascript(self):
""" """
Add javascript functions to htmlbuilder Add javascript functions to htmlbuilder
""" """
return VIDEO_JS + FLASH_JS return VIDEO_JS
def get_media_display_html(self): def get_media_display_html(self):
""" """
Add html code to htmlbuilder Add html code to htmlbuilder
""" """
return VIDEO_HTML + FLASH_HTML return VIDEO_HTML
def setup(self, display): def setup(self, display):
""" """
Set up the player Set up the player
:param display: The display to be updated.
""" """
display.web_view.resize(display.size()) display.web_view.resize(display.size())
display.web_view.raise_() display.web_view.raise_()
@ -235,6 +169,8 @@ class WebkitPlayer(MediaPlayer):
def load(self, display): def load(self, display):
""" """
Load a video Load a video
:param display: The display to be updated.
""" """
log.debug('load vid in Webkit Controller') log.debug('load vid in Webkit Controller')
controller = display.controller controller = display.controller
@ -249,132 +185,120 @@ class WebkitPlayer(MediaPlayer):
else: else:
loop = 'false' loop = 'false'
display.web_view.setVisible(True) display.web_view.setVisible(True)
if controller.media_info.file_info.suffix() == 'swf': js = 'show_video("load", "%s", %s, %s);' % (path.replace('\\', '\\\\'), str(vol), loop)
controller.media_info.is_flash = True
js = 'show_flash("load","%s");' % (path.replace('\\', '\\\\'))
else:
js = 'show_video("load", "%s", %s, %s);' % (path.replace('\\', '\\\\'), str(vol), loop)
display.frame.evaluateJavaScript(js) display.frame.evaluateJavaScript(js)
return True return True
def resize(self, display): def resize(self, display):
""" """
Resize the player Resize the player
:param display: The display to be updated.
""" """
display.web_view.resize(display.size()) display.web_view.resize(display.size())
def play(self, display): def play(self, display):
""" """
Play a video Play a video
:param display: The display to be updated.
""" """
controller = display.controller controller = display.controller
display.web_loaded = True display.web_loaded = True
length = 0
start_time = 0 start_time = 0
if self.state != MediaState.Paused and controller.media_info.start_time > 0: if display.controller.is_live:
start_time = controller.media_info.start_time if self.get_live_state() != MediaState.Paused and controller.media_info.start_time > 0:
self.set_visible(display, True) start_time = controller.media_info.start_time
if controller.media_info.is_flash:
display.frame.evaluateJavaScript('show_flash("play");')
else: else:
display.frame.evaluateJavaScript('show_video("play");') if self.get_preview_state() != MediaState.Paused and controller.media_info.start_time > 0:
start_time = controller.media_info.start_time
self.set_visible(display, True)
display.frame.evaluateJavaScript('show_video("play");')
if start_time > 0: if start_time > 0:
self.seek(display, controller.media_info.start_time * 1000) self.seek(display, controller.media_info.start_time * 1000)
# TODO add playing check and get the correct media length self.set_state(MediaState.Playing, display)
controller.media_info.length = length
self.state = MediaState.Playing
display.web_view.raise_() display.web_view.raise_()
return True return True
def pause(self, display): def pause(self, display):
""" """
Pause a video Pause a video
:param display: The display to be updated.
""" """
controller = display.controller display.frame.evaluateJavaScript('show_video("pause");')
if controller.media_info.is_flash: self.set_state(MediaState.Paused, display)
display.frame.evaluateJavaScript('show_flash("pause");')
else:
display.frame.evaluateJavaScript('show_video("pause");')
self.state = MediaState.Paused
def stop(self, display): def stop(self, display):
""" """
Stop a video Stop a video
:param display: The display to be updated.
""" """
controller = display.controller display.frame.evaluateJavaScript('show_video("stop");')
if controller.media_info.is_flash: self.set_state(MediaState.Stopped, display)
display.frame.evaluateJavaScript('show_flash("stop");')
else:
display.frame.evaluateJavaScript('show_video("stop");')
self.state = MediaState.Stopped
def volume(self, display, volume): def volume(self, display, volume):
""" """
Set the volume Set the volume
:param display: The display to be updated.
:param volume: The volume to be set.
""" """
controller = display.controller
# 1.0 is the highest value # 1.0 is the highest value
if display.has_audio: if display.has_audio:
vol = float(volume) / float(100) vol = float(volume) / float(100)
if not controller.media_info.is_flash: display.frame.evaluateJavaScript('show_video(null, null, %s);' % str(vol))
display.frame.evaluateJavaScript('show_video(null, null, %s);' % str(vol))
def seek(self, display, seek_value): def seek(self, display, seek_value):
""" """
Go to a position in the video Go to a position in the video
:param display: The display to be updated.
:param seek_value: The value to be set.
""" """
controller = display.controller seek = float(seek_value) / 1000
if controller.media_info.is_flash: display.frame.evaluateJavaScript('show_video("seek", null, null, null, "%f");' % seek)
seek = seek_value
display.frame.evaluateJavaScript('show_flash("seek", null, null, "%s");' % seek)
else:
seek = float(seek_value) / 1000
display.frame.evaluateJavaScript('show_video("seek", null, null, null, "%f");' % seek)
def reset(self, display): def reset(self, display):
""" """
Reset the player Reset the player
"""
controller = display.controller
if controller.media_info.is_flash:
display.frame.evaluateJavaScript('show_flash("close");')
else:
display.frame.evaluateJavaScript('show_video("close");')
self.state = MediaState.Off
def set_visible(self, display, status): :param display: The display to be updated.
"""
display.frame.evaluateJavaScript('show_video("close");')
self.set_state(MediaState.Off, display)
def set_visible(self, display, visibility):
""" """
Set the visibility Set the visibility
:param display: The display to be updated.
:param visibility: The visibility to be set.
""" """
controller = display.controller if visibility:
if status:
is_visible = "visible" is_visible = "visible"
else: else:
is_visible = "hidden" is_visible = "hidden"
if controller.media_info.is_flash: display.frame.evaluateJavaScript('show_video("setVisible", null, null, null, "%s");' % is_visible)
display.frame.evaluateJavaScript('show_flash("setVisible", null, null, "%s");' % is_visible)
else:
display.frame.evaluateJavaScript('show_video("setVisible", null, null, null, "%s");' % is_visible)
def update_ui(self, display): def update_ui(self, display):
""" """
Update the UI Update the UI
:param display: The display to be updated.
""" """
controller = display.controller controller = display.controller
if controller.media_info.is_flash: if display.frame.evaluateJavaScript('show_video("isEnded");'):
current_time = display.frame.evaluateJavaScript('show_flash("current_time");') self.stop(display)
length = display.frame.evaluateJavaScript('show_flash("length");') current_time = display.frame.evaluateJavaScript('show_video("current_time");')
else: # check if conversion was ok and value is not 'NaN'
if display.frame.evaluateJavaScript('show_video("isEnded");'): if current_time and current_time != float('inf'):
self.stop(display) current_time = int(current_time * 1000)
current_time = display.frame.evaluateJavaScript('show_video("current_time");') length = display.frame.evaluateJavaScript('show_video("length");')
# check if conversion was ok and value is not 'NaN' # check if conversion was ok and value is not 'NaN'
if current_time and current_time != float('inf'): if length and length != float('inf'):
current_time = int(current_time * 1000) length = int(length * 1000)
length = display.frame.evaluateJavaScript('show_video("length");')
# check if conversion was ok and value is not 'NaN'
if length and length != float('inf'):
length = int(length * 1000)
if current_time and length: if current_time and length:
controller.media_info.length = length controller.media_info.length = length
controller.seek_slider.setMaximum(length) controller.seek_slider.setMaximum(length)

View File

@ -84,7 +84,7 @@ class DisplayController(QtWidgets.QWidget):
super(DisplayController, self).__init__(parent) super(DisplayController, self).__init__(parent)
self.is_live = False self.is_live = False
self.display = None self.display = None
self.controller_type = DisplayControllerType.Plugin self.controller_type = None
def send_to_plugins(self, *args): def send_to_plugins(self, *args):
""" """

View File

@ -147,6 +147,7 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
def update_lines_text(self, lines): def update_lines_text(self, lines):
""" """
Updates the lines on a page on the wizard Updates the lines on a page on the wizard
:param lines: then number of lines to be displayed
""" """
self.main_line_count_label.setText( self.main_line_count_label.setText(
translate('OpenLP.ThemeForm', '(approximately %d lines per slide)') % int(lines)) translate('OpenLP.ThemeForm', '(approximately %d lines per slide)') % int(lines))
@ -186,6 +187,7 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
def on_current_id_changed(self, page_id): def on_current_id_changed(self, page_id):
""" """
Detects Page changes and updates as appropriate. Detects Page changes and updates as appropriate.
:param page_id: current page number
""" """
enabled = self.page(page_id) == self.area_position_page enabled = self.page(page_id) == self.area_position_page
self.setOption(QtWidgets.QWizard.HaveCustomButton1, enabled) self.setOption(QtWidgets.QWizard.HaveCustomButton1, enabled)

View File

@ -111,6 +111,7 @@ class OpenLPWizard(QtWidgets.QWizard, RegistryProperties):
def setupUi(self, image): def setupUi(self, image):
""" """
Set up the wizard UI. Set up the wizard UI.
:param image: path to start up image
""" """
self.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) self.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
self.setModal(True) self.setModal(True)
@ -210,6 +211,7 @@ class OpenLPWizard(QtWidgets.QWizard, RegistryProperties):
def on_current_id_changed(self, page_id): def on_current_id_changed(self, page_id):
""" """
Perform necessary functions depending on which wizard page is active. Perform necessary functions depending on which wizard page is active.
:param page_id: current page number
""" """
if self.with_progress_page and self.page(page_id) == self.progress_page: if self.with_progress_page and self.page(page_id) == self.progress_page:
self.pre_wizard() self.pre_wizard()
@ -221,6 +223,7 @@ class OpenLPWizard(QtWidgets.QWizard, RegistryProperties):
def custom_page_changed(self, page_id): def custom_page_changed(self, page_id):
""" """
Called when changing to a page other than the progress page Called when changing to a page other than the progress page
:param page_id: current page number
""" """
pass pass

View File

@ -195,7 +195,7 @@ class ImageMediaItem(MediaManagerItem):
Add custom buttons to the end of the toolbar Add custom buttons to the end of the toolbar
""" """
self.replace_action = self.toolbar.add_toolbar_action('replace_action', self.replace_action = self.toolbar.add_toolbar_action('replace_action',
icon=':/slides/slide_blank.png', icon=':/slides/slide_theme.png',
triggers=self.on_replace_click) triggers=self.on_replace_click)
self.reset_action = self.toolbar.add_toolbar_action('reset_action', self.reset_action = self.toolbar.add_toolbar_action('reset_action',
icon=':/system/system_close.png', icon=':/system/system_close.png',

View File

@ -29,8 +29,8 @@ from openlp.core.common import Registry, RegistryProperties, AppLocation, Settin
translate translate
from openlp.core.lib import ItemCapabilities, MediaManagerItem, MediaType, ServiceItem, ServiceItemContext, \ from openlp.core.lib import ItemCapabilities, MediaManagerItem, MediaType, ServiceItem, ServiceItemContext, \
build_icon, check_item_selected build_icon, check_item_selected
from openlp.core.lib.ui import critical_error_message_box, create_horizontal_adjusting_combo_box from openlp.core.lib.ui import create_widget_action, critical_error_message_box, create_horizontal_adjusting_combo_box
from openlp.core.ui import DisplayController, Display, DisplayControllerType from openlp.core.ui import DisplayControllerType
from openlp.core.ui.media import get_media_players, set_media_players, parse_optical_path, format_milliseconds from openlp.core.ui.media import get_media_players, set_media_players, parse_optical_path, format_milliseconds
from openlp.core.common.languagemanager import get_locale_key from openlp.core.common.languagemanager import get_locale_key
from openlp.core.ui.media.vlcplayer import get_vlc from openlp.core.ui.media.vlcplayer import get_vlc
@ -78,19 +78,9 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
self.single_service_item = False self.single_service_item = False
self.has_search = True self.has_search = True
self.media_object = None self.media_object = None
self.display_controller = DisplayController(self.parent()) # self.display_controller = DisplayController(self.parent())
self.display_controller.controller_layout = QtWidgets.QVBoxLayout()
self.media_controller.register_controller(self.display_controller)
self.media_controller.set_controls_visible(self.display_controller, False)
self.display_controller.preview_display = Display(self.display_controller)
self.display_controller.preview_display.hide()
self.display_controller.preview_display.setGeometry(QtCore.QRect(0, 0, 300, 300))
self.display_controller.preview_display.screen = {'size': self.display_controller.preview_display.geometry()}
self.display_controller.preview_display.setup()
self.media_controller.setup_display(self.display_controller.preview_display, False)
Registry().register_function('video_background_replaced', self.video_background_replaced) Registry().register_function('video_background_replaced', self.video_background_replaced)
Registry().register_function('mediaitem_media_rebuild', self.rebuild_players) Registry().register_function('mediaitem_media_rebuild', self.rebuild_players)
Registry().register_function('config_screen_changed', self.display_setup)
# Allow DnD from the desktop # Allow DnD from the desktop
self.list_view.activateDnD() self.list_view.activateDnD()
@ -101,12 +91,17 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
""" """
self.on_new_prompt = translate('MediaPlugin.MediaItem', 'Select Media') self.on_new_prompt = translate('MediaPlugin.MediaItem', 'Select Media')
self.replace_action.setText(UiStrings().ReplaceBG) self.replace_action.setText(UiStrings().ReplaceBG)
self.replace_action_context.setText(UiStrings().ReplaceBG)
if 'webkit' in get_media_players()[0]: if 'webkit' in get_media_players()[0]:
self.replace_action.setToolTip(UiStrings().ReplaceLiveBG) self.replace_action.setToolTip(UiStrings().ReplaceLiveBG)
self.replace_action_context.setToolTip(UiStrings().ReplaceLiveBG)
else: else:
self.replace_action.setToolTip(UiStrings().ReplaceLiveBGDisabled) self.replace_action.setToolTip(UiStrings().ReplaceLiveBGDisabled)
self.replace_action_context.setToolTip(UiStrings().ReplaceLiveBGDisabled)
self.reset_action.setText(UiStrings().ResetBG) self.reset_action.setText(UiStrings().ResetBG)
self.reset_action.setToolTip(UiStrings().ResetLiveBG) self.reset_action.setToolTip(UiStrings().ResetLiveBG)
self.reset_action_context.setText(UiStrings().ResetBG)
self.reset_action_context.setToolTip(UiStrings().ResetLiveBG)
self.automatic = UiStrings().Automatic self.automatic = UiStrings().Automatic
self.display_type_label.setText(translate('MediaPlugin.MediaItem', 'Use Player:')) self.display_type_label.setText(translate('MediaPlugin.MediaItem', 'Use Player:'))
@ -151,10 +146,11 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
Adds buttons to the end of the header bar. Adds buttons to the end of the header bar.
""" """
# Replace backgrounds do not work at present so remove functionality. # Replace backgrounds do not work at present so remove functionality.
self.replace_action = self.toolbar.add_toolbar_action('replace_action', icon=':/slides/slide_blank.png', self.replace_action = self.toolbar.add_toolbar_action('replace_action', icon=':/slides/slide_theme.png',
triggers=self.on_replace_click) triggers=self.on_replace_click)
if 'webkit' not in get_media_players()[0]: if 'webkit' not in get_media_players()[0]:
self.replace_action.setDisabled(True) self.replace_action.setDisabled(True)
self.replace_action_context.setDisabled(True)
self.reset_action = self.toolbar.add_toolbar_action('reset_action', icon=':/system/system_close.png', self.reset_action = self.toolbar.add_toolbar_action('reset_action', icon=':/system/system_close.png',
visible=False, triggers=self.on_reset_click) visible=False, triggers=self.on_reset_click)
self.media_widget = QtWidgets.QWidget(self) self.media_widget = QtWidgets.QWidget(self)
@ -173,7 +169,17 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
self.page_layout.addWidget(self.media_widget) self.page_layout.addWidget(self.media_widget)
self.display_type_combo_box.currentIndexChanged.connect(self.override_player_changed) self.display_type_combo_box.currentIndexChanged.connect(self.override_player_changed)
def override_player_changed(self, index): def add_custom_context_actions(self):
create_widget_action(self.list_view, separator=True)
self.replace_action_context = create_widget_action(
self.list_view, text=UiStrings().ReplaceBG, icon=':/slides/slide_blank.png',
triggers=self.on_replace_click)
self.reset_action_context = create_widget_action(
self.list_view, text=UiStrings().ReplaceLiveBG, icon=':/system/system_close.png',
visible=False, triggers=self.on_reset_click)
@staticmethod
def override_player_changed(index):
""" """
The Player has been overridden The Player has been overridden
@ -191,12 +197,14 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
""" """
self.media_controller.media_reset(self.live_controller) self.media_controller.media_reset(self.live_controller)
self.reset_action.setVisible(False) self.reset_action.setVisible(False)
self.reset_action_context.setVisible(False)
def video_background_replaced(self): def video_background_replaced(self):
""" """
Triggered by main display on change of serviceitem. Triggered by main display on change of serviceitem.
""" """
self.reset_action.setVisible(False) self.reset_action.setVisible(False)
self.reset_action_context.setVisible(False)
def on_replace_click(self): def on_replace_click(self):
""" """
@ -215,6 +223,7 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
service_item.add_from_command(path, name, CLAPPERBOARD) service_item.add_from_command(path, name, CLAPPERBOARD)
if self.media_controller.video(DisplayControllerType.Live, service_item, video_behind_text=True): if self.media_controller.video(DisplayControllerType.Live, service_item, video_behind_text=True):
self.reset_action.setVisible(True) self.reset_action.setVisible(True)
self.reset_action_context.setVisible(True)
else: else:
critical_error_message_box(UiStrings().LiveBGError, critical_error_message_box(UiStrings().LiveBGError,
translate('MediaPlugin.MediaItem', translate('MediaPlugin.MediaItem',
@ -273,16 +282,14 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
service_item.processor = self.display_type_combo_box.currentText() service_item.processor = self.display_type_combo_box.currentText()
service_item.add_from_command(path, name, CLAPPERBOARD) service_item.add_from_command(path, name, CLAPPERBOARD)
# Only get start and end times if going to a service # Only get start and end times if going to a service
if context == ServiceItemContext.Service: if not self.media_controller.media_length(service_item):
# Start media and obtain the length return False
if not self.media_controller.media_length(service_item):
return False
service_item.add_capability(ItemCapabilities.CanAutoStartForLive) service_item.add_capability(ItemCapabilities.CanAutoStartForLive)
service_item.add_capability(ItemCapabilities.CanEditTitle) service_item.add_capability(ItemCapabilities.CanEditTitle)
service_item.add_capability(ItemCapabilities.RequiresMedia) service_item.add_capability(ItemCapabilities.RequiresMedia)
if Settings().value(self.settings_section + '/media auto start') == QtCore.Qt.Checked: if Settings().value(self.settings_section + '/media auto start') == QtCore.Qt.Checked:
service_item.will_auto_start = True service_item.will_auto_start = True
# force a non-existent theme # force a non-existent theme
service_item.theme = -1 service_item.theme = -1
return True return True
@ -305,12 +312,6 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
' '.join(self.media_controller.video_extensions_list), ' '.join(self.media_controller.video_extensions_list),
' '.join(self.media_controller.audio_extensions_list), UiStrings().AllFiles) ' '.join(self.media_controller.audio_extensions_list), UiStrings().AllFiles)
def display_setup(self):
"""
Setup media controller display.
"""
self.media_controller.setup_display(self.display_controller.preview_display, False)
def populate_display_types(self): def populate_display_types(self):
""" """
Load the combobox with the enabled media players, allowing user to select a specific player if settings allow. Load the combobox with the enabled media players, allowing user to select a specific player if settings allow.
@ -385,16 +386,16 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
if item_name: if item_name:
self.list_view.addItem(item_name) self.list_view.addItem(item_name)
def get_list(self, type=MediaType.Audio): def get_list(self, media_type=MediaType.Audio):
""" """
Get the list of media, optional select media type. Get the list of media, optional select media type.
:param type: Type to get, defaults to audio. :param media_type: Type to get, defaults to audio.
:return: The media list :return: The media list
""" """
media = Settings().value(self.settings_section + '/media files') media = Settings().value(self.settings_section + '/media files')
media.sort(key=lambda filename: get_locale_key(os.path.split(str(filename))[1])) media.sort(key=lambda filename: get_locale_key(os.path.split(str(filename))[1]))
if type == MediaType.Audio: if media_type == MediaType.Audio:
extension = self.media_controller.audio_extensions_list extension = self.media_controller.audio_extensions_list
else: else:
extension = self.media_controller.video_extensions_list extension = self.media_controller.video_extensions_list

View File

@ -60,13 +60,6 @@ class MediaPlugin(Plugin):
""" """
Override the inherited initialise() method in order to upgrade the media before trying to load it Override the inherited initialise() method in order to upgrade the media before trying to load it
""" """
# FIXME: Remove after 2.2 release.
# This is needed to load the list of media from the config saved before the settings rewrite.
if self.media_item_class is not None:
loaded_list = Settings().get_files_from_config(self)
# Now save the list to the config using our Settings class.
if loaded_list:
Settings().setValue('%s/%s files' % (self.settings_section, self.name), loaded_list)
super().initialise() super().initialise()
def app_startup(self): def app_startup(self):

Binary file not shown.

Before

Width:  |  Height:  |  Size: 815 B

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -148,7 +148,7 @@ class TestMainWindow(TestCase, TestMixin):
# THEN: the following registry functions should have been registered # THEN: the following registry functions should have been registered
self.assertEqual(len(self.registry.service_list), 6, 'The registry should have 6 services.') self.assertEqual(len(self.registry.service_list), 6, 'The registry should have 6 services.')
self.assertEqual(len(self.registry.functions_list), 16, 'The registry should have 16 functions') self.assertEqual(len(self.registry.functions_list), 17, 'The registry should have 17 functions')
self.assertTrue('application' in self.registry.service_list, 'The application should have been registered.') self.assertTrue('application' in self.registry.service_list, 'The application should have been registered.')
self.assertTrue('main_window' in self.registry.service_list, 'The main_window should have been registered.') self.assertTrue('main_window' in self.registry.service_list, 'The main_window should have been registered.')
self.assertTrue('media_controller' in self.registry.service_list, 'The media_controller should have been ' self.assertTrue('media_controller' in self.registry.service_list, 'The media_controller should have been '

View File

@ -78,10 +78,11 @@ class TestMediaController(TestCase, TestMixin):
""" """
Test that we don't try to play media when no players available Test that we don't try to play media when no players available
""" """
# GIVEN: A mocked UiStrings, get_media_players, controller, display and service_item # GIVEN: A mocked UiStrings, get_used_players, controller, display and service_item
with patch('openlp.core.ui.media.mediacontroller.get_media_players') as mocked_get_media_players,\ with patch('openlp.core.ui.media.mediacontroller.MediaController._get_used_players') as \
mocked_get_used_players,\
patch('openlp.core.ui.media.mediacontroller.UiStrings') as mocked_uistrings: patch('openlp.core.ui.media.mediacontroller.UiStrings') as mocked_uistrings:
mocked_get_media_players.return_value = ([], '') mocked_get_used_players.return_value = ([])
mocked_ret_uistrings = MagicMock() mocked_ret_uistrings = MagicMock()
mocked_ret_uistrings.Automatic = 1 mocked_ret_uistrings.Automatic = 1
mocked_uistrings.return_value = mocked_ret_uistrings mocked_uistrings.return_value = mocked_ret_uistrings
@ -97,14 +98,14 @@ class TestMediaController(TestCase, TestMixin):
# THEN: it should return False # THEN: it should return False
self.assertFalse(ret, '_check_file_type should return False when no mediaplayers are available.') self.assertFalse(ret, '_check_file_type should return False when no mediaplayers are available.')
@patch('openlp.core.ui.media.mediacontroller.get_media_players') @patch('openlp.core.ui.media.mediacontroller.MediaController._get_used_players')
@patch('openlp.core.ui.media.mediacontroller.UiStrings') @patch('openlp.core.ui.media.mediacontroller.UiStrings')
def check_file_type_no_processor_test(self, mocked_uistrings, mocked_get_media_players): def check_file_type_no_processor_test(self, mocked_uistrings, mocked_get_used_players):
""" """
Test that we don't try to play media when the processor for the service item is None Test that we don't try to play media when the processor for the service item is None
""" """
# GIVEN: A mocked UiStrings, get_media_players, controller, display and service_item # GIVEN: A mocked UiStrings, get_media_players, controller, display and service_item
mocked_get_media_players.return_value = ([], '') mocked_get_used_players.return_value = ([], '')
mocked_ret_uistrings = MagicMock() mocked_ret_uistrings = MagicMock()
mocked_ret_uistrings.Automatic = 1 mocked_ret_uistrings.Automatic = 1
mocked_uistrings.return_value = mocked_ret_uistrings mocked_uistrings.return_value = mocked_ret_uistrings
@ -120,14 +121,14 @@ class TestMediaController(TestCase, TestMixin):
# THEN: it should return False # THEN: it should return False
self.assertFalse(ret, '_check_file_type should return False when the processor for service_item is None.') self.assertFalse(ret, '_check_file_type should return False when the processor for service_item is None.')
@patch('openlp.core.ui.media.mediacontroller.get_media_players') @patch('openlp.core.ui.media.mediacontroller.MediaController._get_used_players')
@patch('openlp.core.ui.media.mediacontroller.UiStrings') @patch('openlp.core.ui.media.mediacontroller.UiStrings')
def check_file_type_automatic_processor_test(self, mocked_uistrings, mocked_get_media_players): def check_file_type_automatic_processor_test(self, mocked_uistrings, mocked_get_used_players):
""" """
Test that we can play media when players are available and we have a automatic processor from the service item Test that we can play media when players are available and we have a automatic processor from the service item
""" """
# GIVEN: A mocked UiStrings, get_media_players, controller, display and service_item # GIVEN: A mocked UiStrings, get_media_players, controller, display and service_item
mocked_get_media_players.return_value = (['vlc', 'webkit'], '') mocked_get_used_players.return_value = (['vlc', 'webkit'])
mocked_ret_uistrings = MagicMock() mocked_ret_uistrings = MagicMock()
mocked_ret_uistrings.Automatic = 1 mocked_ret_uistrings.Automatic = 1
mocked_uistrings.return_value = mocked_ret_uistrings mocked_uistrings.return_value = mocked_ret_uistrings
@ -150,21 +151,21 @@ class TestMediaController(TestCase, TestMixin):
self.assertTrue(ret, '_check_file_type should return True when mediaplayers are available and ' self.assertTrue(ret, '_check_file_type should return True when mediaplayers are available and '
'the service item has an automatic processor.') 'the service item has an automatic processor.')
@patch('openlp.core.ui.media.mediacontroller.get_media_players') @patch('openlp.core.ui.media.mediacontroller.MediaController._get_used_players')
@patch('openlp.core.ui.media.mediacontroller.UiStrings') @patch('openlp.core.ui.media.mediacontroller.UiStrings')
def check_file_type_processor_different_from_available_test(self, mocked_uistrings, mocked_get_media_players): def check_file_type_processor_different_from_available_test(self, mocked_uistrings, mocked_get_used_players):
""" """
Test that we can play media when players available are different from the processor from the service item Test that we can play media when players available are different from the processor from the service item
""" """
# GIVEN: A mocked UiStrings, get_media_players, controller, display and service_item # GIVEN: A mocked UiStrings, get_media_players, controller, display and service_item
mocked_get_media_players.return_value = (['phonon'], '') mocked_get_used_players.return_value = (['system'])
mocked_ret_uistrings = MagicMock() mocked_ret_uistrings = MagicMock()
mocked_ret_uistrings.Automatic = 'automatic' mocked_ret_uistrings.Automatic = 'automatic'
mocked_uistrings.return_value = mocked_ret_uistrings mocked_uistrings.return_value = mocked_ret_uistrings
media_controller = MediaController() media_controller = MediaController()
mocked_phonon = MagicMock() mocked_phonon = MagicMock()
mocked_phonon.video_extensions_list = ['*.mp4'] mocked_phonon.video_extensions_list = ['*.mp4']
media_controller.media_players = {'phonon': mocked_phonon} media_controller.media_players = {'system': mocked_phonon}
mocked_controller = MagicMock() mocked_controller = MagicMock()
mocked_suffix = MagicMock() mocked_suffix = MagicMock()
mocked_suffix.return_value = 'mp4' mocked_suffix.return_value = 'mp4'

View File

@ -380,7 +380,6 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_display.vlc_media_player.set_media.assert_called_with(mocked_vlc_media) mocked_display.vlc_media_player.set_media.assert_called_with(mocked_vlc_media)
mocked_vlc_media.parse.assert_called_with() mocked_vlc_media.parse.assert_called_with()
mocked_volume.assert_called_with(mocked_display, 100) mocked_volume.assert_called_with(mocked_display, 100)
self.assertEqual(10, mocked_controller.media_info.length)
self.assertTrue(result) self.assertTrue(result)
@patch('openlp.core.ui.media.vlcplayer.is_win') @patch('openlp.core.ui.media.vlcplayer.is_win')
@ -426,7 +425,6 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_display.vlc_media_player.set_media.assert_called_with(mocked_vlc_media) mocked_display.vlc_media_player.set_media.assert_called_with(mocked_vlc_media)
mocked_vlc_media.parse.assert_called_with() mocked_vlc_media.parse.assert_called_with()
mocked_volume.assert_called_with(mocked_display, 100) mocked_volume.assert_called_with(mocked_display, 100)
self.assertEqual(10, mocked_controller.media_info.length)
self.assertTrue(result) self.assertTrue(result)
@patch('openlp.core.ui.media.vlcplayer.is_win') @patch('openlp.core.ui.media.vlcplayer.is_win')
@ -472,7 +470,6 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_display.vlc_media_player.set_media.assert_called_with(mocked_vlc_media) mocked_display.vlc_media_player.set_media.assert_called_with(mocked_vlc_media)
mocked_vlc_media.parse.assert_called_with() mocked_vlc_media.parse.assert_called_with()
mocked_volume.assert_called_with(mocked_display, 100) mocked_volume.assert_called_with(mocked_display, 100)
self.assertEqual(10, mocked_controller.media_info.length)
self.assertTrue(result) self.assertTrue(result)
@patch('openlp.core.ui.media.vlcplayer.is_win') @patch('openlp.core.ui.media.vlcplayer.is_win')
@ -628,7 +625,7 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_display.controller = mocked_controller mocked_display.controller = mocked_controller
mocked_display.vlc_media_player.get_media.return_value = mocked_media mocked_display.vlc_media_player.get_media.return_value = mocked_media
vlc_player = VlcPlayer(None) vlc_player = VlcPlayer(None)
vlc_player.state = MediaState.Paused vlc_player.set_state(MediaState.Paused, mocked_display)
# WHEN: play() is called # WHEN: play() is called
with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait, \ with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait, \
@ -638,10 +635,8 @@ class TestVLCPlayer(TestCase, TestMixin):
# THEN: A bunch of things should happen to play the media # THEN: A bunch of things should happen to play the media
mocked_thread.start.assert_called_with() mocked_thread.start.assert_called_with()
self.assertEqual(50, mocked_controller.media_info.length)
mocked_volume.assert_called_with(mocked_display, 100) mocked_volume.assert_called_with(mocked_display, 100)
mocked_controller.seek_slider.setMaximum.assert_called_with(50000) self.assertEqual(MediaState.Playing, vlc_player.get_live_state())
self.assertEqual(MediaState.Playing, vlc_player.state)
mocked_display.vlc_widget.raise_.assert_called_with() mocked_display.vlc_widget.raise_.assert_called_with()
self.assertTrue(result, 'The value returned from play() should be True') self.assertTrue(result, 'The value returned from play() should be True')
@ -661,7 +656,7 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_display = MagicMock() mocked_display = MagicMock()
mocked_display.controller = mocked_controller mocked_display.controller = mocked_controller
vlc_player = VlcPlayer(None) vlc_player = VlcPlayer(None)
vlc_player.state = MediaState.Paused vlc_player.set_state(MediaState.Paused, mocked_display)
# WHEN: play() is called # WHEN: play() is called
with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait, \ with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait, \
@ -695,7 +690,7 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_display = MagicMock() mocked_display = MagicMock()
mocked_display.controller = mocked_controller mocked_display.controller = mocked_controller
vlc_player = VlcPlayer(None) vlc_player = VlcPlayer(None)
vlc_player.state = MediaState.Paused vlc_player.set_state(MediaState.Paused, mocked_display)
# WHEN: play() is called # WHEN: play() is called
with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait, \ with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait, \
@ -709,10 +704,8 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_display.vlc_media_player.play.assert_called_with() 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.audio_set_track.assert_called_with(1)
mocked_display.vlc_media_player.video_set_spu.assert_called_with(1) mocked_display.vlc_media_player.video_set_spu.assert_called_with(1)
self.assertEqual(50, mocked_controller.media_info.length)
mocked_volume.assert_called_with(mocked_display, 100) mocked_volume.assert_called_with(mocked_display, 100)
mocked_controller.seek_slider.setMaximum.assert_called_with(50000) self.assertEqual(MediaState.Playing, vlc_player.get_live_state())
self.assertEqual(MediaState.Playing, vlc_player.state)
mocked_display.vlc_widget.raise_.assert_called_with() mocked_display.vlc_widget.raise_.assert_called_with()
self.assertTrue(result, 'The value returned from play() should be True') self.assertTrue(result, 'The value returned from play() should be True')
@ -739,7 +732,7 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_display.vlc_media.get_state.assert_called_with() mocked_display.vlc_media.get_state.assert_called_with()
mocked_display.vlc_media_player.pause.assert_called_with() mocked_display.vlc_media_player.pause.assert_called_with()
mocked_media_state_wait.assert_called_with(mocked_display, 2) mocked_media_state_wait.assert_called_with(mocked_display, 2)
self.assertEqual(MediaState.Paused, vlc_player.state) self.assertEqual(MediaState.Paused, vlc_player.get_live_state())
@patch('openlp.core.ui.media.vlcplayer.get_vlc') @patch('openlp.core.ui.media.vlcplayer.get_vlc')
def pause_not_playing_test(self, mocked_get_vlc): def pause_not_playing_test(self, mocked_get_vlc):
@ -805,7 +798,7 @@ class TestVLCPlayer(TestCase, TestMixin):
# THEN: A thread should have been started to stop VLC # THEN: A thread should have been started to stop VLC
mocked_threading.Thread.assert_called_with(target=mocked_stop) mocked_threading.Thread.assert_called_with(target=mocked_stop)
mocked_thread.start.assert_called_with() mocked_thread.start.assert_called_with()
self.assertEqual(MediaState.Stopped, vlc_player.state) self.assertEqual(MediaState.Stopped, vlc_player.get_live_state())
def volume_test(self): def volume_test(self):
""" """
@ -900,10 +893,10 @@ class TestVLCPlayer(TestCase, TestMixin):
# WHEN: reset() is called # WHEN: reset() is called
vlc_player.reset(mocked_display) vlc_player.reset(mocked_display)
# THEN: The media should be stopped and invsibile # THEN: The media should be stopped and invisible
mocked_display.vlc_media_player.stop.assert_called_with() mocked_display.vlc_media_player.stop.assert_called_with()
mocked_display.vlc_widget.setVisible.assert_called_with(False) mocked_display.vlc_widget.setVisible.assert_called_with(False)
self.assertEqual(MediaState.Off, vlc_player.state) self.assertEqual(MediaState.Off, vlc_player.get_live_state())
def set_visible_has_own_widget_test(self): def set_visible_has_own_widget_test(self):
""" """

View File

@ -54,8 +54,6 @@ class MediaPluginTest(TestCase, TestMixin):
media_plugin.initialise() media_plugin.initialise()
# THEN: The settings should be upgraded and the base initialise() method should be called # THEN: The settings should be upgraded and the base initialise() method should be called
mocked_settings.get_files_from_config.assert_called_with(media_plugin)
mocked_settings.setValue.assert_called_with('media/media files', True)
mocked_initialise.assert_called_with() mocked_initialise.assert_called_with()
def test_about_text(self): def test_about_text(self):

View File

@ -0,0 +1,21 @@
# -*- 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 #
###############################################################################

View File

@ -0,0 +1,51 @@
# -*- 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 #
###############################################################################
"""
Package to test the openlp.core.ui.media package.
"""
import os
from unittest import TestCase
from openlp.core.ui.media.vendor.mediainfoWrapper import MediaInfoWrapper
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources', 'media'))
TEST_MEDIA = [['avi_file.avi', 61495], ['mp3_file.mp3', 134426], ['mpg_file.mpg', 9404], ['mp4_file.mp4', 188336]]
class TestMediainfoWrapper(TestCase):
def media_length_test(self):
"""
Test the Media Info basic functionality
"""
for test_data in TEST_MEDIA:
# GIVEN: a media file
full_path = os.path.normpath(os.path.join(TEST_PATH, test_data[0]))
# WHEN the media data is retrieved
results = MediaInfoWrapper.parse(full_path)
# THEN you can determine the run time
self.assertEqual(results.tracks[0].duration, test_data[1], 'The correct duration is returned for ' +
test_data[0])

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.