Как извлечь длительность аудиофайла с помощью PyQt 5?(часть 2)

UPD: Мне не нужно воспроизводить песни, пути к которым я получаю. Мне нужно извлечь из них длину и не более

Я уже задавал такой вопрос здесь, но, чтобы сделать ситуацию проще, я попытался отбросить все моменты, о которых, как потом оказалось, нужно было рассказать.

Сейчас я попытаюсь объяснить, что происходит и что мне нужно.

Кнопка play_btn взаимодействует с методом play, который запускает воспроизведение какой-нибудь песни. Я кликаю по ней.

Кнопка folder_btn связана с методом choose_folder, который совершает некоторые манипуляции с файлами:

  • Открывает диалоговое окно выбора папки с файлами
  • Отбирает из выбранной папки файлы MP3-формата
  • Немного изменяет путь к этим файлам, чтобы было проще работать с PyQt
  • Создает соединение с базой данных, куда будет записываться информация о треке

Все эти пункты не играют никакой роли в моем вопросе, поэтому я вырезал их из кода. В конечном итоге я получаю список, содержащий пути к MP3-файлам. Я дал ему название songs.

Получив пути к файлам, нужно достать и записать длительность каждого в self.list. При этом play_btn уже нажата, а значит, приложение уже воспроизводит звук. Если добавлять файлы из songs в QMediaPlayer, песня, котороя уже воспроизводилась плеером, собьется. Поэтому в прошлом вопросе я говорил о том, что не могу задавать новый источник звука плееру.

Пожалуйста, скажите, как извлечь длительность аудиофайлов в моем случае?

from PyQt5 import QtCore, QtWidgets, QtGui, QtMultimedia

class MyWindow(QtWidgets.QWidget):
    def __init__(self, parent = None):
        super().__init__(parent)

        self.list = []   # сюда нужно записать длину аудиофайлов

        self.player = QtMultimedia.QMediaPlayer()

        box = QtWidgets.QHBoxLayout(self)

        play_btn = QtWidgets.QPushButton('Play', clicked = self.play)
        box.addWidget(play_btn)

        folder_btn = QtWidgets.QPushButton('New Folder', clicked = self.choose_folder)
        box.addWidget(folder_btn)

    def play(self):
        self.song = 'C:/Users/User/Desktop/music/8.Figure09.mp3'
        self.player.setMedia(QtMultimedia.QMediaContent(QtCore.QUrl(self.song)))
        self.player.play()

    def choose_folder(self):
        # тут происходит выбор директории с аудиофайлами
        songs = ['C:/Users/User/Desktop/music/13.Numb.mp3',
                 'C:/Users/User/Desktop/music/10.From the Inside.mp3']
        # в итоге я получаю такой список ^^^
        self.extract_length(songs)

    def extract_length(self, songs):
        pass
        # тут нужно извлечь длину аудиофайлов из self.songs и записать в self.list
        # при этом self.player уже будет воспроизводить звук


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = MyWindow()
    window.setWindowTitle(' ')
    window.show()
    sys.exit(app.exec_())

Возможно, я мог что-то неправильно понять. Возможно, я что-то плохо объяснил. Пожалуйста, напишите, если что-то не так


Ответы (2 шт):

Автор решения: Alexander Chernin

Вообще, извлечение длительности проигрывания медиа-файла (duration) до начала воспроизведения для Qt, задача не тривиальная. Но можно попробовать один Qt-hack:

def extract_length(self, songs):
    # В процессе получения длительностей можно/нужно
    # заблокировать соответствующие виджеты интерфейса,
    # чтобы исключить вмешательство пользователя в процесс
    # получения длительностей медиа-файлов
    songs = ['C:/Users/User/Desktop/music/13.Numb.mp3',
             'C:/Users/User/Desktop/music/10.From the Inside.mp3']

    playlist = player.playlist()
    if not playlist:
        playlist = QMediaPlaylist()

    # Текущее количество элементов в листе
    # для пустого и нового 0
    pos = playlist.mediaCount()

    # Наполняем плейлист
    for song in songs:
        playlist.addMedia(QMediaContent(QUrl(song)))

    player.setPlaylist(playlist)
    # устанавливаем текущий индекс файла в листа
    # для получения длительностей
    player.playlist().setCurrentIndex(pos)

    player.durationChanged.connect(self.fileDuration)
    # Убираем звук
    player.setMuted(true)
    # Стартуем программное проигрывание файлов из плейлиста
    player.play()

