forked from openlp/openlp
Made it possible to name optical clips, and fixed some issues.
This commit is contained in:
parent
06825e4aeb
commit
2a1a93d319
@ -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
|
||||
|
@ -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]
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user