From 2a1a93d319221f5335e7451d12b34400ae71e312 Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Sat, 14 Jun 2014 23:48:07 +0200 Subject: [PATCH] Made it possible to name optical clips, and fixed some issues. --- openlp/core/ui/media/__init__.py | 23 ++++++-- openlp/core/ui/media/mediacontroller.py | 17 +++++- .../media/forms/mediaclipselectorform.py | 54 +++++++++++++++---- openlp/plugins/media/lib/mediaitem.py | 47 ++++++++-------- 4 files changed, 105 insertions(+), 36 deletions(-) diff --git a/openlp/core/ui/media/__init__.py b/openlp/core/ui/media/__init__.py index 1d8ee3a4c..9248d03cf 100644 --- a/openlp/core/ui/media/__init__.py +++ b/openlp/core/ui/media/__init__.py @@ -110,6 +110,7 @@ def set_media_players(players_list, overridden_player='auto'): players = players.replace(overridden_player, '[%s]' % overridden_player) Settings().setValue('media/players', players) + def parse_optical_path(input): """ Split the optical path info. @@ -123,10 +124,24 @@ def parse_optical_path(input): subtitle_track = int(clip_info[3]) start = float(clip_info[4]) end = float(clip_info[5]) - filename = clip_info[6] - if len(clip_info) > 7: - filename += clip_info[7] - return filename, title, audio_track, subtitle_track, start, end + clip_name = clip_info[6] + filename = clip_info[7] + # Windows path usually contains a colon after the drive letter + if len(clip_info) > 8: + filename += clip_info[8] + return filename, title, audio_track, subtitle_track, start, end, clip_name + + +def format_milliseconds(milliseconds): + """ + Format milliseconds into a human readable time string. + :param milliseconds: Milliseconds to format + :return: Time string in format: hh.mm.ss,ttt + """ + seconds, millis = divmod(milliseconds, 1000) + minutes, seconds = divmod(seconds, 60) + hours, minutes = divmod(minutes, 60) + return "%02d:%02d:%02d,%03d" % (hours, minutes, seconds, millis) from .mediacontroller import MediaController from .playertab import PlayerTab diff --git a/openlp/core/ui/media/mediacontroller.py b/openlp/core/ui/media/mediacontroller.py index b25556e73..a62a9465c 100644 --- a/openlp/core/ui/media/mediacontroller.py +++ b/openlp/core/ui/media/mediacontroller.py @@ -373,7 +373,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties): if service_item.is_capable(ItemCapabilities.IsOptical): log.debug('video is optical and live') path = service_item.get_frame_path() - (name, title, audio_track, subtitle_track, start, end) = parse_optical_path(path) + (name, title, audio_track, subtitle_track, start, end, clip_name) = parse_optical_path(path) is_valid = self.media_setup_optical(name, title, audio_track, subtitle_track, start, end, display, controller) else: @@ -392,7 +392,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties): if service_item.is_capable(ItemCapabilities.IsOptical): log.debug('video is optical and preview') path = service_item.get_frame_path() - (name, title, audio_track, subtitle_track, start, end) = parse_optical_path(path) + (name, title, audio_track, subtitle_track, start, end, clip_name) = parse_optical_path(path) is_valid = self.media_setup_optical(name, title, audio_track, subtitle_track, start, end, display, controller) else: @@ -456,6 +456,19 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties): return True def media_setup_optical(self, filename, title, audio_track, subtitle_track, start, end, display, controller): + """ + Setup playback of optical media + + :param filename: Path of the optical device/drive. + :param title: The main/title track to play. + :param audio_track: The audio track to play. + :param subtitle_track: The subtitle track to play. + :param start: Start position in miliseconds. + :param end: End position in miliseconds. + :param display: The display to play the media. + :param controller: The media contraoller. + :return: True if setup succeded else False. + """ log.debug('media_setup_optical') if controller is None: controller = self.display_controllers[DisplayControllerType.Plugin] diff --git a/openlp/plugins/media/forms/mediaclipselectorform.py b/openlp/plugins/media/forms/mediaclipselectorform.py index 82b3cd9ea..06f583a3e 100644 --- a/openlp/plugins/media/forms/mediaclipselectorform.py +++ b/openlp/plugins/media/forms/mediaclipselectorform.py @@ -43,6 +43,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.common import translate from openlp.plugins.media.forms.mediaclipselectordialog import Ui_MediaClipSelector from openlp.core.lib.ui import critical_error_message_box +from openlp.core.ui.media import format_milliseconds try: from openlp.core.ui.media.vendor import vlc except (ImportError, NameError, NotImplementedError): @@ -156,7 +157,7 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector): def detect_audio_cd(self, path): """ Detects is the given path is an audio CD - + :param path: Path to the device to be tested. :return: True if it was an audio CD else False. """ @@ -166,7 +167,7 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector): self.vlc_media_player.play() # Wait for media to start playing. In this case VLC actually returns an error. self.media_state_wait(vlc.State.Playing) - self.vlc_media_player.pause() + self.vlc_media_player.set_pause(1) # If subitems exists, this is a CD self.audio_cd_tracks = self.vlc_media.subitems() if not self.audio_cd_tracks or self.audio_cd_tracks.count() < 1: @@ -231,7 +232,7 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector): 'VLC player failed playing the media')) self.toggle_disable_load_media(False) return - self.vlc_media_player.pause() + self.vlc_media_player.set_pause(1) self.vlc_media_player.set_time(0) if not self.audio_cd: # Get titles, insert in combobox @@ -247,6 +248,7 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector): # Enable audio track combobox if anything is in it if len(titles) > 0: self.title_combo_box.setDisabled(False) + self.vlc_media_player.set_pause(1) self.toggle_disable_load_media(False) @QtCore.pyqtSlot(bool) @@ -367,7 +369,7 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector): if not self.media_state_wait(vlc.State.Playing): return # pause - self.vlc_media_player.pause() + self.vlc_media_player.set_pause(1) self.vlc_media_player.set_time(0) self.vlc_media_player.audio_set_mute(False) self.toggle_disable_player(False) @@ -379,7 +381,7 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector): if not self.media_state_wait(vlc.State.Playing): return # pause - self.vlc_media_player.pause() + self.vlc_media_player.set_pause(1) self.vlc_media_player.set_time(0) # Get audio tracks, insert in combobox audio_tracks = self.vlc_media_player.audio_get_track_description() @@ -520,12 +522,38 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector): path = self.media_path_combobox.currentText() optical = '' if self.audio_cd: - optical = 'optical:' + str(title) + ':-1:-1:' + str(start_time_ms) + ':' + str(end_time_ms) + ':' + path + optical = 'optical:' + str(title) + ':-1:-1:' + str(start_time_ms) + ':' + str(end_time_ms) + ':' else: audio_track = self.audio_tracks_combobox.itemData(self.audio_tracks_combobox.currentIndex()) subtitle_track = self.subtitle_tracks_combobox.itemData(self.subtitle_tracks_combobox.currentIndex()) optical = 'optical:' + str(title) + ':' + str(audio_track) + ':' + str(subtitle_track) + ':' + str( - start_time_ms) + ':' + str(end_time_ms) + ':' + path + start_time_ms) + ':' + str(end_time_ms) + ':' + # Ask for an alternative name for the mediaclip + while True: + new_optical_name, ok = QtGui.QInputDialog.getText(self, translate('MediaPlugin.MediaClipSelectorForm', + 'Set name of mediaclip'), + translate('MediaPlugin.MediaClipSelectorForm', + 'Name of mediaclip:'), + QtGui.QLineEdit.Normal) + # User pressed cancel, don't save the clip + if not ok: + return + # User pressed ok, but the input text is blank + if not new_optical_name: + critical_error_message_box(translate('MediaPlugin.MediaClipSelectorForm', + 'Enter a valid name or cancel'), + translate('MediaPlugin.MediaClipSelectorForm', + 'Enter a valid name or cancel')) + # The entered new name contains a colon, which we don't allow because colons is used to seperate clip info + elif new_optical_name.find(':') >= 0: + critical_error_message_box(translate('MediaPlugin.MediaClipSelectorForm', 'Invalid character'), + translate('MediaPlugin.MediaClipSelectorForm', + 'The name of the mediaclip must not contain the character ":"')) + # New name entered and we use it + else: + break + # Append the new name to the optical string and the path + optical += new_optical_name + ':' + path self.media_item.add_optical_clip(optical) def media_state_wait(self, media_state): @@ -585,7 +613,7 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector): log.debug('could not use udisks, will try udisks2') udev_manager_obj = bus.get_object('org.freedesktop.UDisks2', '/org/freedesktop/UDisks2') udev_manager = dbus.Interface(udev_manager_obj, 'org.freedesktop.DBus.ObjectManager') - for k,v in udev_manager.GetManagedObjects().items(): + for k, v in udev_manager.GetManagedObjects().items(): drive_info = v.get('org.freedesktop.UDisks2.Drive', {}) drive_props = drive_info.get('MediaCompatibility') if drive_props and any('optical' in prop for prop in drive_props): @@ -593,7 +621,8 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector): if dbus.String('org.freedesktop.UDisks2.Block') in device: if device[dbus.String('org.freedesktop.UDisks2.Block')][dbus.String('Drive')] == k: block_file = '' - for c in device[dbus.String('org.freedesktop.UDisks2.Block')][dbus.String('PreferredDevice')]: + for c in device[dbus.String('org.freedesktop.UDisks2.Block')][ + dbus.String('PreferredDevice')]: if chr(c) != '\x00': block_file += chr(c) self.media_path_combobox.addItem(block_file) @@ -605,5 +634,12 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector): if volume.startswith('.'): continue dirs = os.listdir('/Volumes/' + volume) + # Detect DVD if 'VIDEO_TS' in dirs: self.media_path_combobox.addItem('/Volumes/' + volume) + # Detect audio cd + files = [f for f in dirs if os.path.isfile(f)] + for file in files: + if file.endswith('aiff'): + self.media_path_combobox.addItem('/Volumes/' + volume) + break diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index ca8690d75..a47609ea1 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -39,7 +39,7 @@ from openlp.core.lib import ItemCapabilities, MediaManagerItem, MediaType, Servi build_icon, check_item_selected from openlp.core.lib.ui import critical_error_message_box, create_horizontal_adjusting_combo_box from openlp.core.ui import DisplayController, Display, DisplayControllerType -from openlp.core.ui.media import get_media_players, set_media_players, parse_optical_path +from openlp.core.ui.media import get_media_players, set_media_players, parse_optical_path, format_milliseconds from openlp.core.utils import get_locale_key from openlp.core.ui.media.vlcplayer import VLC_AVAILABLE if VLC_AVAILABLE: @@ -125,9 +125,20 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties): """ Adds buttons to the start of the header bar. """ - self.load_optical = self.toolbar.add_toolbar_action('load_optical', icon=OPTICAL_ICON, text='Load CD/DVD', - tooltip='Load CD/DVD', triggers=self.on_load_optical) - if not VLC_AVAILABLE: + print(get_media_players()[0]) + if 'vlc' in get_media_players()[0]: + diable_optical_button_text = False + optical_button_text = translate('MediaPlugin.MediaItem', 'Load CD/DVD') + optical_button_tooltip = translate('MediaPlugin.MediaItem', 'Load CD/DVD') + else: + diable_optical_button_text = True + optical_button_text = translate('MediaPlugin.MediaItem', 'Load CD/DVD') + optical_button_tooltip = translate('MediaPlugin.MediaItem', + 'Load CD/DVD - only supported when VLC is installed and enabled') + self.load_optical = self.toolbar.add_toolbar_action('load_optical', icon=OPTICAL_ICON, text=optical_button_text, + tooltip=optical_button_tooltip, + triggers=self.on_load_optical) + if diable_optical_button_text: self.load_optical.setDisabled(True) def add_end_header_bar(self): @@ -224,7 +235,7 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties): filename = item.data(QtCore.Qt.UserRole) # Special handling if the filename is a optical clip if filename.startswith('optical:'): - (name, title, audio_track, subtitle_track, start, end) = parse_optical_path(filename) + (name, title, audio_track, subtitle_track, start, end, clip_name) = parse_optical_path(filename) if not os.path.exists(name): if not remote: # Optical disc is no longer present @@ -234,7 +245,7 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties): return False service_item.processor = self.display_type_combo_box.currentText() service_item.add_from_command(filename, name, CLAPPERBOARD) - service_item.title = name + '@' + self.format_milliseconds(start) + '-' + self.format_milliseconds(end) + service_item.title = clip_name # Only set start and end times if going to a service #if context == ServiceItemContext.Service: # Set the length @@ -345,12 +356,11 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties): track_info = QtCore.QFileInfo(track) if track.startswith('optical:'): # Handle optical based item - (file_name, title, audio_track, subtitle_track, start, end) = parse_optical_path(track) - optical = file_name + '@' + self.format_milliseconds(start) + '-' + self.format_milliseconds(end) - item_name = QtGui.QListWidgetItem(optical) + (file_name, title, audio_track, subtitle_track, start, end, clip_name) = parse_optical_path(track) + item_name = QtGui.QListWidgetItem(clip_name) item_name.setIcon(OPTICAL_ICON) item_name.setData(QtCore.Qt.UserRole, track) - item_name.setToolTip(optical) + item_name.setToolTip(file_name + '@' + format_milliseconds(start) + '-' + format_milliseconds(end)) elif not os.path.exists(track): # File doesn't exist, mark as error. file_name = os.path.split(str(track))[1] @@ -417,17 +427,12 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties): :param optical: The clip to add. """ full_list = self.get_file_list() + # If the clip already is in the media list it isn't added and an error message is displayed. + if optical in full_list: + critical_error_message_box(translate('MediaPlugin.MediaItem', 'Mediaclip already saved'), + translate('MediaPlugin.MediaItem', 'This mediaclip has already been saved')) + return + # Append the optical string to the media list full_list.append(optical) self.load_list([optical]) Settings().setValue(self.settings_section + '/media files', self.get_file_list()) - - def format_milliseconds(self, milliseconds): - """ - Format milliseconds into a human readable time string. - :param milliseconds: Milliseconds to format - :return: Time string in format: hh.mm.ss,ttt - """ - seconds, millis = divmod(milliseconds, 1000) - minutes, seconds = divmod(seconds, 60) - hours, minutes = divmod(minutes, 60) - return "%02d:%02d:%02d,%03d" % (hours, minutes, seconds, millis)