def fileDuration(self, duration_value):        
    cur_index = player.playlist().currentIndex()
    if cur_index >= player.playlist().mediaCount():
        # дисконектимся от слота
        player.durationChanged.disconnect(self.fileDuration)
        # останавливаем программное проигрывание файлов
        player.stop()
        # Разблокируем виджеты интерфейса
    else:
        # по текущему индексу cur_index (например в QListWidget)
        # записываем текущее значение duration_value
        ...
        # вызываем следующий медиа-файл из медиа-списка
        player.playlist().next()
→ Ссылка
Автор решения: S. Nick

Я не понимаю зачем вы пишите в БД информацию о длительност песни, она рассчитывается мгновенно и это по моему излишнее.

Где вы прочитали, что вы не можете создавать новый источник звука плееру? Обратите внимание на объект player2.

В моем примере вы увидите два варианта получения длительность аудиофайлов:

  • метод time_duration;
  • метод time_duration2.

Обратите внимание как эти методы вызываются.

Я увидел вашу задачу так:

from PyQt5 import QtCore, QtWidgets, QtGui, QtMultimedia

class MyWindow(QtWidgets.QWidget):
    def __init__(self, parent = None):
        super().__init__(parent)
        self.list = []   # сюда нужно записать длину аудиофайлов
        self.player = QtMultimedia.QMediaPlayer()
        self.player.durationChanged.connect(self.time_duration)                           # +++

        play_btn = QtWidgets.QPushButton('Play', clicked = self.play)
        folder_btn = QtWidgets.QPushButton('New Folder', clicked = self.choose_folder)

        # self.options - какой-то список существующей музыки, которуй можно выбирать и проигрывать 
        self.list_widget = QtWidgets.QListWidget()                                        # +++
        self.options = ['D:/_Qt/__Qt/sound/sound_ringtimer.mp3',
                        'D:/_Qt/EXE/Mp3/new_build/QThread_mp3/img/zvuki_prirody.mp3']
        self.list_widget.addItems([ s.split('/')[-1] for s in self.options ])
        self.list_widget.currentRowChanged.connect(self.on_itemClicked)

        self.row = 0   # индекс строки списка options, кот. можео проигрывать

        box = QtWidgets.QGridLayout(self)   
        box.addWidget(self.list_widget, 0, 0, 1, 2)
        box.addWidget(play_btn, 1, 0)
        box.addWidget(folder_btn, 1, 1)

    def on_itemClicked(self, row):
        self.row = row                     # мы выбрали другию стороку

    def time_duration(self, d):
        m = d // 1000 // 60
        s = d // 1000 % 60
        # Pедактируем выбранный элемент списка QListWidget 
        itemNumber = self.list_widget.currentRow()
        item       = self.list_widget.item(itemNumber)
        if f'{m:>1}:{s:0>2}' != '0:00' and item.text()[-1] != '.':
            text = f'{item.text()} -> {m:>1}:{s:0>2}.' 
            item.setText(text)        

    def play(self):
        self.song = self.options[self.row]
        self.player.setMedia(QtMultimedia.QMediaContent(QtCore.QUrl(self.song)))
        self.player.play()

    def choose_folder(self):
        # тут происходит выбор директории с аудиофайлами
        songs = ['D:/_Qt/__Qt/bibi.mp3',
                 'D:/_Qt/__Qt/sound/sound_ringtimer.mp3',
                 'D:/_Qt/EXE/Mp3/new_build/QThread_mp3/img/zvuki_prirody.mp3',
                 'D:/_Qt/__Qt/bibi.mp3',]
        # в итоге я получаю такой список ^^^
        self.extract_length(songs)

    def extract_length(self, songs):
        # pass
        # тут нужно извлечь длину аудиофайлов из self.songs и записать в self.list
        # при этом self.player уже будет воспроизводить звук

        for song in songs:
            player2 = QtMultimedia.QMediaPlayer()                                    # ! player2 
            player2.setMedia(QtMultimedia.QMediaContent(QtCore.QUrl(song)))
            player2.play()
            player2.setVolume(0.0)
            QtCore.QTimer.singleShot(150, lambda p=player2, song=song: self.time_duration2(p, song))

    def time_duration2(self, p, song): #d):
        d = p.duration()
        m = d // 1000 // 60
        s = d // 1000 % 60
        # Добавляем элементы в список QListWidget 
        item = QtWidgets.QListWidgetItem()
        item.setText(f'{song.split("/")[-1]} - {m:>1}:{s:0>2} -> {d}')
        self.list_widget.addItem(item)
        self.options.append(song)


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = MyWindow()
    window.setWindowTitle(' ')
    window.show()
    sys.exit(app.exec_())

введите сюда описание изображения

→ Ссылка