Made it possible to save and load optical clip to servicefile. Also some pep8 and general cleanup.

This commit is contained in:
Tomas Groth 2014-03-08 21:14:04 +00:00
parent e6f23324ba
commit 3988aacfbd
5 changed files with 167 additions and 82 deletions

View File

@ -435,7 +435,10 @@ class ServiceItem(object):
for text_image in serviceitem['serviceitem']['data']:
if not self.title:
self.title = text_image['title']
if path:
if self.is_capable(ItemCapabilities.IsOptical):
self.has_original_files = False
self.add_from_command(text_image['path'], text_image['title'], text_image['image'])
elif path:
self.has_original_files = False
self.add_from_command(path, text_image['title'], text_image['image'])
else:
@ -446,7 +449,7 @@ class ServiceItem(object):
"""
Returns the title of the service item.
"""
if self.is_text():
if self.is_text() or self.is_capable(ItemCapabilities.IsOptical):
return self.title
else:
if len(self._raw_frames) > 1:
@ -516,7 +519,8 @@ class ServiceItem(object):
"""
Confirms if the ServiceItem uses a file
"""
return self.service_item_type == ServiceItemType.Image or self.service_item_type == ServiceItemType.Command
return self.service_item_type == ServiceItemType.Image or \
(self.service_item_type == ServiceItemType.Command and not self.is_capable(ItemCapabilities.IsOptical))
def is_text(self):
"""
@ -646,15 +650,20 @@ class ServiceItem(object):
self.is_valid = False
break
elif self.is_command():
file_name = os.path.join(frame['path'], frame['title'])
if not os.path.exists(file_name):
self.is_valid = False
break
if suffix_list and not self.is_text():
file_suffix = frame['title'].split('.')[-1]
if file_suffix.lower() not in suffix_list:
if self.is_capable(ItemCapabilities.IsOptical):
if not os.path.exists(frame['title']):
self.is_valid = False
break
else:
file_name = os.path.join(frame['path'], frame['title'])
if not os.path.exists(file_name):
self.is_valid = False
break
if suffix_list and not self.is_text():
file_suffix = frame['title'].split('.')[-1]
if file_suffix.lower() not in suffix_list:
self.is_valid = False
break
def _get_renderer(self):
"""

View File

@ -102,11 +102,8 @@ def set_media_players(players_list, overridden_player='auto'):
This method saves the configured media players and overridden player to the
settings
``players_list``
A list with all active media players.
``overridden_player``
Here an special media player is chosen for all media actions.
:param players_list: A list with all active media players.
:param overridden_player: Here an special media player is chosen for all media actions.
"""
log.debug('set_media_players')
players = ','.join(players_list)
@ -114,6 +111,24 @@ 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.
:param input: The string to parse
:return: The elements extracted from the string: filename, title, audio_track, subtitle_track, start, end
"""
clip_info = input.split(sep=':')
title = int(clip_info[1])
audio_track = int(clip_info[2])
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
from .mediacontroller import MediaController
from .playertab import PlayerTab

View File

@ -38,7 +38,7 @@ from PyQt4 import QtCore, QtGui
from openlp.core.common import Registry, RegistryMixin, Settings, UiStrings, translate
from openlp.core.lib import OpenLPToolbar, ItemCapabilities
from openlp.core.lib.ui import critical_error_message_box
from openlp.core.ui.media import MediaState, MediaInfo, MediaType, get_media_players, set_media_players
from openlp.core.ui.media import MediaState, MediaInfo, MediaType, get_media_players, set_media_players, parse_optical_path
from openlp.core.ui.media.mediaplayer import MediaPlayer
from openlp.core.common import AppLocation
from openlp.core.ui import DisplayControllerType
@ -397,7 +397,7 @@ class MediaController(object):
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) = self.parse_optical_path(path)
(name, title, audio_track, subtitle_track, start, end) = parse_optical_path(path)
is_valid = self.media_setup_optical(name, title, audio_track, subtitle_track, start, end, display, controller)
else :
log.debug('video is not optical and live')
@ -415,7 +415,7 @@ class MediaController(object):
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) = self.parse_optical_path(path)
(name, title, audio_track, subtitle_track, start, end) = parse_optical_path(path)
is_valid = self.media_setup_optical(name, title, audio_track, subtitle_track, start, end, display, controller)
else :
log.debug('video is not optical and preview')
@ -819,16 +819,3 @@ class MediaController(object):
live_controller = property(_get_live_controller)
@staticmethod
def parse_optical_path(input):
# split the clip info
clip_info = input.split(sep=':')
title = int(clip_info[1])
audio_track = int(clip_info[2])
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

