Stop media crashing

This commit is contained in:
Tim Bentley 2018-11-01 20:51:42 +00:00
parent 26a9b2430d
commit 0694d1b3d0
6 changed files with 121 additions and 119 deletions

View File

@ -616,6 +616,10 @@ class ServiceItem(RegistryProperties):
path_from = frame['path'] path_from = frame['path']
else: else:
path_from = os.path.join(frame['path'], frame['title']) path_from = os.path.join(frame['path'], frame['title'])
if isinstance(path_from, str):
# Handle service files prior to OpenLP 3.0
# Windows can handle both forward and backward slashes, so we use ntpath to get the basename
path_from = Path(path_from)
return path_from return path_from
def remove_frame(self, frame): def remove_frame(self, frame):

View File

@ -24,7 +24,7 @@ The :mod:`~openlp.core.loader` module provides a bootstrap for the singleton cla
""" """
from openlp.core.state import State from openlp.core.state import State
from openlp.core.ui.media import MediaController from openlp.core.ui.media.mediacontroller import MediaController
from openlp.core.lib.pluginmanager import PluginManager from openlp.core.lib.pluginmanager import PluginManager
from openlp.core.display.renderer import Renderer from openlp.core.display.renderer import Renderer
from openlp.core.lib.imagemanager import ImageManager from openlp.core.lib.imagemanager import ImageManager

View File

