From 3988aacfbd7d12c50d20ea42b8685c9c80986719 Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Sat, 8 Mar 2014 21:14:04 +0000 Subject: [PATCH] Made it possible to save and load optical clip to servicefile. Also some pep8 and general cleanup. --- openlp/core/lib/serviceitem.py | 29 +++-- openlp/core/ui/media/__init__.py | 25 +++- openlp/core/ui/media/mediacontroller.py | 19 +-- .../media/forms/mediaclipselectorform.py | 62 +++++++--- openlp/plugins/media/lib/mediaitem.py | 114 +++++++++++++----- 5 files changed, 167 insertions(+), 82 deletions(-) diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index cb03d981f..694bb139f 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -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): """ diff --git a/openlp/core/ui/media/__init__.py b/openlp/core/ui/media/__init__.py index aa2819450..8223ed694 100644 --- a/openlp/core/ui/media/__init__.py +++ b/openlp/core/ui/media/__init__.py @@ -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 diff --git a/openlp/core/ui/media/mediacontroller.py b/openlp/core/ui/media/mediacontroller.py index 26e153503..8f782ce24 100644 --- a/openlp/core/ui/media/mediacontroller.py +++ b/openlp/core/ui/media/mediacontroller.py @@ -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 diff --git a/openlp/plugins/media/forms/mediaclipselectorform.py b/openlp/plugins/media/forms/mediaclipselectorform.py index 115e6690b..34a228136 100644 --- a/openlp/plugins/media/forms/mediaclipselectorform.py +++ b/openlp/plugins/media/forms/mediaclipselectorform.py @@ -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(): diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 7f06a392f..86d6d38ac 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -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)