View File

@ -67,7 +67,7 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector):
"""
Exit Dialog and do not save
"""
log.debug ('MediaClipSelectorForm.reject')
log.debug('MediaClipSelectorForm.reject')
self.vlc_media_player.stop()
QtGui.QDialog.reject(self)
@ -110,6 +110,8 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector):
def on_load_disc_pushbutton_clicked(self, clicked):
"""
Load the media when the load-button has been clicked
:param clicked: Given from signal, not used.
"""
self.disable_all()
path = self.media_path_combobox.currentText()
@ -134,12 +136,6 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector):
self.toggle_disable_load_media(False)
return
self.vlc_media_player.audio_set_mute(True)
#while self.vlc_media_player.get_time() == 0:
# if self.vlc_media_player.get_state() == vlc.State.Error:
# log.debug('player in error state')
# self.toggle_disable_load_media(False)
# return
# time.sleep(0.1)
if not self.media_state_wait(vlc.State.Playing):
return
self.vlc_media_player.pause()
@ -163,6 +159,8 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector):
def on_pause_pushbutton_clicked(self, clicked):
"""
Pause the playback
:param clicked: Given from signal, not used.
"""
self.vlc_media_player.pause()
@ -170,6 +168,8 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector):
def on_play_pushbutton_clicked(self, clicked):
"""
Start the playback
:param clicked: Given from signal, not used.
"""
self.vlc_media_player.play()
@ -177,26 +177,40 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector):
def on_set_start_pushbutton_clicked(self, clicked):
"""
Copy the current player position to start_timeedit
:param clicked: Given from signal, not used.
"""
vlc_ms_pos = self.vlc_media_player.get_time()
time = QtCore.QTime()
new_pos_time = time.addMSecs(vlc_ms_pos)
self.start_timeedit.setTime(new_pos_time)
# If start time is after end time, update end time.
end_time = self.end_timeedit.time()
if end_time < new_pos_time:
self.end_timeedit.setTime(new_pos_time)
@QtCore.pyqtSlot(bool)
def on_set_end_pushbutton_clicked(self, clicked):
"""
Copy the current player position to end_timeedit
:param clicked: Given from signal, not used.
"""
vlc_ms_pos = self.vlc_media_player.get_time()
time = QtCore.QTime()
new_pos_time = time.addMSecs(vlc_ms_pos)
self.end_timeedit.setTime(new_pos_time)
# If start time is after end time, update end time.
start_time = self.start_timeedit.time()
if start_time > new_pos_time:
self.start_timeedit.setTime(new_pos_time)
@QtCore.pyqtSlot(bool)
def on_jump_end_pushbutton_clicked(self, clicked):
"""
Set the player position to the position stored in end_timeedit
:param clicked: Given from signal, not used.
"""
end_time = self.end_timeedit.time()
end_time_ms = end_time.hour() * 60 * 60 * 1000 + \
@ -209,6 +223,8 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector):
def on_jump_start_pushbutton_clicked(self, clicked):
"""
Set the player position to the position stored in start_timeedit
:param clicked: Given from signal, not used.
"""
start_time = self.start_timeedit.time()
start_time_ms = start_time.hour() * 60 * 60 * 1000 + \
@ -221,17 +237,14 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector):
def on_title_combo_box_currentIndexChanged(self, index):
"""
When a new title is chosen, it is loaded by VLC and info about audio and subtitle tracks is reloaded
:param index: The index of the newly chosen title track.
"""
log.debug('in on_title_combo_box_changed, index: ', str(index))
self.vlc_media_player.set_title(index)
self.vlc_media_player.set_time(0)
self.vlc_media_player.play()
self.vlc_media_player.audio_set_mute(True)
#while self.vlc_media_player.get_time() == 0:
# if self.vlc_media_player.get_state() == vlc.State.Error:
# log.debug('player in error state')
# return
# time.sleep(0.1)
if not self.media_state_wait(vlc.State.Playing):
return
# pause
@ -241,7 +254,7 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector):
audio_tracks = self.vlc_media_player.audio_get_track_description()
self.audio_tracks_combobox.clear()
for audio_track in audio_tracks:
self.audio_tracks_combobox.addItem(audio_track[1].decode(),audio_track[0])
self.audio_tracks_combobox.addItem(audio_track[1].decode(), audio_track[0])
# Enable audio track combobox if anything is in it
if len(audio_tracks) > 0:
self.audio_tracks_combobox.setDisabled(False)
@ -270,6 +283,8 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector):
def on_audio_tracks_combobox_currentIndexChanged(self, index):
"""
When a new audio track is chosen update audio track bing played by VLC
:param index: The index of the newly chosen audio track.
"""
audio_track = self.audio_tracks_combobox.itemData(index)
log.debug('in on_audio_tracks_combobox_currentIndexChanged, index: ', str(index), ' audio_track: ', audio_track)
@ -280,15 +295,18 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector):
def on_subtitle_tracks_combobox_currentIndexChanged(self, index):
"""
When a new subtitle track is chosen update subtitle track bing played by VLC
:param index: The index of the newly chosen subtitle.
"""
subtitle_track = self.subtitle_tracks_combobox.itemData(index)
log.debug('in on_subtitle_tracks_combobox_currentIndexChanged, index: ', str(index), ' subtitle_track: ', subtitle_track)
if subtitle_track:
self.vlc_media_player.video_set_spu(int(subtitle_track))
def on_position_horizontalslider_sliderMoved(self, position):
"""
Set player position according to new slider position.
:param position: Position to seek to.
"""
self.vlc_media_player.set_time(position)
@ -318,16 +336,16 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector):
"""
Enable/disable load media combobox and button.
@param action: If True elements are disabled, if False they are enabled.
:param action: If True elements are disabled, if False they are enabled.
"""
self.media_path_combobox.setDisabled(action)
self.load_disc_pushbutton.setDisabled(action)
def toggle_disable_player(self, action):
"""
Enable/disable player elementa.
Enable/disable player elements.
@param action: If True elements are disabled, if False they are enabled.
:param action: If True elements are disabled, if False they are enabled.
"""
self.play_pushbutton.setDisabled(action)
self.pause_pushbutton.setDisabled(action)
@ -343,9 +361,11 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector):
self.save_pushbutton.setDisabled(action)
@QtCore.pyqtSlot(bool)
def on_save_pushbutton_clicked(self, checked):
def on_save_pushbutton_clicked(self, clicked):
"""
Saves the current media and trackinfo as a clip to the mediamanager
:param clicked: Given from signal, not used.
"""
log.debug('in on_save_pushbutton_clicked')
start_time = self.start_timeedit.time()
@ -362,13 +382,17 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector):
audio_track = self.audio_tracks_combobox.itemData(self.audio_tracks_combobox.currentIndex())
subtitle_track = self.subtitle_tracks_combobox.itemData(self.subtitle_tracks_combobox.currentIndex())
path = self.media_path_combobox.currentText()
optical = 'optical:' + str(title) + ':' + str(audio_track) + ':' + str(subtitle_track) + ':' + str(start_time_ms) + ':' + str(end_time_ms) + ':' + path
optical = 'optical:' + str(title) + ':' + str(audio_track) + ':' + str(subtitle_track) + ':' + str(
start_time_ms) + ':' + str(end_time_ms) + ':' + path
self.media_item.add_optical_clip(optical)
def media_state_wait(self, media_state):
"""
Wait for the video to change its state
Wait no longer than 15 seconds. (loading an iso file needs a long time)
:param media_state: VLC media state to wait for.
:return: True if state was reached within 15 seconds, False if not or error occurred.
"""
start = datetime.now()
while not media_state == self.vlc_media.get_state():