@ -33,7 +33,7 @@ except ImportError:
pymediainfo_available = False pymediainfo_available = False
from subprocess import check_output from subprocess import check_output
from pathlib import Path
from PyQt5 import QtCore, QtWidgets from PyQt5 import QtCore, QtWidgets
from openlp.core.state import State from openlp.core.state import State
@ -81,7 +81,6 @@ 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 :param event: The triggering event
""" """
QtWidgets.QSlider.mousePressEvent(self, event) QtWidgets.QSlider.mousePressEvent(self, event)
@ -385,7 +384,7 @@ class MediaController(RegistryBase, LogMixin, 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.
""" """
is_valid = False is_valid = True
controller = self.display_controllers[source] controller = self.display_controllers[source]
# stop running videos # stop running videos
self.media_reset(controller) self.media_reset(controller)
@ -397,7 +396,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
if service_item.is_capable(ItemCapabilities.HasBackgroundAudio): if service_item.is_capable(ItemCapabilities.HasBackgroundAudio):
controller.media_info.file_info = service_item.background_audio controller.media_info.file_info = service_item.background_audio
else: else:
controller.media_info.file_info = [QtCore.QFileInfo(service_item.get_frame_path())] controller.media_info.file_info = [service_item.get_frame_path()]
display = self._define_display(controller) display = self._define_display(controller)
if controller.is_live: if controller.is_live:
# if this is an optical device use special handling # if this is an optical device use special handling
@ -465,18 +464,14 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
""" """
Uses Media Info to obtain the media length Uses Media Info to obtain the media length
:param service_item: The ServiceItem containing the details to be played. :param media_path: The file path to be checked..
""" """
media_info = MediaInfo()
media_info.volume = 0
media_info.file_info = media_path
filename = media_path
if pymediainfo.MediaInfo.can_parse(): if pymediainfo.MediaInfo.can_parse():
media_data = pymediainfo.MediaInfo.parse(filename) media_data = pymediainfo.MediaInfo.parse(media_path)
else: else:
xml = check_output(['mediainfo', '-f', '--Output=XML', '--Inform=OLDXML', filename]) xml = check_output(['mediainfo', '-f', '--Output=XML', '--Inform=OLDXML', media_path])
if not xml.startswith(b'<?xml'): if not xml.startswith(b'<?xml'):
xml = check_output(['mediainfo', '-f', '--Output=XML', filename]) xml = check_output(['mediainfo', '-f', '--Output=XML', media_path])
media_data = pymediainfo.MediaInfo(xml.decode("utf-8")) media_data = pymediainfo.MediaInfo(xml.decode("utf-8"))
# duration returns in milli seconds # duration returns in milli seconds
return media_data.tracks[0].duration return media_data.tracks[0].duration
@ -550,30 +545,33 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
:param display: Which display to use :param display: Which display to use
:param service_item: The ServiceItem containing the details to be played. :param service_item: The ServiceItem containing the details to be played.
""" """
if controller.media_info.file_info.isFile(): for file in controller.media_info.file_info:
suffix = '*.%s' % controller.media_info.file_info.suffix().lower() if file.is_file:
player = self.media_players suffix = '*%s' % file.suffix.lower()
if suffix in player.video_extensions_list: player = self.media_players
if not controller.media_info.is_background or controller.media_info.is_background and \ file = str(file)
player.can_background: if suffix in player.video_extensions_list:
if not controller.media_info.is_background or controller.media_info.is_background and \
player.can_background:
self.resize(display, player)
if player.load(display, file):
self.current_media_players[controller.controller_type] = player
controller.media_info.media_type = MediaType.Video
return True
if suffix in player.audio_extensions_list:
if player.load(display, file):
self.current_media_players[controller.controller_type] = player
controller.media_info.media_type = MediaType.Audio
return True
else:
player = self.media_players
file = str(file)
if player.can_folder:
self.resize(display, player) self.resize(display, player)
if player.load(display): if player.load(display, file):
self.current_media_players[controller.controller_type] = player self.current_media_players[controller.controller_type] = player
controller.media_info.media_type = MediaType.Video controller.media_info.media_type = MediaType.Video
return True return True
if suffix in player.audio_extensions_list:
if player.load(display):
self.current_media_players[controller.controller_type] = player
controller.media_info.media_type = MediaType.Audio
return True
else:
player = self.media_players
if player.can_folder:
self.resize(display, player)
if player.load(display):
self.current_media_players[controller.controller_type] = player
controller.media_info.media_type = MediaType.Video
return True
# no valid player found # no valid player found
return False return False

View File

@ -196,19 +196,19 @@ class VlcPlayer(MediaPlayer):
""" """
return get_vlc() is not None return get_vlc() is not None
def load(self, display): def load(self, display, file):
""" """
Load a video into VLC Load a video into VLC
:param display: The display where the media is :param display: The display where the media is
:param file: file to be played
:return: :return:
""" """
vlc = get_vlc() vlc = get_vlc()
log.debug('load vid in Vlc Controller') log.debug('load vid in Vlc Controller')
controller = display.controller controller = display.controller
volume = controller.media_info.volume volume = controller.media_info.volume
file_path = str(controller.media_info.file_info.absoluteFilePath()) path = os.path.normcase(file)
path = os.path.normcase(file_path)
# create the media # create the media
if controller.media_info.media_type == MediaType.CD: if controller.media_info.media_type == MediaType.CD:
if is_win(): if is_win():

View File

@ -48,13 +48,13 @@ from openlp.core.widgets.views import ListPreviewWidget
# Threshold which has to be trespassed to toggle. # Threshold which has to be trespassed to toggle.
HIDE_MENU_THRESHOLD = 27 HIDE_MENU_THRESHOLD = 27
AUDIO_TIME_LABEL_STYLESHEET = 'background-color: palette(background); ' \ # AUDIO_TIME_LABEL_STYLESHEET = 'background-color: palette(background); ' \
'border-top-color: palette(shadow); ' \ # 'border-top-color: palette(shadow); ' \
'border-left-color: palette(shadow); ' \ # 'border-left-color: palette(shadow); ' \
'border-bottom-color: palette(light); ' \ # 'border-bottom-color: palette(light); ' \
'border-right-color: palette(light); ' \ # 'border-right-color: palette(light); ' \
'border-radius: 3px; border-style: inset; ' \ # 'border-radius: 3px; border-style: inset; ' \
'border-width: 1; font-family: monospace; margin: 2px;' # 'border-width: 1; font-family: monospace; margin: 2px;'
NARROW_MENU = [ NARROW_MENU = [
'hide_menu' 'hide_menu'
@ -64,10 +64,10 @@ LOOP_LIST = [
'loop_separator', 'loop_separator',
'delay_spin_box' 'delay_spin_box'
] ]
AUDIO_LIST = [ # AUDIO_LIST = [
'audioPauseItem', # 'audioPauseItem',
'audio_time_label' # 'audio_time_label'
] # ]
WIDE_MENU = [ WIDE_MENU = [
'blank_screen_button', 'blank_screen_button',
'theme_screen_button', 'theme_screen_button',
@ -84,6 +84,7 @@ class DisplayController(QtWidgets.QWidget):
""" """
Controller is a general display controller widget. Controller is a general display controller widget.
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
""" """
Set up the general Controller. Set up the general Controller.
@ -140,6 +141,7 @@ class SlideController(DisplayController, LogMixin, RegistryProperties):
SlideController is the slide controller widget. This widget is what the SlideController is the slide controller widget. This widget is what the
user uses to control the displaying of verses/slides/etc on the screen. user uses to control the displaying of verses/slides/etc on the screen.
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
""" """
Set up the Slide Controller. Set up the Slide Controller.
@ -371,7 +373,7 @@ class SlideController(DisplayController, LogMixin, RegistryProperties):
self.preview_frame.setMinimumHeight(100) self.preview_frame.setMinimumHeight(100)
self.preview_frame.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Ignored, self.preview_frame.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Ignored,
QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Ignored,
QtWidgets.QSizePolicy.Label)) QtWidgets.QSizePolicy.Label))
self.preview_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) self.preview_frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.preview_frame.setFrameShadow(QtWidgets.QFrame.Sunken) self.preview_frame.setFrameShadow(QtWidgets.QFrame.Sunken)
self.preview_frame.setObjectName('preview_frame') self.preview_frame.setObjectName('preview_frame')
@ -409,7 +411,7 @@ class SlideController(DisplayController, LogMixin, RegistryProperties):
{'key': 'C', 'configurable': True, 'text': translate('OpenLP.SlideController', 'Go to "Chorus"')}, {'key': 'C', 'configurable': True, 'text': translate('OpenLP.SlideController', 'Go to "Chorus"')},
{'key': 'B', 'configurable': True, 'text': translate('OpenLP.SlideController', 'Go to "Bridge"')}, {'key': 'B', 'configurable': True, 'text': translate('OpenLP.SlideController', 'Go to "Bridge"')},
{'key': 'P', 'configurable': True, {'key': 'P', 'configurable': True,
'text': translate('OpenLP.SlideController', 'Go to "Pre-Chorus"')}, 'text': translate('OpenLP.SlideController', 'Go to "Pre-Chorus"')},
{'key': 'I', 'configurable': True, 'text': translate('OpenLP.SlideController', 'Go to "Intro"')}, {'key': 'I', 'configurable': True, 'text': translate('OpenLP.SlideController', 'Go to "Intro"')},
{'key': 'E', 'configurable': True, 'text': translate('OpenLP.SlideController', 'Go to "Ending"')}, {'key': 'E', 'configurable': True, 'text': translate('OpenLP.SlideController', 'Go to "Ending"')},
{'key': 'O', 'configurable': True, 'text': translate('OpenLP.SlideController', 'Go to "Other"')} {'key': 'O', 'configurable': True, 'text': translate('OpenLP.SlideController', 'Go to "Other"')}
@ -475,6 +477,7 @@ class SlideController(DisplayController, LogMixin, RegistryProperties):
This empty class is mostly just to satisfy Python, PEP8 and PyCharm This empty class is mostly just to satisfy Python, PEP8 and PyCharm
""" """
pass pass
is_songs_plugin_available = False is_songs_plugin_available = False
sender_name = self.sender().objectName() sender_name = self.sender().objectName()
verse_type = sender_name[15:] if sender_name[:15] == 'shortcutAction_' else '' verse_type = sender_name[15:] if sender_name[:15] == 'shortcutAction_' else ''
@ -597,8 +600,8 @@ class SlideController(DisplayController, LogMixin, RegistryProperties):
self.display.setup() self.display.setup()
if self.is_live: if self.is_live:
self.__add_actions_to_widget(self.display) self.__add_actions_to_widget(self.display)
if self.display.audio_player: # if self.display.audio_player:
self.display.audio_player.position_changed.connect(self.on_audio_time_remaining) # self.display.audio_player.position_changed.connect(self.on_audio_time_remaining)
# The SlidePreview's ratio. # The SlidePreview's ratio.
try: try:
self.ratio = self.screens.current['size'].width() / self.screens.current['size'].height() self.ratio = self.screens.current['size'].width() / self.screens.current['size'].height()
@ -610,7 +613,7 @@ class SlideController(DisplayController, LogMixin, RegistryProperties):
self.preview_display.setup() self.preview_display.setup()
service_item = ServiceItem() service_item = ServiceItem()
self.preview_display.web_view.setHtml(build_html(service_item, self.preview_display.screen, None, self.is_live, self.preview_display.web_view.setHtml(build_html(service_item, self.preview_display.screen, None, self.is_live,
plugins=self.plugin_manager.plugins)) plugins=self.plugin_manager.plugins))
self.media_controller.setup_display(self.preview_display, True) self.media_controller.setup_display(self.preview_display, True)
if self.service_item: if self.service_item:
self.refresh_service_item() self.refresh_service_item()
@ -871,29 +874,29 @@ class SlideController(DisplayController, LogMixin, RegistryProperties):
self.slide_list = {} self.slide_list = {}
if self.is_live: if self.is_live:
self.song_menu.menu().clear() self.song_menu.menu().clear()
if self.display.audio_player: # if self.display.audio_player:
self.display.audio_player.reset() # self.display.audio_player.reset()
self.set_audio_items_visibility(False) # self.set_audio_items_visibility(False)
# self.audio_pause_item.setChecked(False) # self.audio_pause_item.setChecked(False)
# If the current item has background audio # If the current item has background audio
if self.service_item.is_capable(ItemCapabilities.HasBackgroundAudio): if self.service_item.is_capable(ItemCapabilities.HasBackgroundAudio):
self.on_media_start(service_item) self.on_media_start(service_item)
#self.log_debug('Starting to play...') # self.log_debug('Starting to play...')
#self.display.audio_player.add_to_playlist(self.service_item.background_audio) # self.display.audio_player.add_to_playlist(self.service_item.background_audio)
#self.track_menu.clear() # self.track_menu.clear()
#for counter in range(len(self.service_item.background_audio)): # for counter in range(len(self.service_item.background_audio)):
# action = self.track_menu.addAction( # action = self.track_menu.addAction(
# os.path.basename(str(self.service_item.background_audio[counter]))) # os.path.basename(str(self.service_item.background_audio[counter])))
# action.setData(counter) # action.setData(counter)
# action.triggered.connect(self.on_track_triggered) # action.triggered.connect(self.on_track_triggered)
#self.display.audio_player.repeat = \ # self.display.audio_player.repeat = \
# Settings().value(self.main_window.general_settings_section + '/audio repeat list') # Settings().value(self.main_window.general_settings_section + '/audio repeat list')
#if Settings().value(self.main_window.general_settings_section + '/audio start paused'): # if Settings().value(self.main_window.general_settings_section + '/audio start paused'):
# self.audio_pause_item.setChecked(True) # self.audio_pause_item.setChecked(True)
# self.display.audio_player.pause() # self.display.audio_player.pause()
#else: # else:
# self.display.audio_player.play() # self.display.audio_player.play()
self.set_audio_items_visibility(True) # self.set_audio_items_visibility(True)
row = 0 row = 0
width = self.main_window.control_splitter.sizes()[self.split] width = self.main_window.control_splitter.sizes()[self.split]
for frame_number, frame in enumerate(self.service_item.get_frames()): for frame_number, frame in enumerate(self.service_item.get_frames()):
@ -1355,24 +1358,24 @@ class SlideController(DisplayController, LogMixin, RegistryProperties):
self.play_slides_once.setText(UiStrings().PlaySlidesToEnd) self.play_slides_once.setText(UiStrings().PlaySlidesToEnd)
self.on_toggle_loop() self.on_toggle_loop()
def set_audio_items_visibility(self, visible): # def set_audio_items_visibility(self, visible):
""" # """
Set the visibility of the audio stuff # Set the visibility of the audio stuff
""" # """
self.toolbar.set_widget_visible(AUDIO_LIST, visible) # self.toolbar.set_widget_visible(AUDIO_LIST, visible)
def set_audio_pause_clicked(self, checked): # def set_audio_pause_clicked(self, checked):
""" # """
Pause the audio player # Pause the audio player
:param checked: is the check box checked. # :param checked: is the check box checked.
""" # """
if not self.audio_pause_item.isVisible(): # if not self.audio_pause_item.isVisible():
return # return
if checked: # if checked:
self.display.audio_player.pause() # self.display.audio_player.pause()
else: # else:
self.display.audio_player.play() # self.display.audio_player.play()
def timerEvent(self, event): def timerEvent(self, event):
""" """
@ -1509,29 +1512,29 @@ class SlideController(DisplayController, LogMixin, RegistryProperties):
else: else:
return None return None
def on_next_track_clicked(self): # def on_next_track_clicked(self):
""" # """
Go to the next track when next is clicked # Go to the next track when next is clicked
""" # """
self.display.audio_player.next() # self.display.audio_player.next()
#
def on_audio_time_remaining(self, time): # def on_audio_time_remaining(self, time):
""" # """
Update how much time is remaining # Update how much time is remaining
#
:param time: the time remaining # :param time: the time remaining
""" # """
seconds = (self.display.audio_player.player.duration() - self.display.audio_player.player.position()) // 1000 # seconds = (self.display.audio_player.player.duration() - self.display.audio_player.player.position()) // 1000
minutes = seconds // 60 # minutes = seconds // 60
seconds %= 60 # seconds %= 60
self.audio_time_label.setText(' %02d:%02d ' % (minutes, seconds)) # self.audio_time_label.setText(' %02d:%02d ' % (minutes, seconds))
#
def on_track_triggered(self, field=None): # def on_track_triggered(self, field=None):
""" # """
Start playing a track # Start playing a track
""" # """
action = self.sender() # action = self.sender()
self.display.audio_player.go_to(action.data()) # self.display.audio_player.go_to(action.data())
class PreviewController(RegistryBase, SlideController): class PreviewController(RegistryBase, SlideController):

View File

@ -179,8 +179,6 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
:param remote: Triggered from remote :param remote: Triggered from remote
:param context: Why is it being generated :param context: Why is it being generated
""" """
if State().check_active_dependency():
a=1
if item is None: if item is None:
item = self.list_view.currentItem() item = self.list_view.currentItem()
if item is None: if item is None:
@ -197,7 +195,7 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
translate('MediaPlugin.MediaItem', translate('MediaPlugin.MediaItem',
'The optical disc {name} is no longer available.').format(name=name)) 'The optical disc {name} is no longer available.').format(name=name))
return False return False
service_item.processor = self.display_type_combo_box.currentText() service_item.processor = 'vlc'
service_item.add_from_command(filename, name, CLAPPERBOARD) service_item.add_from_command(filename, name, CLAPPERBOARD)
service_item.title = clip_name service_item.title = clip_name
# Set the length # Set the length
@ -218,8 +216,7 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
service_item.processor = 'vlc' service_item.processor = 'vlc'
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 not self.media_controller.media_length(service_item): service_item.set_media_length(self.media_controller.media_length(filename))
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)