Source code for openlp.plugins.media.forms.mediaclipselectorform

# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4

###############################################################################
# OpenLP - Open Source Lyrics Projection                                      #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2017 OpenLP Developers                                   #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it     #
# under the terms of the GNU General Public License as published by the Free  #
# Software Foundation; version 2 of the License.                              #
#                                                                             #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for    #
# more details.                                                               #
#                                                                             #
# You should have received a copy of the GNU General Public License along     #
# with this program; if not, write to the Free Software Foundation, Inc., 59  #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
###############################################################################

import os
import logging
import re
from time import sleep
from datetime import datetime

from PyQt5 import QtCore, QtGui, QtWidgets

from openlp.core.common import translate, is_win, is_linux, is_macosx, RegistryProperties
from openlp.plugins.media.forms.mediaclipselectordialog import Ui_MediaClipSelector
from openlp.core.lib.ui import critical_error_message_box
from openlp.core.ui.media.vlcplayer import get_vlc

if is_win():
    from win32com.client import Dispatch

if is_linux():
    import dbus

log = logging.getLogger(__name__)


[docs]class MediaClipSelectorForm(QtWidgets.QDialog, Ui_MediaClipSelector, RegistryProperties): """ Class to manage the clip selection """ log.info('{name} MediaClipSelectorForm loaded'.format(name=__name__)) def __init__(self, media_item, parent, manager): """ Constructor """ super(MediaClipSelectorForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) self.vlc_instance = None self.vlc_media_player = None self.vlc_media = None self.timer = None self.audio_cd_tracks = None self.audio_cd = False self.playback_length = 0 self.media_item = media_item self.setupUi(self) # setup play/pause icon self.play_icon = QtGui.QIcon() self.play_icon.addPixmap(QtGui.QPixmap(":/slides/media_playback_start.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.pause_icon = QtGui.QIcon() self.pause_icon.addPixmap(QtGui.QPixmap(":/slides/media_playback_pause.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
[docs] def reject(self): """ Exit Dialog and do not save """ log.debug('MediaClipSelectorForm.reject') # Tear down vlc if self.vlc_media_player: self.vlc_media_player.stop() self.vlc_media_player.release() self.vlc_media_player = None if self.vlc_instance: self.vlc_instance.release() self.vlc_instance = None if self.vlc_media: self.vlc_media.release() self.vlc_media = None return QtWidgets.QDialog.reject(self)
[docs] def exec(self): """ Start dialog """ self.reset_ui() self.setup_vlc() return QtWidgets.QDialog.exec(self)
[docs] def reset_ui(self): """ Reset the UI to default values """ self.playback_length = 0 self.position_slider.setMinimum(0) self.disable_all() self.toggle_disable_load_media(False) self.subtitle_tracks_combobox.clear() self.audio_tracks_combobox.clear() self.titles_combo_box.clear() time = QtCore.QTime() self.start_position_edit.setTime(time) self.end_timeedit.setTime(time) self.position_timeedit.setTime(time)
[docs] def setup_vlc(self): """ Setup VLC instance and mediaplayer """ vlc = get_vlc() self.vlc_instance = vlc.Instance() # creating an empty vlc media player self.vlc_media_player = self.vlc_instance.media_player_new() # The media player has to be 'connected' to the QFrame. # (otherwise a video would be displayed in it's own window) # This is platform specific! # You have to give the id of the QFrame (or similar object) # to vlc, different platforms have different functions for this. win_id = int(self.preview_frame.winId()) if is_win(): self.vlc_media_player.set_hwnd(win_id) elif is_macosx(): # We have to use 'set_nsobject' since Qt5 on OSX uses Cocoa # framework and not the old Carbon. self.vlc_media_player.set_nsobject(win_id) else: # for Linux using the X Server self.vlc_media_player.set_xwindow(win_id) self.vlc_media = None # Setup timer every 100 ms to update position self.timer = QtCore.QTimer(self) self.timer.timeout.connect(self.update_position) self.timer.start(100) self.find_optical_devices() self.audio_cd = False self.audio_cd_tracks = None
[docs] 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. """ vlc = get_vlc() # Detect by trying to play it as a CD self.vlc_media = self.vlc_instance.media_new_location('cdda://' + path) self.vlc_media_player.set_media(self.vlc_media) 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.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: return False # Insert into titles_combo_box self.titles_combo_box.clear() for i in range(self.audio_cd_tracks.count()): item = self.audio_cd_tracks.item_at_index(i) item_title = item.get_meta(vlc.Meta.Title) self.titles_combo_box.addItem(item_title, i) self.vlc_media_player.set_media(self.audio_cd_tracks.item_at_index(0)) self.audio_cd = True self.titles_combo_box.setDisabled(False) self.titles_combo_box.setCurrentIndex(0) self.on_titles_combo_box_currentIndexChanged(0) return True
[docs] @QtCore.pyqtSlot(bool) def on_load_disc_button_clicked(self, clicked): """ Load the media when the load-button has been clicked :param clicked: Given from signal, not used. """ log.debug('on_load_disc_button_clicked') vlc = get_vlc() self.disable_all() self.application.set_busy_cursor() path = self.media_path_combobox.currentText() # Check if given path is non-empty and exists before starting VLC if not path: log.debug('no given path') critical_error_message_box(message=translate('MediaPlugin.MediaClipSelectorForm', 'No path was given')) self.toggle_disable_load_media(False) self.application.set_normal_cursor() return if not os.path.exists(path): log.debug('Given path does not exists') critical_error_message_box(message=translate('MediaPlugin.MediaClipSelectorForm', 'Given path does not exists')) self.toggle_disable_load_media(False) self.application.set_normal_cursor() return # VLC behaves a bit differently on windows and linux when loading, which creates problems when trying to # detect if we're dealing with a DVD or CD, so we use different loading approaches depending on the OS. if is_win(): # If the given path is in the format "D:\" or "D:", prefix it with "/" to make VLC happy pattern = re.compile('^\w:\\\\*$') if pattern.match(path): path = '/' + path self.vlc_media = self.vlc_instance.media_new_location('dvd://' + path) else: self.vlc_media = self.vlc_instance.media_new_path(path) if not self.vlc_media: log.debug('vlc media player is none') critical_error_message_box(message=translate('MediaPlugin.MediaClipSelectorForm', 'An error happened during initialization of VLC player')) self.toggle_disable_load_media(False) self.application.set_normal_cursor() return # put the media in the media player self.vlc_media_player.set_media(self.vlc_media) self.vlc_media_player.audio_set_mute(True) # start playback to get vlc to parse the media if self.vlc_media_player.play() < 0: log.debug('vlc play returned error') critical_error_message_box(message=translate('MediaPlugin.MediaClipSelectorForm', 'VLC player failed playing the media')) self.toggle_disable_load_media(False) self.application.set_normal_cursor() self.vlc_media_player.audio_set_mute(False) return self.vlc_media_player.audio_set_mute(True) if not self.media_state_wait(vlc.State.Playing): # Tests if this is an audio CD if not self.detect_audio_cd(path): critical_error_message_box(message=translate('MediaPlugin.MediaClipSelectorForm', 'VLC player failed playing the media')) self.toggle_disable_load_media(False) self.application.set_normal_cursor() self.vlc_media_player.audio_set_mute(False) return # pause self.vlc_media_player.set_time(0) self.vlc_media_player.set_pause(1) self.media_state_wait(vlc.State.Paused) self.toggle_disable_load_media(False) self.application.set_normal_cursor() self.vlc_media_player.audio_set_mute(False) if not self.audio_cd: # Temporarily disable signals self.blockSignals(True) # Get titles, insert in combobox titles = self.vlc_media_player.video_get_title_description() self.titles_combo_box.clear() for title in titles: self.titles_combo_box.addItem(title[1].decode(), title[0]) # Re-enable signals self.blockSignals(False) # Main title is usually title #1 if len(titles) > 1: self.titles_combo_box.setCurrentIndex(1) # Enable audio track combobox if anything is in it if len(titles) > 0: self.titles_combo_box.setDisabled(False) log.debug('load_disc_button end - ' 'vlc_media_player state: {state}'.format(state=self.vlc_media_player.get_state()))
[docs] @QtCore.pyqtSlot(bool) def on_play_button_clicked(self, clicked): """ Toggle the playback :param clicked: Given from signal, not used. """ vlc = get_vlc() if self.vlc_media_player.get_state() == vlc.State.Playing: self.vlc_media_player.pause() self.play_button.setIcon(self.play_icon) else: self.vlc_media_player.play() self.media_state_wait(vlc.State.Playing) self.play_button.setIcon(self.pause_icon)
[docs] @QtCore.pyqtSlot(bool) def on_set_start_button_clicked(self, clicked): """ Copy the current player position to start_position_edit :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_position_edit.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)
[docs] @QtCore.pyqtSlot(bool) def on_set_end_button_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 start time. start_time = self.start_position_edit.time() if start_time > new_pos_time: self.start_position_edit.setTime(new_pos_time)
[docs] @QtCore.pyqtSlot(QtCore.QTime) def on_start_timeedit_timeChanged(self, new_time): """ Called when start_position_edit is changed manually :param new_time: The new time """ # If start time is after end time, update end time. end_time = self.end_timeedit.time() if end_time < new_time: self.end_timeedit.setTime(new_time)
[docs] @QtCore.pyqtSlot(QtCore.QTime) def on_end_timeedit_timeChanged(self, new_time): """ Called when end_timeedit is changed manually :param new_time: The new time """ # If start time is after end time, update start time. start_time = self.start_position_edit.time() if start_time > new_time: self.start_position_edit.setTime(new_time)
[docs] @QtCore.pyqtSlot(bool) def on_jump_end_button_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 + \ end_time.minute() * 60 * 1000 + \ end_time.second() * 1000 + \ end_time.msec() self.vlc_media_player.set_time(end_time_ms)
[docs] @QtCore.pyqtSlot(bool) def on_jump_start_button_clicked(self, clicked): """ Set the player position to the position stored in start_position_edit :param clicked: Given from signal, not used. """ start_time = self.start_position_edit.time() start_time_ms = start_time.hour() * 60 * 60 * 1000 + \ start_time.minute() * 60 * 1000 + \ start_time.second() * 1000 + \ start_time.msec() self.vlc_media_player.set_time(start_time_ms)
[docs] @QtCore.pyqtSlot(int) def on_titles_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_titles_combo_box_changed, index: {index:d}'.format(index=index)) vlc = get_vlc() if not self.vlc_media_player: log.error('vlc_media_player was None') return self.application.set_busy_cursor() if self.audio_cd: self.vlc_media = self.audio_cd_tracks.item_at_index(index) self.vlc_media_player.set_media(self.vlc_media) self.vlc_media_player.set_time(0) self.vlc_media_player.play() if not self.media_state_wait(vlc.State.Playing): log.error('Could not start playing audio cd, needed to get track info') self.application.set_normal_cursor() return self.vlc_media_player.audio_set_mute(True) # pause self.vlc_media_player.set_time(0) self.vlc_media_player.set_pause(1) self.vlc_media_player.audio_set_mute(False) self.application.set_normal_cursor() self.toggle_disable_player(False) else: self.vlc_media_player.set_title(index) self.vlc_media_player.set_time(0) self.vlc_media_player.play() if not self.media_state_wait(vlc.State.Playing): log.error('Could not start playing dvd, needed to get track info') self.application.set_normal_cursor() return self.vlc_media_player.audio_set_mute(True) # Get audio tracks audio_tracks = self.vlc_media_player.audio_get_track_description() log.debug('number of audio tracks: {tracks:d}'.format(tracks=len(audio_tracks))) # Clear the audio track combobox, insert new tracks self.audio_tracks_combobox.clear() for audio_track in audio_tracks: 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) # First track is "deactivated", so set to next if it exists if len(audio_tracks) > 1: self.audio_tracks_combobox.setCurrentIndex(1) # Get subtitle tracks, insert in combobox subtitles_tracks = self.vlc_media_player.video_get_spu_description() self.subtitle_tracks_combobox.clear() for subtitle_track in subtitles_tracks: self.subtitle_tracks_combobox.addItem(subtitle_track[1].decode(), subtitle_track[0]) # Enable subtitle track combobox is anything in it if len(subtitles_tracks) > 0: self.subtitle_tracks_combobox.setDisabled(False) self.vlc_media_player.audio_set_mute(False) self.vlc_media_player.set_pause(1) # If a title or audio track is available the player is enabled if self.titles_combo_box.count() > 0 or len(audio_tracks) > 0: self.toggle_disable_player(False) # Set media length info self.playback_length = self.vlc_media_player.get_length() log.debug('playback_length: {length:d} ms'.format(length=self.playback_length)) # if length is 0, wait a bit, maybe vlc will change its mind... loop_count = 0 while self.playback_length == 0 and loop_count < 20: sleep(0.1) self.playback_length = self.vlc_media_player.get_length() loop_count += 1 log.debug('in loop, playback_length: {length:d} ms'.format(length=self.playback_length)) self.position_slider.setMaximum(self.playback_length) # setup start and end time rounded_vlc_ms_length = int(round(self.playback_length / 100.0) * 100.0) time = QtCore.QTime() playback_length_time = time.addMSecs(rounded_vlc_ms_length) self.start_position_edit.setMaximumTime(playback_length_time) self.end_timeedit.setMaximumTime(playback_length_time) self.end_timeedit.setTime(playback_length_time) # Pause once again, just to make sure loop_count = 0 while self.vlc_media_player.get_state() == vlc.State.Playing and loop_count < 20: sleep(0.1) self.vlc_media_player.set_pause(1) loop_count += 1 log.debug('titles_combo_box end - ' 'vlc_media_player state: {state}'.format(state=self.vlc_media_player.get_state())) self.application.set_normal_cursor()
[docs] @QtCore.pyqtSlot(int) 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. """ if not self.vlc_media_player: return audio_track = self.audio_tracks_combobox.itemData(index) log.debug('in on_audio_tracks_combobox_currentIndexChanged, ' 'index: {index:d} audio_track: {tracks}'.format(index=index, tracks=audio_track)) if audio_track and int(audio_track) > 0: self.vlc_media_player.audio_set_track(int(audio_track))
[docs] @QtCore.pyqtSlot(int) 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. """ if not self.vlc_media_player: return subtitle_track = self.subtitle_tracks_combobox.itemData(index) if subtitle_track: self.vlc_media_player.video_set_spu(int(subtitle_track))
[docs] def on_position_slider_sliderMoved(self, position): """ Set player position according to new slider position. :param position: Position to seek to. """ self.vlc_media_player.set_time(position)
[docs] def update_position(self): """ Update slider position and displayed time according to VLC player position. """ if self.vlc_media_player: vlc_ms_pos = self.vlc_media_player.get_time() rounded_vlc_ms_pos = int(round(vlc_ms_pos / 100.0) * 100.0) time = QtCore.QTime() new_pos_time = time.addMSecs(rounded_vlc_ms_pos) self.position_timeedit.setTime(new_pos_time) self.position_slider.setSliderPosition(vlc_ms_pos)
[docs] def disable_all(self): """ Disable all elements in the dialog """ self.toggle_disable_load_media(True) self.titles_combo_box.setDisabled(True) self.audio_tracks_combobox.setDisabled(True) self.subtitle_tracks_combobox.setDisabled(True) self.toggle_disable_player(True)
[docs] def toggle_disable_load_media(self, action): """ Enable/disable load media combobox and button. :param action: If True elements are disabled, if False they are enabled. """ self.media_path_combobox.setDisabled(action) self.load_disc_button.setDisabled(action)
[docs] def toggle_disable_player(self, action): """ Enable/disable player elements. :param action: If True elements are disabled, if False they are enabled. """ self.play_button.setDisabled(action) self.position_slider.setDisabled(action) self.position_timeedit.setDisabled(action) self.start_position_edit.setDisabled(action) self.set_start_button.setDisabled(action) self.jump_start_button.setDisabled(action) self.end_timeedit.setDisabled(action) self.set_end_button.setDisabled(action) self.jump_end_button.setDisabled(action) self.save_button.setDisabled(action)
[docs] def accept(self): """ Saves the current media and trackinfo as a clip to the mediamanager """ log.debug('in MediaClipSelectorForm.accept') start_time = self.start_position_edit.time() start_time_ms = start_time.hour() * 60 * 60 * 1000 + \ start_time.minute() * 60 * 1000 + \ start_time.second() * 1000 + \ start_time.msec() end_time = self.end_timeedit.time() end_time_ms = end_time.hour() * 60 * 60 * 1000 + \ end_time.minute() * 60 * 1000 + \ end_time.second() * 1000 + \ end_time.msec() title = self.titles_combo_box.itemData(self.titles_combo_box.currentIndex()) path = self.media_path_combobox.currentText() optical = '' if self.audio_cd: # Check for load problems if start_time_ms is None or end_time_ms is None or title is None: critical_error_message_box(translate('MediaPlugin.MediaClipSelectorForm', 'CD not loaded correctly'), translate('MediaPlugin.MediaClipSelectorForm', 'The CD was not loaded correctly, please re-load and try again.')) return optical = 'optical:{title:d}:-1:-1:{start:d}:{end:d}:'.format(title=title, start=start_time_ms, end=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()) # Check for load problems if start_time_ms is None or end_time_ms is None or title is None or audio_track is None\ or subtitle_track is None: critical_error_message_box(translate('MediaPlugin.MediaClipSelectorForm', 'DVD not loaded correctly'), translate('MediaPlugin.MediaClipSelectorForm', 'The DVD was not loaded correctly, please re-load and try again.')) return optical = 'optical:{title:d}:{audio:d}:{sub:d}:{start:d}:{end:d}:'.format(title=title, audio=audio_track, sub=subtitle_track, start=start_time_ms, end=end_time_ms) # Ask for an alternative name for the mediaclip while True: new_optical_name, ok = QtWidgets.QInputDialog.getText(self, translate('MediaPlugin.MediaClipSelectorForm', 'Set name of mediaclip'), translate('MediaPlugin.MediaClipSelectorForm', 'Name of mediaclip:'), QtWidgets.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)
[docs] def media_state_wait(self, media_state): """ Wait for the video to change its state Wait no longer than 15 seconds. (loading an optical disc takes some 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. """ vlc = get_vlc() start = datetime.now() while media_state != self.vlc_media_player.get_state(): if self.vlc_media_player.get_state() == vlc.State.Error: return False if (datetime.now() - start).seconds > 15: return False return True
[docs] def find_optical_devices(self): """ Attempt to autodetect optical devices on the computer, and add them to the media-dropdown :return: """ # Clear list first self.media_path_combobox.clear() if is_win(): # use win api to find optical drives fso = Dispatch('scripting.filesystemobject') for drive in fso.Drives: log.debug('Drive {drive} has type {types:d}'.format(drive=drive.DriveLetter, types=drive.DriveType)) # if type is 4, it is a cd-rom drive if drive.DriveType == 4: self.media_path_combobox.addItem('{drive}:\\'.format(drive=drive.DriveLetter)) elif is_linux(): # Get disc devices from dbus and find the ones that are optical bus = dbus.SystemBus() try: udev_manager_obj = bus.get_object('org.freedesktop.UDisks', '/org/freedesktop/UDisks') udev_manager = dbus.Interface(udev_manager_obj, 'org.freedesktop.UDisks') for dev in udev_manager.EnumerateDevices(): device_obj = bus.get_object("org.freedesktop.UDisks", dev) device_props = dbus.Interface(device_obj, dbus.PROPERTIES_IFACE) if device_props.Get('org.freedesktop.UDisks.Device', 'DeviceIsDrive'): drive_props = device_props.Get('org.freedesktop.UDisks.Device', 'DriveMediaCompatibility') if any('optical' in prop for prop in drive_props): self.media_path_combobox.addItem(device_props.Get('org.freedesktop.UDisks.Device', 'DeviceFile')) return except dbus.exceptions.DBusException: 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(): 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): for device in udev_manager.GetManagedObjects().values(): 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')]: if chr(c) != '\x00': block_file += chr(c) self.media_path_combobox.addItem(block_file) elif is_macosx(): # Look for DVD folders in devices to find optical devices volumes = os.listdir('/Volumes') candidates = list() for volume in volumes: 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