View File

@ -29,6 +29,7 @@
import logging
import os
from datetime import time
from PyQt4 import QtCore, QtGui
@ -37,7 +38,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
from openlp.core.ui.media import get_media_players, set_media_players, parse_optical_path
from openlp.core.utils import get_locale_key
from openlp.plugins.media.forms.mediaclipselectorform import MediaClipSelectorForm
from openlp.core.ui.media.vlcplayer import VLC_AVAILABLE
@ -47,9 +48,10 @@ log = logging.getLogger(__name__)
CLAPPERBOARD = ':/media/slidecontroller_multimedia.png'
OPTICAL = ':/media/media_optical.png'
VIDEO_ICON = build_icon(':/media/media_video.png')
AUDIO_ICON = build_icon(':/media/media_audio.png')
OPTICAL_ICON = build_icon(':/media/media_optical.png')
OPTICAL_ICON = build_icon(OPTICAL)
ERROR_ICON = build_icon(':/general/general_delete.png')
@ -89,6 +91,10 @@ class MediaMediaItem(MediaManagerItem):
self.list_view.activateDnD()
def retranslateUi(self):
"""
This method is called automatically to provide OpenLP with the opportunity to translate the ``MediaManagerItem``
to another language.
"""
self.on_new_prompt = translate('MediaPlugin.MediaItem', 'Select Media')
self.replace_action.setText(UiStrings().ReplaceBG)
self.replace_action.setToolTip(UiStrings().ReplaceLiveBG)
@ -108,16 +114,25 @@ class MediaMediaItem(MediaManagerItem):
self.has_edit_icon = False
def add_list_view_to_toolbar(self):
"""
Creates the main widget for listing items.
"""
MediaManagerItem.add_list_view_to_toolbar(self)
self.list_view.addAction(self.replace_action)
def add_start_header_bar(self):
"""
Adds buttons to the start of the header bar.
"""
self.load_optical = self.toolbar.add_toolbar_action('load_optical', icon=OPTICAL_ICON, text='Load optical disc',
tooltip='Load optical disc', triggers=self.on_load_optical)
if not VLC_AVAILABLE:
self.load_optical.setDisabled(True)
def add_end_header_bar(self):
"""
Adds buttons to the end of the header bar.
"""
# 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',
triggers=self.on_replace_click)
@ -139,6 +154,11 @@ class MediaMediaItem(MediaManagerItem):
self.display_type_combo_box.currentIndexChanged.connect(self.override_player_changed)
def override_player_changed(self, index):
"""
Change to the selected override media player
:param index: Index of the new selected player.
"""
player = get_media_players()[0]
if index == 0:
set_media_players(player)
@ -163,7 +183,7 @@ class MediaMediaItem(MediaManagerItem):
Called to replace Live background with the media selected.
"""
if check_item_selected(self.list_view,
translate('MediaPlugin.MediaItem',
translate('MediaPlugin.MediaItem',
'You must select a media file to replace the background with.')):
item = self.list_view.currentItem()
filename = item.data(QtCore.Qt.UserRole)
@ -172,12 +192,12 @@ class MediaMediaItem(MediaManagerItem):
service_item.title = 'webkit'
service_item.processor = 'webkit'
(path, name) = os.path.split(filename)
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):
self.reset_action.setVisible(True)
else:
critical_error_message_box(UiStrings().LiveBGError,
translate('MediaPlugin.MediaItem',
translate('MediaPlugin.MediaItem',
'There was no display item to amend.'))
else:
critical_error_message_box(UiStrings().LiveBGError,
@ -195,10 +215,9 @@ class MediaMediaItem(MediaManagerItem):
if item is None:
return False
filename = item.data(QtCore.Qt.UserRole)
log.debug('generate_slide_data, filename: ' + filename)
# Special handling if the filename is a optical clip
if filename.startswith('optical:'):
(name, title, audio_track, subtitle_track, start, end) = self.parse_optical_path(filename)
log.debug('generate_slide_data, optical name: ' + name)
(name, title, audio_track, subtitle_track, start, end) = parse_optical_path(filename)
if not os.path.exists(name):
if not remote:
# Optical disc is no longer present
@ -206,16 +225,16 @@ class MediaMediaItem(MediaManagerItem):
translate('MediaPlugin.MediaItem', 'Missing Media File'),
translate('MediaPlugin.MediaItem', 'The optical disc %s is no longer available.') % name)
return False
service_item.title = name
service_item.processor = self.display_type_combo_box.currentText()
service_item.add_from_command(filename, name, OPTICAL_ICON)
service_item.add_from_command(filename, name, CLAPPERBOARD)
service_item.title = name + '@' + self.format_milliseconds(start) + '-' + self.format_milliseconds(end)
# Only set start and end times if going to a service
#if context == ServiceItemContext.Service:
# Set the length
self.media_controller.media_setup_optical(name, title, audio_track, subtitle_track, start, end, None, None)
service_item.set_media_length((end - start)/1000)
service_item.start_time = start/1000
service_item.end_time = end/1000
service_item.set_media_length((end - start) / 1000)
service_item.start_time = start / 1000
service_item.end_time = end / 1000
service_item.add_capability(ItemCapabilities.IsOptical)
else:
if not os.path.exists(filename):
@ -243,12 +262,15 @@ class MediaMediaItem(MediaManagerItem):
return True
def initialise(self):
"""
Initialize media item.
"""
self.list_view.clear()
self.list_view.setIconSize(QtCore.QSize(88, 50))
self.service_path = os.path.join(AppLocation.get_section_data_path(self.settings_section), 'thumbnails')
check_directory_exists(self.service_path)
self.load_list(Settings().value(self.settings_section + '/media files'))
self.populate_display_types()
self.rebuild_players()
self.media_clip_selector_form = MediaClipSelectorForm(self, self.main_window, None)
def rebuild_players(self):
@ -261,6 +283,9 @@ class MediaMediaItem(MediaManagerItem):
' '.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):
@ -300,24 +325,32 @@ class MediaMediaItem(MediaManagerItem):
Settings().setValue(self.settings_section + '/media files', self.get_file_list())
def load_list(self, media, target_group=None):
# Sort the media by its filename considering language specific characters.
"""
Sort the media by its filename considering language specific characters.
:param media: List if media to sort and list.
:param target_group: Not used in media.
"""
media.sort(key=lambda file_name: get_locale_key(os.path.split(str(file_name))[1]))
for track in media:
track_info = QtCore.QFileInfo(track)
if track.startswith('optical:'):
(file_name, title, audio_track, subtitle_track, start, end) = self.parse_optical_path(track)
optical = file_name + '@' + str(title) + ':' + str(start) + '-' + str(end)
# 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)
item_name.setIcon(build_icon(OPTICAL_ICON))
item_name.setIcon(OPTICAL_ICON)
item_name.setData(QtCore.Qt.UserRole, track)
item_name.setToolTip(optical)
elif not os.path.exists(track):
# File doesn't exist, mark as error.
file_name = os.path.split(str(track))[1]
item_name = QtGui.QListWidgetItem(file_name)
item_name.setIcon(ERROR_ICON)
item_name.setData(QtCore.Qt.UserRole, track)
item_name.setToolTip(track)
elif track_info.isFile():
# Normal media file handling.
file_name = os.path.split(str(track))[1]
item_name = QtGui.QListWidgetItem(file_name)
if '*.%s' % (file_name.split('.')[-1].lower()) in self.media_controller.audio_extensions_list:
@ -329,6 +362,12 @@ class MediaMediaItem(MediaManagerItem):
self.list_view.addItem(item_name)
def get_list(self, type=MediaType.Audio):
"""
Get the list of media, optional select media type.
:param type: Type to get, defaults to audio.
:return: The media list
"""
media = Settings().value(self.settings_section + '/media files')
media.sort(key=lambda filename: get_locale_key(os.path.split(str(filename))[1]))
extension = []
@ -341,6 +380,13 @@ class MediaMediaItem(MediaManagerItem):
return media
def search(self, string, show_error):
"""
Performs a search for items containing ``string``
:param string: String to be displayed
:param show_error: Should the error be shown (True)
:return: The search result.
"""
files = Settings().value(self.settings_section + '/media files')
results = []
string = string.lower()
@ -351,25 +397,29 @@ class MediaMediaItem(MediaManagerItem):
return results
def on_load_optical(self):
log.debug('in on_load_optical')
"""
When the load optical button is clicked, open the clip selector window.
"""
self.media_clip_selector_form.exec_()
def add_optical_clip(self, optical):
"""
Add a optical based clip to the mediamanager, called from media_clip_selector_form.
:param optical: The clip to add.
"""
full_list = self.get_file_list()
full_list.append(optical)
self.load_list([optical])
Settings().setValue(self.settings_section + '/media files', self.get_file_list())
@staticmethod
def parse_optical_path(input):
# split the clip info
clip_info = input.split(sep=':')
title = int(clip_info[1])
audio_track = int(clip_info[2])
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
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(millis, 60)
hours, minutes = divmod(minutes, 60)
return "%02d:%02d:%02d,%03d" % (hours, minutes, seconds, millis)