Esempio n. 1
0
class MyWidget(QMainWindow):
    def __init__(self):
        super().__init__()
        #  !Костыль
        #  Запрещаю менять размер окна, т.к. не хочу работать с layout`ами
        self.setFixedSize(656, 532)
        self.setWindowIcon(QIcon('icons/icon.ico'))

        #  Добавляем эквалайзер громкости
        self.equalizer = QVolumeEq((255, 255, 102), (255, 26, 10), bg_color=(0, 0, 0, 20))
        self.equalizer.setParent(self)
        self.equalizer.move(20, 391)
        self.equalizer.resize(341, 31)

        uic.loadUi('icons/djap.ui', self)

        self.lightThemeAction.triggered.connect(self.change_theme)
        self.darkThemeAction.triggered.connect(self.change_theme)

        #  Создаём организатор плейлистов
        self.playlist_handler = QPlaylistHandler()
        self.playlist_handler.setParent(self)
        self.playlist_handler.move(380, 40)

        #  Настраиваем меню
        self.saveOption.triggered.connect(self.playlist_handler.save_playlist)
        self.saveAsOption.triggered.connect(self.playlist_handler.save_playlist_as)
        self.deleteOption.triggered.connect(self.playlist_handler.delete_playlist)
        self.aboutOption.triggered.connect(self.about)
        self.helpOption.triggered.connect(self.help)
        self.repeatModeOption.triggered.connect(self.playlist_handler.change_playmode)
        self.oneModeOption.triggered.connect(self.playlist_handler.change_playmode)
        self.randomModeOption.triggered.connect(self.playlist_handler.change_playmode)

        #  Реализация проигрывателя и основного плейлиста
        self.player = QMediaPlayer()
        self.player.durationChanged.connect(self.update_song)
        self.player.setVolume(25)
        self.player.positionChanged.connect(self.update_timeline_position)
        self.queue = QMediaPlaylist(self.player)
        self.player.setPlaylist(self.queue)

        self.queue.setPlaybackMode(QMediaPlaylist.Loop)
        self.is_looped_queue = True

        #  Загрузка музыки из плейлиста
        self.load_songs_from_playlist()

        #  Подключаем кнопки к их функциям
        self.is_playing = False
        self.playBtn.clicked.connect(self.play_or_pause)
        self.queue.currentMediaChanged.connect(self.check_to_stop)

        self.nextBtn.clicked.connect(self.next_audio)
        self.prevBtn.clicked.connect(self.prev_audio)

        self.is_looped_current_track = False
        self.loopBtn.clicked.connect(self.loop_or_unloop_current_track)

        #  Настройка слайдера звука
        self.volumeSlider.hide()
        self.volumeSlider.setValue(25)
        self.equalizer.setValue(25)
        self.volumeSlider.sliderReleased.connect(self.volumeSlider.hide)
        self.volumeSlider.valueChanged.connect(self.change_volume)
        self.volumeBtn.clicked.connect(self.change_volume)

        #  Настройка таймлайна
        self.is_timeline_dragged = False
        self.timeline.sliderReleased.connect(self.timeline_changed)
        self.timeline.sliderPressed.connect(self.timeline_is_dragged)

        #  !Костыль
        #  Имитируем запуск аудиодорожки, чтобы подгрузить данные
        self.play_or_pause()
        self.play_or_pause()
        self.icon_changed()

    def palette(self):
        """Функция для создания темы"""
        con = sqlite3.connect('user_data.sqlite')
        cur = con.cursor()
        if int(cur.execute('SELECT * FROM theme').fetchone()[0]):
            palette = QPalette()
            palette.setColor(QPalette.Window, QColor(240, 240, 240))
            palette.setColor(QPalette.WindowText, Qt.black)
            palette.setColor(QPalette.Base, Qt.white)
            palette.setColor(QPalette.AlternateBase, QColor(246, 246, 246))
            palette.setColor(QPalette.ToolTipBase, QColor(255, 255, 240))
            palette.setColor(QPalette.ToolTipText, Qt.black)
            palette.setColor(QPalette.Text, Qt.black)
            palette.setColor(QPalette.Button, QColor(240, 240, 240))
            palette.setColor(QPalette.ButtonText, Qt.black)
            palette.setColor(QPalette.BrightText, Qt.white)
            palette.setColor(QPalette.Highlight, QColor(0, 120, 215).lighter())
            palette.setColor(QPalette.HighlightedText, Qt.white)
        else:
            palette = QPalette()
            palette.setColor(QPalette.Window, QColor(53, 53, 53))
            palette.setColor(QPalette.WindowText, Qt.white)
            palette.setColor(QPalette.Base, QColor(15, 15, 15))
            palette.setColor(QPalette.AlternateBase, QColor(53, 53, 53))
            palette.setColor(QPalette.ToolTipBase, Qt.white)
            palette.setColor(QPalette.ToolTipText, Qt.white)
            palette.setColor(QPalette.Text, Qt.white)
            palette.setColor(QPalette.Button, QColor(53, 53, 53))
            palette.setColor(QPalette.ButtonText, Qt.white)
            palette.setColor(QPalette.BrightText, Qt.red)
            palette.setColor(QPalette.Highlight, QColor(142, 45, 197).lighter())
            palette.setColor(QPalette.HighlightedText, Qt.black)
        return palette

    def change_theme(self):
        """Функция для смены темы"""
        con = sqlite3.connect('user_data.sqlite')
        cur = con.cursor()
        if self.sender() == self.lightThemeAction:
            cur.execute('UPDATE theme SET isit = 1')
        else:
            cur.execute('UPDATE theme SET isit = 0')
        con.commit()

        QMessageBox.warning(self, 'DJust Audio Player', 'Чтобы изменения вступили в силу, '
                                                        'перезапустите приложение.', QMessageBox.Ok)

    def help(self):
        """Функция для показа формы справки"""
        self.help = QHelp()

    def about(self):
        """Функция для показа формы О программе"""
        self.about = QAbout()

    def delete_song_from_mediaplayer(self, index):
        """Удалить трек из медиаплеера"""
        self.queue.removeMedia(index)

    def add_song_to_mediaplayer(self, url):
        """Добавить трек в медиаплеер"""
        self.queue.addMedia(QMediaContent(QUrl(url)))

    def change_track_by_click(self, row):
        """Функция для смены трека по нажатию в списке"""
        self.queue.setCurrentIndex(row)

    def check_to_stop(self):
        """Проверяем, чтобы плеер не играл с пустым плейлистом"""
        if self.queue.isEmpty() and self.is_playing:
            self.play_or_pause()

    def load_songs_from_playlist(self):
        """Загрузка музыки из БД в плейлист"""
        self.queue.clear()
        for url in self.playlist_handler.urls():
            self.queue.addMedia(QMediaContent(QUrl(url)))

    def icon_changed(self):
        """Функция для вывода обложки трека"""

        def noicon():
            """В случае отсутствия обложки эта функция ставить свою заглушку"""
            try:
                pixmap = QPixmap()
                if pixmap.load('icons/noicon.png'):
                    self.audioPic.setPixmap(pixmap)
            except Exception as e:
                print(e.__class__.__name__, ': ', e, sep='')

        try:
            #  Читаем метаданные трека
            n = self.queue.currentIndex()
            url = self.playlist_handler.urls()[n if n > -1 else 0]
            file = File(url)
            for i in file.tags.keys():
                if 'APIC:' in i:
                    artwork = file.tags[i].data
                    break
            else:
                raise KeyError
            #  Переводим их в байтовый массив
            ba = QByteArray(artwork)
            #  И загружаем на форму, если это возможно
            pixmap = QPixmap()
            if pixmap.loadFromData(ba):
                self.audioPic.setPixmap(pixmap.scaled(341, 341, Qt.IgnoreAspectRatio))
            else:
                raise KeyError
        except Exception as e:
            #  print(e.__class__.__name__, ': ', e, sep='')
            noicon()

    def timeline_is_dragged(self):
        """Мини-функция для изменения переменной"""
        #  При вызове этой функции мы понимаем, что пользователь
        #  взаимодействует с таймлайном трека
        self.is_timeline_dragged = True

    def timeline_changed(self):
        """Функция для пользовательской перемотки данного трека"""
        #  Перематываем позицию плеера
        self.player.setPosition(self.timeline.value() * 1000)

        #  Пользователь отпустил язычок слайдера и больше не взаимодействует с таймлайном
        self.is_timeline_dragged = False

        #  Вызываем обновление временной шкалы, т.к. произошла перемотка
        self.update_timeline_position()

    def update_song(self):
        """Изменение данных о треке"""
        #  !Костыль
        #  Проматываем несколько миллисекунд, чтобы избежать повреждённых треков
        self.player.setPosition(10)

        #  Меняем обложку
        self.icon_changed()

        #  Выделяем в списке новый трек
        self.playlist_handler.set_current_select(self.queue.currentIndex())

        #  Меняем название окна в соответствии с играющим треком
        title = self.queue.currentMedia().canonicalUrl().path().split('/')[-1]
        if title:
            self.setWindowTitle('DJust Audio Player | %s' % title)
        else:
            self.setWindowTitle('DJust Audio Player')

        #  Изменяем длину таймлайна на длину трека
        self.timeline.setMaximum(self.player.duration() // 1000)

        #  Выводим длину трека в минутах/секундах на экран
        minutes, seconds = self.player.duration() // 1000 // 60, self.player.duration() // 1000 % 60
        self.endTimeLabel.setText(str(minutes) + ':{:02d}'.format(seconds))

    def update_timeline_position(self):
        """Функция для обновления временной шкалы"""
        #  Выводим текущее положение плеера в минутах/секундах
        minutes, seconds = self.player.position() // 1000 // 60, self.player.position() // 1000 % 60
        self.currentTimeLabel.setText(str(minutes) + ':{:02d}'.format(seconds))

        #  Чтобы не "вырывать" язычок слайдера из рук пользователя,
        #  проверяем, что пользователь НЕ взаимодейтсвует с таймлайном.
        if not self.is_timeline_dragged:
            self.timeline.setValue(minutes * 60 + seconds)

    def loop_or_unloop_current_track(self):
        """Мини-функция для зацикливания данного трека"""
        if not self.is_looped_current_track:
            #  Зацикливаем
            self.is_looped_current_track = True
            self.queue.setPlaybackMode(QMediaPlaylist.CurrentItemInLoop)
            #  Меняем картинку на перечёркнутый круг
            self.loopBtn.setStyleSheet('background-image: url(icons/unloop.png)')
        else:
            #  убираем цикл
            self.is_looped_current_track = False
            self.queue.setPlaybackMode(self.playlist_handler.mode)
            self.loopBtn.setStyleSheet('background-image: url(icons/loop.png)')

    def next_audio(self):
        """Функция для перемотки на следующий в списке трек"""
        if self.queue.mediaCount() - 1 == self.queue.currentIndex():
            #  Если трек ппоследний в списке, возвращаемся к первому
            self.queue.setCurrentIndex(0)
        else:
            #  Иначе просто перемещаемся к следующему
            self.queue.next()

    def prev_audio(self):
        """Функция для перемотки на предыдущий в списке трек"""
        if 0 == self.queue.currentIndex():
            #  Если трек первый в списке, возвращаемся к последнему
            self.queue.setCurrentIndex(self.queue.mediaCount() - 1)
        else:
            #  Иначе просто возвращаемся к предыдущему
            self.queue.previous()

    def play_or_pause(self):
        """Функция для запуска или остановки проигрывателя"""
        if self.queue.isEmpty():
            #  Меняем картинку на стрелочку (проигрывание)
            self.is_playing = False
            self.playBtn.setStyleSheet('background-image: url(icons/play.png)')
            #  Ставим на паузу
            self.player.pause()
            return
        if self.is_playing:
            #  Меняем картинку на стрелочку (проигрывание)
            self.is_playing = False
            self.playBtn.setStyleSheet('background-image: url(icons/play.png)')
            #  Ставим на паузу
            self.player.pause()
        else:
            #  Меняем картинку на 2 палочки (пауза)
            self.is_playing = True
            self.playBtn.setStyleSheet('background-image: url(icons/pause.png)')
            #  Запускаем звук
            self.player.play()

    def change_volume(self):
        """Мини-Функция для передачи уровня громкости в плеер"""
        #  Функцию использует несколько объектов сразу, проверяем вызывателя
        if self.sender() == self.volumeBtn:
            #  Если функцию вызвала кнопка, то включаем/выключаем отображение слайдера
            if not self.volumeSlider.isHidden():
                self.volumeSlider.hide()
            else:
                self.volumeSlider.show()
        else:
            #  Иначе функцию вызвал сам слайдер. Значит, меняем значение
            self.player.setVolume(self.volumeSlider.value())
            self.equalizer.setValue(self.volumeSlider.value())
class Player(QMediaPlayer):
    def __init__(self, parent=None):
        super(Player, self).__init__(parent)
        self.parent = parent
        self.player = QMediaPlayer()
        self.queueList = QMediaPlaylist()
        self.player.setPlaylist(self.queueList)
        self.queueData = []
        self.position = 0
        self.volume = 100

        self.player.mediaStatusChanged.connect(self.qmp_mediaStatusChanged)
        self.player.positionChanged.connect(self.qmp_positionChanged)
        self.player.durationChanged.connect(self.durationChanged)
        self.queueList.currentIndexChanged.connect(self.playlistPosChanged)

    def add(self, data):
        """Add track to the queue"""
        queueData = {
            'pc_title': data['pc_title'],
            'title': data['title'],
            'url': data['url'],
            'date': data['date_format'],
            'description': data['description']
        }
        self.queueData.append(queueData)
        self.queueList.addMedia(QMediaContent(QUrl(data['url'])))

    def playPause(self):
        icon = QIcon.fromTheme("media-playback-pause")

        if self.player.state() == QMediaPlayer.StoppedState:
            if self.player.mediaStatus() == QMediaPlayer.NoMedia:
                if self.queueList.mediaCount() != 0:
                    self.player.play()
            elif self.player.mediaStatus() == QMediaPlayer.LoadedMedia:
                self.queueList.setCurrentIndex(self.position)
                self.player.play()
            elif self.player.mediaStatus() == QMediaPlayer.BufferedMedia:
                self.player.play()
        elif self.player.state() == QMediaPlayer.PlayingState:
            icon = QIcon.fromTheme("media-playback-start")
            self.player.pause()
        elif self.player.state() == QMediaPlayer.PausedState:
            self.player.play()

        self.parent.playBtn.setIcon(icon)

    def startPlay(self):
        data = self.queueData[0]
        self.queueList.setCurrentIndex(0)
        self.parent.curPCLabel.setText(data['pc_title'])
        self.parent.curTrackName.setText(data['title'])
        self.player.play()
        icon = QIcon.fromTheme("media-playback-pause")
        self.parent.playBtn.setIcon(icon)

    def stop(self):
        self.player.stop()
        icon = QIcon.fromTheme("media-playback-start")
        self.parent.playBtn.setIcon(icon)

    def setPosition(self, pos):
        self.player.setPosition(pos)

    def durationChanged(self, duration):
        total_time = '0:00:00'
        duration = self.player.duration()
        total_time = ms_to_time(duration)
        self.parent.timeSlider.setMaximum(duration)
        self.currentTrackDuration = duration
        self.parent.totalTimeLabel.setText(total_time)

    def qmp_mediaStatusChanged(self, status):
        icon = QIcon.fromTheme("media-playback-pause")
        if self.player.state() == QMediaPlayer.StoppedState:
            icon = QIcon.fromTheme("media-playback-start")
        elif self.player.state() == QMediaPlayer.PausedState:
            icon = QIcon.fromTheme("media-playback-start")

        self.parent.playBtn.setIcon(icon)

    def qmp_positionChanged(self, position, senderType=False):
        self.currentTime = position
        current_time = '0:00:00'

        if position != -1:
            current_time = ms_to_time(position)
            self.parent.timeLabel.setText(current_time)

        self.parent.timeSlider.blockSignals(True)
        self.parent.timeSlider.setValue(position)
        self.parent.timeSlider.blockSignals(False)

    def playlistPosChanged(self):
        pos = self.queueList.currentIndex()
        data = self.queueData[pos]
        self.parent.curPCLabel.setText(data['pc_title'])
        self.parent.curTrackName.setText(data['title'])
        windowTitle = '{0} - {1}'.format(data['pc_title'], data['title'])
        self.parent.setWindowTitle(windowTitle)
        if self.queueList.mediaCount() > 1:
            if pos < self.queueList.mediaCount() - 1:
                self.parent.queueNextBtn.setEnabled(True)
            else:
                self.parent.queueNextBtn.setEnabled(False)

            if pos > 0:
                self.parent.queuePrevBtn.setEnabled(True)
            else:
                self.parent.queuePrevBtn.setEnabled(False)

            if pos < self.queueList.mediaCount():
                prevPos = 0
                if self.position < pos:
                    prevPos = pos - 1
                else:
                    prevPos = pos + 1
                prevItem = self.parent.queueList.item(prevPos)
                prevWidget = self.parent.queueList.itemWidget(prevItem)
                if prevItem:
                    prevWidget.statusIcon.setPixmap(QPixmap())

        self.position = pos
        item = self.parent.queueList.item(pos)
        widget = self.parent.queueList.itemWidget(item)
        if widget:
            icon = QIcon.fromTheme("media-playback-start")
            widget.statusIcon.setPixmap(icon.pixmap(16, 16))

    def setVolume(self, volume):
        self.player.setVolume(volume)

    def rev10Secs(self):
        position = self.player.position()
        new_pos = position - 10000
        self.player.setPosition(new_pos)

    def for10Secs(self):
        position = self.player.position()
        new_pos = position + 10000
        self.player.setPosition(new_pos)

    def delete(self, position):
        """ Delete the track and her data from position"""
        self.queueData.pop(position)
        self.queueList.removeMedia(position)
        if (position == self.position):
            self.playlistPosChanged()
Esempio n. 3
0
class Control:
    """A class that handles the logic behind the program by manipulating the GUI classes
    and calling their methods in response to received signals."""

    MAGIC = b"\x01\xff"

    def __init__(self, screens: list) -> None:
        self.player = QMediaPlayer()
        self.player.setAudioRole(QAudio.MusicRole)
        self.playlist = QMediaPlaylist()
        self.player.setPlaylist(self.playlist)
        self.mainWindow = MainWindow(self, screens)
        self.mainArea = self.mainWindow.centralWidget().upperBox.mainArea
        self.songList = self.mainWindow.centralWidget().upperBox.songList
        self.mediaControlArea = self.mainWindow.centralWidget(
        ).mediaControlArea
        self.mainWindow.show()
        self.library = None
        self.currentSong = None
        self.playing = False
        self.random = False
        self.repeat = 0
        self.volume = 50

        self.volumeChange(self.volume)
        self.mediaControlArea.volumeControl.setValue(self.volume)

        self.mainTimer = QTimer()
        self.mainTimer.setInterval(100)
        self.mainTimer.timeout.connect(self.updateSongProgress)
        self.mainTimer.start()

        self.libraryUpdateTimer = QTimer()
        self.libraryUpdateTimer.setInterval(15_000)
        self.libraryUpdateTimer.timeout.connect(self.updateLibrary)
        self.libraryUpdateTimer.start()

        self._connection = None
        self.connections()

        self.displayedType = None
        self.displayedName = None
        self.types = None
        self.songListWidth = None
        values = self.load()
        if values:
            self.mainWindow.setGeometry(*values)
            self.volumeChange(self.volume)
            self.mediaControlArea.volumeControl.setValue(self.volume)

        self.setUpTimer = QTimer()
        self.setUpTimer.setInterval(20)
        self.setUpTimer.setSingleShot(True)
        self.setUpTimer.timeout.connect(self.setAreas)
        self.setUpTimer.start()

    def connections(self):
        self.player.currentMediaChanged.connect(self.updateCurrentSong)
        self.player.durationChanged.connect(self.updateSongProgressRange)
        self.player.stateChanged.connect(self.playerStatusChanged)
        self.mediaControlArea.previousButton.click.connect(
            self.playlist.previous)
        self.mediaControlArea.repeatButton.click.connect(
            self.repeatButtonClick)
        self.mediaControlArea.stopButton.click.connect(self.stopButtonClick)
        self.mediaControlArea.playButton.click.connect(self.playButtonClick)
        self.mediaControlArea.randomButton.click.connect(
            self.randomButtonClick)
        self.mediaControlArea.nextButton.click.connect(self.playlist.next)
        self.mediaControlArea.muteButton.click.connect(self.mute)
        self.mediaControlArea.songProgress.sliderMoved.connect(
            self.songProgressMove)
        self.mediaControlArea.volumeControl.sliderMoved.connect(
            self.volumeChange)

    def setAreas(self) -> None:
        """Called after the GUI is created to provide user with a feedback
        that the program is running in case a larger amount of songs will be added
        when the Library class is initialized."""
        # TODO add a tooltip that will notify the user larger amount of songs is being loaded
        #  (freezes the program as the execution moves to the Library class.)
        self.library = library.Library()
        self.types = {
            "artist": self.library.getSongsForArtist,
            "album": self.library.getSongsForAlbum,
            "playlist": self.library.getSongsForPlaylist
        }
        self.mainArea.setAreas(self.library)
        self.setUpTimer.deleteLater()
        self.setUpTimer = None
        self.getSongs(self.displayedType, self.displayedName)
        if self.songListWidth is not None:
            songListGeometry = self.songList.geometry()
            self.songList.preferredWidth = songListGeometry.width(
            ) - self.songListWidth
            self.mainWindow.centralWidget().upperBox.line.resizeWidgets(
                songListGeometry.width() - self.songListWidth)

    def updateLibrary(self) -> None:
        self.library.update()
        self.mainArea.updateView(self.library)

    def updateCurrentSong(self) -> None:
        """Update all areas that may display information about the currently
        playing song - SongList, Now Playing tab, BottomBox"""
        media = self.player.currentMedia()
        self.currentSong = media.request().url().toLocalFile().replace(
            "/", "\\")
        if self.currentSong in self.library.library:
            self.songList.updateActiveSong(self.currentSong)
            self.mainArea.updateActiveSong(self.playlist.currentIndex())
            songEntry = self.library.library[self.currentSong]
            self.mediaControlArea.updateSongInfo(
                f"{songEntry[ARTIST]} - {songEntry[NAME]}")

    def updateSongProgressRange(self) -> None:
        """Updates the range of the slider that represents the song position."""
        self.mediaControlArea.updateSongProgressRange(self.player.duration())

    def playerStatusChanged(self) -> None:
        """Used to properly update the player look after the current playlist has finished."""
        index = self.playlist.currentIndex()
        if index == -1:
            self.stopButtonClick()

    def getSongs(self, isType: str, name: str) -> None:
        """Retrieves the songs for a given artist, album or playlist based on type
        and passes the resulting list to the SongList class."""
        if isType is None:
            isType = self.displayedType
        if name is None:
            name = self.displayedName
        orderBy = self.songList.buttonOrderBy.text()
        reverse = True if self.songList.buttonOrderReverse.text() == chr(
            0x25bc) else False
        listForType = self.types[isType](name, orderBy, reverse)
        playlist = None
        if isType == "playlist":
            playlist = name
        if len(listForType) == 0:
            artists = self.library.artists
            if len(artists):
                listForType = self.library.getSongsForArtist(artists[0])
        self.songList.updateSongList(listForType, self.library.library,
                                     self.currentSong, playlist, isType)
        self.displayedType = isType
        self.displayedName = name

    def playSongList(self, song: str = None) -> None:
        """Called when user double-clicks on an artist/album/playlist widget or a song
        in right-hand side panel."""
        self.playlist.clear()
        index = 0
        loopIndex = 0
        for songPath in self.songList.garbageProtector:
            if song == songPath:
                index = loopIndex
            self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(songPath)))
            loopIndex += 1
        if self.playlist.isEmpty():
            return
        self.player.play()
        if index > 0:
            self.playlist.setCurrentIndex(index)
        self.playing = True
        self.mediaControlArea.playButton.updatePictures(
            bottom.pausePixmap, bottom.pauseHoverPixmap, False)
        self.mainArea.setNowPlayingArea(self.library)
        self.mainArea.updateActiveSong(self.playlist.currentIndex())

    def playSongWidget(self,
                       songPath: str,
                       afterCurrent: bool = False) -> None:
        if afterCurrent:
            index = self.playlist.currentIndex() + 1
            self.playlist.insertMedia(
                index, QMediaContent(QUrl.fromLocalFile(songPath)))
        else:
            self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(songPath)))
        self.mainArea.setNowPlayingArea(self.library)
        self.mainArea.updateActiveSong(self.playlist.currentIndex())
        self.playing = True

    def removeFromNowPlaying(self, widget) -> None:
        if self.playlist.mediaCount() > 1:
            for row in range(self.mainArea.nowPlayingLayout.rowCount()):
                for column in range(
                        self.mainArea.nowPlayingLayout.columnCount()):
                    if self.mainArea.nowPlayingLayout.itemAtPosition(
                            row, column).widget() is widget:
                        self.playlist.removeMedia(row - 1)
                        break
                else:
                    continue
                break
        else:
            self.stopButtonClick()
            self.playlist.clear()
        self.mainArea.setNowPlayingArea(self.library)
        if self.playing:
            self.mainArea.updateActiveSong(self.playlist.currentIndex())

    def playMediaWidget(self, isType: str, target: str, startOver: bool,
                        afterCurrent: bool) -> None:
        """Called from MediaWidget - plays all songs for MediaWidget's type and name."""
        if startOver:
            self.playlist.clear()
        if afterCurrent:
            index = self.playlist.currentIndex() + 1
            for songPath in self.types[isType](target):
                self.playlist.insertMedia(
                    index, QMediaContent(QUrl.fromLocalFile(songPath)))
                index += 1
        else:
            for songPath in self.types[isType](target):
                self.playlist.addMedia(
                    QMediaContent(QUrl.fromLocalFile(songPath)))
        if startOver:
            self.player.play()
            self.playing = True
            self.mediaControlArea.playButton.updatePictures(
                bottom.pausePixmap, bottom.pauseHoverPixmap, False)
        self.mainArea.setNowPlayingArea(self.library)
        self.mainArea.updateActiveSong(self.playlist.currentIndex())

    def playFromNowPlaying(self, song: str) -> None:
        """Called when user double-clicks on a song in the Now Playing tab."""
        for n in range(self.playlist.mediaCount()):
            media = self.playlist.media(n)
            if song == media.request().url().toLocalFile().replace("/", "\\"):
                self.playlist.setCurrentIndex(n)
                if not self.playing:
                    self.player.play()
                    self.playing = True
                return

    def createPlaylist(self, playlistName: str) -> None:
        self.library.createPlaylist(playlistName)
        self.mainArea.setMainAreaPlaylists(self.library)

    def addToExistingPlaylist(self, playlist: str, songOrWidget: str,
                              isType: str) -> None:
        if isType in self.types:
            for song in self.types[isType](songOrWidget):
                self.library.addToPlaylist(playlist, song)
        else:
            self.library.addToPlaylist(playlist, songOrWidget)
        self.library.update()

    def removeFromPlaylist(self, playlist: str, song: str) -> None:
        self.library.deleteFromPlaylist(playlist, song)
        self.mainArea.setMainAreaPlaylists(self.library)
        self.library.update()
        self.getSongs("playlist", playlist)

    def renamePlaylist(self, playlistName: str, newPlaylistName: str) -> None:
        self.library.renamePlaylist(playlistName, newPlaylistName)
        self.mainArea.setMainAreaPlaylists(self.library)
        self.library.update()

    def deletePlaylist(self, playlistName: str) -> None:
        self.library.deletePlaylist(playlistName)
        self.mainArea.setMainAreaPlaylists(self.library)
        self.library.update()

    def addWatchedFolder(self, folder: str) -> None:
        """Adds a folder to the Library class. all mp3 files within the folder
        and its sub-folders will be added to the library and accessible to the player."""
        self.library.addFolder(folder.replace("/", "\\"))
        self.mainArea.updateView(self.library)

    def removeWatchedFolder(self, folder: str) -> None:
        """Removes folder from the library, updates view and stops playback if
        the current song was in the now-removed folder."""
        self.library.deleteFolder(folder)
        self.mainArea.updateView(self.library)
        if self.currentSong not in self.library.library:
            self.songList.updateSongList([], [], "", "")
            self.player.stop()
            self.playlist.clear()
            self.mediaControlArea.updateSongInfo("")
            self.songList.nowPlayingSong = None
            self.mainArea.nowPlayingSong = None
            self.playing = False
            self.mediaControlArea.updatePlayButton(self.playing, False)

    def playButtonClick(self, passMove: bool = True) -> None:
        if not self.playing:
            if self.playlist.isEmpty():
                self.playSongList()
                return
            self.playing = True
            self.player.play()
            self.mainArea.updateActiveSong(self.playlist.currentIndex())
            self.songList.updateActiveSong(self.currentSong)
        else:
            self.playing = False
            self.player.pause()
        self.mediaControlArea.updatePlayButton(self.playing, passMove)

    def repeatButtonClick(self) -> None:
        if self.repeat == 0:
            self.repeat = 1
            self.playlist.setPlaybackMode(QMediaPlaylist.Loop)
        elif self.repeat == 1:
            self.repeat = 2
            self.playlist.setPlaybackMode(QMediaPlaylist.CurrentItemInLoop)
        elif self.repeat == 2:
            self.repeat = 0
            self.playlist.setPlaybackMode(QMediaPlaylist.Sequential)
        self.mediaControlArea.updateRepeatButton(self.repeat)

    def randomButtonClick(self) -> None:
        if not self.random:
            self.random = True
            self.playlist.setPlaybackMode(QMediaPlaylist.Random)
        else:
            self.random = False
            self.playlist.setPlaybackMode(QMediaPlaylist.Sequential)
        self.mediaControlArea.updateRandomButton(self.random)

    def stopButtonClick(self) -> None:
        self.playing = False
        self.player.stop()
        if self.songList.nowPlayingSong is not None:
            self.songList.nowPlayingSong.clear()
        if self.mainArea.nowPlayingSong is not None:
            self.mainArea.nowPlayingSong.clear()
        self.mediaControlArea.updatePlayButton(self.playing, False)

    def mute(self) -> None:
        if not self.player.isMuted():
            self.player.setMuted(True)
            self.mediaControlArea.showMute()
        else:
            self.player.setMuted(False)
            self.volumeChange(self.volume)

    def volumeChange(self, volume: int) -> None:
        logVolume = QAudio.convertVolume(volume / 100,
                                         QAudio.LogarithmicVolumeScale,
                                         QAudio.LinearVolumeScale) * 100
        self.player.setVolume(logVolume)
        self.volume = volume
        self.mediaControlArea.updateVolumeBar(volume)

    def songProgressMove(self, position: int) -> None:
        self.player.setPosition(position)

    def updateSongProgress(self) -> None:
        position = self.player.position()
        if 0 <= position < 2_000_000_000:
            if self.player.state() > 0:
                self.mediaControlArea.updateSongProgress(position)
                if self.playing:
                    self.songList.activeSongPixmap()
                    self.mainArea.activeSongPixmap()
            else:
                self.mediaControlArea.updateSongProgress(0)

    def disconnect(self):
        self.player.currentMediaChanged.disconnect()
        self.player.durationChanged.disconnect()
        self.player.stateChanged.disconnect()
        self.mediaControlArea.previousButton.click.disconnect()
        self.mediaControlArea.repeatButton.click.disconnect()
        self.mediaControlArea.stopButton.click.disconnect()
        self.mediaControlArea.playButton.click.disconnect()
        self.mediaControlArea.randomButton.click.disconnect()
        self.mediaControlArea.nextButton.click.disconnect()
        self.mediaControlArea.muteButton.click.disconnect()
        self.mediaControlArea.songProgress.sliderMoved.disconnect()
        self.mediaControlArea.volumeControl.sliderMoved.disconnect()

    def close(self) -> None:
        self.disconnect()
        self.player.stop()
        self.mainTimer.stop()
        self.save()

    def save(self) -> None:
        """Called on exit, saves current view, geometry and volume."""
        with gzip.open(r"musicplayer\mpdata", "wb") as fh:
            fh.write(self.MAGIC)
            toBeWritten = struct.pack(f"<h{len(self.displayedType.encode())}s",
                                      len(self.displayedType.encode()),
                                      self.displayedType.encode())
            fh.write(toBeWritten)
            fh.write(self.MAGIC)
            toBeWritten = struct.pack(f"<h{len(self.displayedName.encode())}s",
                                      len(self.displayedName.encode()),
                                      self.displayedName.encode())
            fh.write(toBeWritten)
            fh.write(self.MAGIC)
            geo = self.mainWindow.geometry()
            toBeWritten = struct.pack("<4h", geo.x(), geo.y(), geo.width(),
                                      geo.height())
            fh.write(toBeWritten)
            toBeWritten = struct.pack("<h", self.volume)
            fh.write(toBeWritten)
            toBeWritten = struct.pack("<h", self.songList.width())
            fh.write(toBeWritten)

    def load(self) -> [bool, tuple]:
        """Called on startup, loads view, geometry and volume saved on previous run."""
        try:
            with gzip.open(r"musicplayer\mpdata", "rb") as fh:
                if not fh.read(2) == self.MAGIC:
                    return False
                length = fh.read(2)
                length = struct.unpack("<h", length)[0]
                displayedType = fh.read(length)
                displayedType = struct.unpack(f"<{length}s",
                                              displayedType)[0].decode("utf8")
                if displayedType in ["artist", "album", "playlist"]:
                    self.displayedType = displayedType
                if not fh.read(2) == self.MAGIC:
                    return False
                length = fh.read(2)
                length = struct.unpack("<h", length)[0]
                displayedName = fh.read(length)
                displayedName = struct.unpack(f"<{length}s",
                                              displayedName)[0].decode("utf8")
                if not fh.read(2) == self.MAGIC:
                    return False
                self.displayedName = displayedName
                variables = []
                for n in range(6):
                    var = fh.read(2)
                    var = struct.unpack("<h", var)[0]
                    variables.append(var)
                x, y, width, height, volume, songListWidth = variables
                self.volume = volume
                self.songListWidth = songListWidth
                return x, y, width, height
        except Exception:
            return False
Esempio n. 4
0
class FmvManager(QDockWidget, Ui_ManagerWindow):
    ''' Video Manager '''
    def __init__(self, iface, parent=None):
        super().__init__(parent)
        self.setupUi(self)

        self.parent = parent
        self.iface = iface
        self._PlayerDlg = None
        self.meta_reader = []
        self.initialPt = []
        self.pass_time = 250
        self.buf_interval = 2000
        self.update_interval = 2000
        self.loading = False
        self.playlist = QMediaPlaylist()
        self.VManager.viewport().installEventFilter(self)

        # Context Menu
        self.VManager.customContextMenuRequested.connect(self.__context_menu)
        self.removeAct = QAction(
            QIcon(":/imgFMV/images/mActionDeleteSelected.svg"),
            QCoreApplication.translate("ManagerDock", "Remove from list"),
            self,
            triggered=self.remove)

        self.VManager.setColumnWidth(1, 250)
        self.VManager.setColumnWidth(2, 140)
        self.VManager.setColumnWidth(3, 600)
        self.VManager.setColumnWidth(4, 600)
        self.VManager.setColumnWidth(5, 130)
        self.VManager.verticalHeader().setDefaultAlignment(Qt.AlignHCenter)
        self.VManager.hideColumn(0)

        self.videoPlayable = []
        self.videoIsStreaming = []
        self.dtm_path = parser['GENERAL']['DTM_file']
        draw.setValues()
        self.setAcceptDrops(True)

    def loadVideosFromSettings(self):

        # Get Video Manager List
        VideoList = getVideoManagerList()
        for load_id in VideoList:
            filename = s.value(getNameSpace() + "/Manager_List/" + load_id)
            _, name = os.path.split(filename)

            folder = getVideoFolder(filename)
            klv_folder = os.path.join(folder, "klv")
            exist = os.path.exists(klv_folder)

            if exist:
                self.AddFileRowToManager(name, filename, load_id, exist,
                                         klv_folder)
            else:
                if os.path.isfile(filename):
                    self.AddFileRowToManager(name, filename, load_id)

    def eventFilter(self, source, event):
        ''' Event Filter '''
        if (event.type() == QEvent.MouseButtonPress
                and source is self.VManager.viewport()
                and self.VManager.itemAt(event.pos()) is None):
            self.VManager.clearSelection()
        return QDockWidget.eventFilter(self, source, event)

    @pyqtSlot(QPoint)
    def __context_menu(self, position):
        ''' Context Menu Manager Rows '''
        if self.VManager.itemAt(position) is None:
            return
        menu = QMenu()
        menu.addAction(self.removeAct)
        menu.exec_(self.VManager.mapToGlobal(position))

    def remove(self):
        ''' Remove current row '''
        if self.loading:
            return

        # close video player (safer because it changes playlist internals)
        if self._PlayerDlg is not None:
            self._PlayerDlg.close()
        for cr in self.VManager.selectedItems():
            idx = 0
            # we browse cells but we need lines, so ignore already deleted rows
            try:
                idx = cr.row()
            except Exception:
                continue

            row_id = self.VManager.item(idx, 0).text()
            row_text = self.VManager.item(idx, 1).text()

            self.VManager.removeRow(idx)

            self.videoPlayable.pop(idx)
            self.videoIsStreaming.pop(idx)
            self.initialPt.pop(idx)

            # Remove video to Settings List
            RemoveVideoToSettings(row_id)
            # Remove folder if is local
            RemoveVideoFolder(row_text)

            if self.meta_reader[idx] is not None:
                self.meta_reader[idx].dispose()

            self.meta_reader.pop(idx)

            # remove from playlist
            self.playlist.removeMedia(idx)

    def closePlayer(self):
        ''' Close FMV '''
        try:
            self._PlayerDlg.close()
        except Exception:
            None

    def closeFMV(self):
        ''' Close FMV '''
        try:
            self._PlayerDlg.close()
        except Exception:
            None
        self.close()
        return

    def openStreamDialog(self):
        ''' Open Stream Dialog '''
        self.OpenStream = OpenStream(self.iface, parent=self)
        self.OpenStream.setWindowFlags(Qt.Window | Qt.WindowCloseButtonHint)
        self.OpenStream.exec_()
        return

    def openMuiltiplexorDialog(self):
        ''' Open Multiplexor Dialog '''
        self.Muiltiplexor = Multiplexor(self.iface,
                                        parent=self,
                                        Exts=ast.literal_eval(
                                            parser.get("FILES", "Exts")))
        self.Muiltiplexor.setWindowFlags(Qt.Window | Qt.WindowCloseButtonHint)
        self.Muiltiplexor.exec_()
        return

    def AddFileRowToManager(self,
                            name,
                            filename,
                            load_id=None,
                            islocal=False,
                            klv_folder=None):
        ''' Add file Video to new Row '''
        # We limit the number of videos due to the buffer
        self.loading = True
        if self.VManager.rowCount() > 5:
            qgsu.showUserAndLogMessage(QCoreApplication.translate(
                "ManagerDock",
                "You must delete some video from the list before adding a new one"
            ),
                                       level=QGis.Warning)
            self.loading = False
            return

        self.islocal = islocal
        self.klv_folder = klv_folder
        w = QWidget()
        layout = QVBoxLayout()
        pbar = QProgressBar()
        layout.addWidget(pbar)
        w.setLayout(layout)
        rowPosition = self.VManager.rowCount()
        self.videoPlayable.append(False)

        pbar.setGeometry(0, 0, 300, 30)
        pbar.setValue(0)
        pbar.setMaximumHeight(30)

        if load_id is None:
            row_id = 0
            if rowPosition != 0:
                row_id = int(self.VManager.item(rowPosition - 1, 0).text()) + 1
        else:
            row_id = load_id

        self.VManager.insertRow(rowPosition)

        self.VManager.setItem(rowPosition, 0, QTableWidgetItem(str(row_id)))

        self.VManager.setItem(rowPosition, 1, QTableWidgetItem(name))
        self.VManager.setItem(
            rowPosition, 2,
            QTableWidgetItem(
                QCoreApplication.translate("ManagerDock", "Loading")))
        self.VManager.setItem(rowPosition, 3, QTableWidgetItem(filename))
        self.VManager.setItem(rowPosition, 4, QTableWidgetItem("-"))
        self.VManager.setCellWidget(rowPosition, 5, w)

        self.VManager.setVisible(False)
        self.VManager.horizontalHeader().setStretchLastSection(True)
        self.VManager.setVisible(True)

        # resolve if it is a stream
        if "://" in filename:
            self.videoIsStreaming.append(True)
        else:
            self.videoIsStreaming.append(False)

        if not self.videoIsStreaming[-1]:
            # Disable row if not exist video file
            if not os.path.exists(filename):
                self.ToggleActiveRow(rowPosition, value="Missing source file")
                for j in range(self.VManager.columnCount()):
                    try:
                        self.VManager.item(rowPosition,
                                           j).setFlags(Qt.NoItemFlags
                                                       | Qt.ItemIsEnabled)
                        self.VManager.item(rowPosition, j).setBackground(
                            QColor(211, 211, 211))
                    except Exception:
                        self.VManager.cellWidget(rowPosition, j).setStyleSheet(
                            "background-color:rgb(211,211,211);")
                        pass
                self.loading = False
                return

            pbar.setValue(30)
            info = FFMpeg().probe(filename)
            if info is None:
                qgsu.showUserAndLogMessage(
                    QCoreApplication.translate("ManagerDock",
                                               "Failed loading FFMPEG ! "))

            klvIdx = getKlvStreamIndex(filename, islocal)

            # init non-blocking metadata buffered reader
            self.meta_reader.append(
                BufferedMetaReader(filename,
                                   klv_index=klvIdx,
                                   pass_time=self.pass_time,
                                   interval=self.buf_interval))

            pbar.setValue(60)
            try:
                # init point we can center the video on
                self.initialPt.append(
                    getVideoLocationInfo(filename, islocal, klv_folder))
                if not self.initialPt[rowPosition]:
                    self.VManager.setItem(
                        rowPosition, 4,
                        QTableWidgetItem(
                            QCoreApplication.translate(
                                "ManagerDock",
                                "Start location not available.")))
                    self.ToggleActiveRow(rowPosition,
                                         value="Video not applicable")
                    pbar.setValue(100)

                else:
                    self.VManager.setItem(
                        rowPosition, 4,
                        QTableWidgetItem(self.initialPt[rowPosition][2]))
                    pbar.setValue(90)
                    self.videoPlayable[rowPosition] = True
            except Exception:
                qgsu.showUserAndLogMessage(
                    QCoreApplication.translate(
                        "ManagerDock", "This video doesn't have Metadata ! "))
                pbar.setValue(100)
                self.ToggleActiveRow(rowPosition, value="Video not applicable")

        else:
            self.meta_reader.append(StreamMetaReader(filename))
            qgsu.showUserAndLogMessage("",
                                       "StreamMetaReader initialized.",
                                       onlyLog=True)
            self.initialPt.append(None)
            self.videoPlayable[rowPosition] = True

        url = ""
        if self.videoIsStreaming[-1]:
            # show video from splitter (port +1)
            oldPort = filename.split(":")[2]
            newPort = str(int(oldPort) + 10)
            proto = filename.split(":")[0]
            url = QUrl(proto + "://127.0.0.1:" + newPort)
        else:
            url = QUrl.fromLocalFile(filename)

        self.playlist.addMedia(QMediaContent(url))

        if self.videoPlayable[rowPosition]:
            pbar.setValue(100)
            if islocal:
                self.ToggleActiveRow(rowPosition, value="Ready Local")
            else:
                self.ToggleActiveRow(rowPosition, value="Ready")
            # Add video to settings list
            AddVideoToSettings(str(row_id), filename)

        self.loading = False

    def openVideoFileDialog(self):
        ''' Open video file dialog '''
        if self.loading:
            return

        Exts = ast.literal_eval(parser.get("FILES", "Exts"))

        filename, _ = askForFiles(self,
                                  QCoreApplication.translate(
                                      "ManagerDock", "Open video"),
                                  exts=Exts)

        if filename:
            if not self.isFileInPlaylist(filename):
                _, name = os.path.split(filename)
                self.AddFileRowToManager(name, filename)
            else:
                qgsu.showUserAndLogMessage(
                    QCoreApplication.translate(
                        "ManagerDock",
                        "File is already loaded in playlist: " + filename))

        return

    def isFileInPlaylist(self, filename):
        mcount = self.playlist.mediaCount()
        for x in range(mcount):
            if filename in self.playlist.media(x).canonicalUrl().toString():
                return True
        return False

    def PlayVideoFromManager(self, model):
        ''' Play video from manager dock.
            Manager row double clicked
        '''
        # Don't enable Play if video doesn't have metadata
        if not self.videoPlayable[model.row()]:
            return

        try:
            if self._PlayerDlg.isVisible():
                self._PlayerDlg.close()
        except Exception:
            None

        path = self.VManager.item(model.row(), 3).text()
        text = self.VManager.item(model.row(), 1).text()

        folder = getVideoFolder(text)
        klv_folder = os.path.join(folder, "klv")
        exist = os.path.exists(klv_folder)

        # First time we open the player
        if self._PlayerDlg is None:
            if exist:
                self.CreatePlayer(path,
                                  self.update_interval,
                                  model.row(),
                                  islocal=True,
                                  klv_folder=klv_folder)
            else:
                self.CreatePlayer(path, self.update_interval, model.row())

        self.SetupPlayer(model.row())
        if exist:
            self._PlayerDlg.playFile(path, islocal=True, klv_folder=klv_folder)
        else:
            self._PlayerDlg.playFile(path)

    def SetupPlayer(self, row):
        ''' Play video from manager dock.
            Manager row double clicked
        '''
        self.ToggleActiveRow(row)

        self.playlist.setCurrentIndex(row)

        # qgsu.CustomMessage("QGIS FMV", path, self._PlayerDlg.fileName, icon="Information")
        # if path != self._PlayerDlg.fileName:
        self._PlayerDlg.setMetaReader(self.meta_reader[row])
        self.ToggleActiveFromTitle()
        self._PlayerDlg.show()
        self._PlayerDlg.activateWindow()

        # zoom to map zone
        curAuthId = self.iface.mapCanvas().mapSettings().destinationCrs(
        ).authid()

        if self.initialPt[row][1] is not None and self.initialPt[row][
                0] is not None:
            map_pos = QgsPointXY(self.initialPt[row][1],
                                 self.initialPt[row][0])
            if curAuthId != "EPSG:4326":
                trgCode = int(curAuthId.split(":")[1])
                xform = QgsCoordinateTransform(
                    QgsCoordinateReferenceSystem(4326),
                    QgsCoordinateReferenceSystem(trgCode),
                    QgsProject().instance())
                map_pos = xform.transform(map_pos)

            self.iface.mapCanvas().setCenter(map_pos)

        self.iface.mapCanvas().zoomScale(50000)

    def CreatePlayer(self,
                     path,
                     interval,
                     row,
                     islocal=False,
                     klv_folder=None):
        ''' Create Player '''
        self._PlayerDlg = QgsFmvPlayer(self.iface,
                                       path,
                                       interval,
                                       parent=self,
                                       meta_reader=self.meta_reader[row],
                                       pass_time=self.pass_time,
                                       islocal=islocal,
                                       klv_folder=klv_folder)

        self._PlayerDlg.player.setPlaylist(self.playlist)
        self._PlayerDlg.setWindowFlags(Qt.Dialog | Qt.WindowCloseButtonHint)
        self._PlayerDlg.show()
        self._PlayerDlg.activateWindow()

    def ToggleActiveFromTitle(self):
        ''' Toggle Active video status '''
        column = 2
        for row in range(self.VManager.rowCount()):
            if self.VManager.item(row, column) is not None:
                v = self.VManager.item(row, column).text()
                text = self.VManager.item(row, 1).text()
                if v == "Playing":
                    folder = getVideoFolder(text)
                    klv_folder = os.path.join(folder, "klv")
                    exist = os.path.exists(klv_folder)
                    if exist:
                        self.ToggleActiveRow(row, value="Ready Local")
                    else:
                        self.ToggleActiveRow(row, value="Ready")
                    return

    def ToggleActiveRow(self, row, value="Playing"):
        ''' Toggle Active row manager video status '''
        self.VManager.setItem(
            row, 2,
            QTableWidgetItem(QCoreApplication.translate("ManagerDock", value)))
        return

    def closeEvent(self, _):
        ''' Close Manager Event '''
        FmvDock = qgis.utils.plugins[getNameSpace()]
        FmvDock._FMVManager = None
        try:
            if self._PlayerDlg.isVisible():
                self._PlayerDlg.close()
        except Exception:
            None
        return

    def dragEnterEvent(self, e):
        check = True
        Exts = ast.literal_eval(parser.get("FILES", "Exts"))
        if e.mimeData().hasUrls():
            for url in e.mimeData().urls():
                fileIsOk = False
                for ext in Exts:
                    if url.fileName().lower().endswith(ext):
                        fileIsOk = True
                        break
                if not fileIsOk:
                    check = False
                    break

        # Only accept if all files match a required extension
        if check:
            e.acceptProposedAction()
        # Ignore and stop propagation
        else:
            e.setDropAction(Qt.IgnoreAction)
            e.accept()

    def dropEvent(self, e):
        for url in e.mimeData().urls():
            # local files
            if "file:///" in url.toString():
                if not self.isFileInPlaylist(url.toString()[8:]):
                    self.AddFileRowToManager(url.fileName(),
                                             url.toString()[8:])
            # network drives
            else:
                if not self.isFileInPlaylist(url.toString()[5:]):
                    self.AddFileRowToManager(url.fileName(),
                                             url.toString()[5:])
Esempio n. 5
0
class MusicPlayer(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.options = self.parent().options

        self.ffmpeg = self.options['paths']['ffmpeg_bin']
        self.thumbnails = self.options['paths']['thumbnails']
        self.thumb_width = self.options['thumbnail']['width']
        self.thumb_height = self.options['thumbnail']['height']

        self.jumping = False
        self.blocked = False
        self.durations = {}
        self.playback_value = 0

        self.text = ["None", "Repeat", "Random"]
        self.playlist_list = []
        self.values = [
            QMediaPlaylist.Loop, QMediaPlaylist.CurrentItemInLoop,
            QMediaPlaylist.Random
        ]

        # Thumbnail widget
        self.image_label = QLabel()

        # Control widgets
        self.playButton = QToolButton(clicked=self.play)
        self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))

        self.stopButton = QToolButton(clicked=self.stop)
        self.stopButton.setIcon(self.style().standardIcon(QStyle.SP_MediaStop))
        self.stopButton.setEnabled(False)

        self.playbackButton = QToolButton(clicked=self.playback_mode)
        self.playbackButton.setText(self.text[0])

        self.nextButton = QToolButton(clicked=self.next_song)
        self.nextButton.setIcon(self.style().standardIcon(
            QStyle.SP_MediaSkipForward))

        self.previousButton = QToolButton(clicked=self.previous_song)
        self.previousButton.setIcon(self.style().standardIcon(
            QStyle.SP_MediaSkipBackward))

        self.muteButton = QToolButton(clicked=self.mute_clicked)
        self.muteButton.setIcon(self.style().standardIcon(
            QStyle.SP_MediaVolume))

        self.volumeSlider = QSlider(Qt.Horizontal,
                                    sliderMoved=self.change_volume)
        self.volumeSlider.setRange(0, 100)
        self.volumeSlider.setPageStep(1)
        self.volumeSlider.setValue(50)

        # Player and playlist setup

        self.player = QMediaPlayer()
        self.player.setVolume(50)
        self.player.stateChanged.connect(self.waveform)

        self.playlist = QMediaPlaylist()
        self.playlist.setPlaybackMode(self.values[0])
        self.playlist.setCurrentIndex(1)

        self.player.setPlaylist(self.playlist)

        self.playlistModel = PlaylistModel()
        self.playlistModel.setPlaylist(self.playlist)

        self.playlistView = QListView()
        self.playlistView.setModel(self.playlistModel)
        self.playlistView.setCurrentIndex(
            self.playlistModel.index(self.playlist.currentIndex(), 0))
        self.playlistView.activated.connect(self.jump)
        self.playlistView.setContextMenuPolicy(Qt.CustomContextMenu)
        self.playlistView.customContextMenuRequested.connect(
            self.list_view_menu)
        self.playlist.currentIndexChanged.connect(
            lambda position: self.change_thumbnail(position))

        song_search = QLineEdit()
        song_search.textChanged.connect(self.search)
        song_search.setClearButtonEnabled(True)

        # Playlist
        self.playlist_name = QComboBox()
        self.playlist_name.currentTextChanged.connect(self.switch_playlist)
        self.refresh_lists()

        self.up_button = QToolButton(clicked=self.move_up)
        self.up_button.setIcon(self.style().standardIcon(QStyle.SP_ArrowUp))
        self.down_button = QToolButton(clicked=self.move_down)
        self.down_button.setIcon(self.style().standardIcon(
            QStyle.SP_ArrowDown))

        # Sound wave widget

        self.wave_graphic = WaveGraphic(self)
        #self.wave_graphic.hide()

        # Testing slider again
        self.slider = QSlider(Qt.Horizontal)
        self.slider.setRange(0, self.player.duration() / 1000)
        self.slider.sliderMoved.connect(self.seek)
        self.player.durationChanged.connect(self.durationChanged)
        self.player.positionChanged.connect(self.positionChanged)

        # Shortcuts
        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_F), self, song_search.setFocus)

        # Layouts setup

        playlist_layout = QHBoxLayout()
        playlist_layout.addWidget(self.playlist_name)
        playlist_layout.addWidget(self.up_button)
        playlist_layout.addWidget(self.down_button)

        control_layout = QHBoxLayout()
        control_layout.setContentsMargins(0, 0, 0, 0)
        control_layout.addWidget(self.stopButton)
        control_layout.addWidget(self.previousButton)
        control_layout.addWidget(self.playButton)
        control_layout.addWidget(self.nextButton)
        control_layout.addWidget(self.muteButton)
        control_layout.addWidget(self.volumeSlider)
        control_layout.addWidget(self.playbackButton)

        display_layout = QVBoxLayout()
        display_layout.addWidget(song_search)
        display_layout.addWidget(self.playlistView)
        display_layout.addLayout(playlist_layout)

        music_layout = QVBoxLayout()
        music_layout.addWidget(self.image_label)
        music_layout.addWidget(self.slider)
        music_layout.addLayout(control_layout)

        main_layout = QHBoxLayout()
        main_layout.addLayout(music_layout)
        main_layout.addLayout(display_layout)

        main_2_layout = QVBoxLayout()
        main_2_layout.addLayout(main_layout)
        main_2_layout.addWidget(self.wave_graphic)

        self.setLayout(main_2_layout)

    def waveform(self, status):
        if status == QMediaPlayer.PlayingState:
            self.wave_graphic.start()
        elif status == QMediaPlayer.PausedState:
            self.wave_graphic.pause()
        else:
            self.wave_graphic.stop()

    def list_view_menu(self, point):
        menu = QMenu("Menu", self)
        recommend_action = QAction('&Recommend Songs', self)
        menu.addAction(recommend_action)

        # TODO: [FEATURE] add rename song
        recommend_action.triggered.connect(
            lambda: self.parent().call_download_manager(self.get_links()))

        rename_action = QAction('&Rename', self)
        menu.addAction(rename_action)

        # TODO: [FEATURE] add rename song
        rename_action.triggered.connect(lambda: print("rename"))

        # Show the context menu.
        menu.exec_(self.playlistView.mapToGlobal(point))

    def get_links(self):
        title = self.playlistView.selectedIndexes()[0].data()
        link = getYoutubeURLFromSearch(title)
        if len(link) > 1:
            return youtube_recommendations(link)

    def move_up(self):
        index = self.playlistView.currentIndex().row()
        if index - 1 >= 0:
            item, above = self.playlist.media(index), self.playlist.media(
                index - 1)
            self.playlist.removeMedia(index)
            self.playlist.removeMedia(index - 1)
            self.playlist.insertMedia(index - 1, item)
            self.playlist.insertMedia(index, above)
            self.blocked = True
            self.playlistView.setCurrentIndex(
                self.playlistModel.index(index - 1, 0))
            self.blocked = False
            self.stop()

    def move_down(self):
        index = self.playlistView.currentIndex().row()
        if index + 1 <= self.playlistModel.rowCount():
            item, below = self.playlist.media(index), self.playlist.media(
                index + 1)
            self.playlist.removeMedia(index + 1)
            self.playlist.removeMedia(index)
            self.playlist.insertMedia(index, below)
            self.playlist.insertMedia(index + 1, item)
            self.blocked = True
            self.playlistView.setCurrentIndex(
                self.playlistModel.index(index + 1, 0))
            self.blocked = False
            self.stop()

    def search(self, part_of_song):
        for index in range(self.playlistModel.rowCount()):
            item = self.playlistModel.data(self.playlistModel.index(
                index, 0)).lower()
            self.playlistView.setRowHidden(index,
                                           part_of_song.lower() not in item)

    def change_thumbnail(self, position=0):

        self.playlistView.setCurrentIndex(self.playlistModel.index(
            position, 0))

        song = self.playlistView.selectedIndexes()[0].data()
        if self.wave_graphic.is_song_cached(song):
            self.wave_graphic.load_waves(song)
        else:
            wc_ = WaveConverter(song, self.wave_graphic.set_wav)
            wc_.convert()

        if self.playlistView.currentIndex().data() is None or self.blocked:
            return

        max_ratio = 0
        img = None

        for item in listdir(self.thumbnails):
            if item.endswith('.jpg'):
                ratio = similar(
                    item,
                    self.playlistView.currentIndex().data().replace(
                        '.mp3', '.jpg'))
                if ratio > max_ratio:
                    max_ratio = ratio
                    img = item

        if img:
            p = QPixmap(self.thumbnails + img)
            self.image_label.setPixmap(
                p.scaled(self.thumb_width, self.thumb_height,
                         Qt.KeepAspectRatio))

    def switch_playlist(self, current_text):
        self.playlist.clear()
        if current_text == "No Playlist":
            self.refresh()
        else:
            if read_playlist(current_text):
                songs = read_playlist(current_text).split('\n')
                for song in songs:
                    self.playlist.addMedia(
                        QMediaContent(QUrl.fromLocalFile(song)))

    def refresh(self):
        # Change it so it will go to same song.
        if self.playlist_name.currentText() != "No Playlist":
            return

        paths = fetch_options()['paths']['music_path'].split(';')

        current_songs = [
            self.playlistModel.data(self.playlistModel.index(row, 0))
            for row in range(self.playlistModel.rowCount())
        ]

        for path in paths:
            if not path:
                continue
            for item in listdir(path):
                if isfile(join(path, item)) and item.endswith(".mp3") and (
                        item not in current_songs):
                    self.playlist.addMedia(
                        QMediaContent(QUrl.fromLocalFile(join(path, item))))

    def refresh_lists(self):
        path = fetch_options()['paths']['playlist']
        self.playlist_name.clear()
        self.playlist_list = ["No Playlist"]
        self.playlist_name.addItem("No Playlist")
        for item in listdir(path):
            if isfile(join(path, item)) and item.endswith(".lst"):
                self.playlist_list.append(item.split('.')[0])
                self.playlist_name.addItem(item.split('.')[0])

    def playback_mode(self):
        # Normal -> Loop -> Random
        def up_value(value, max_value=3):
            if value + 1 == max_value:
                return 0
            return value + 1

        self.playback_value = up_value(self.playback_value)
        self.playlist.setPlaybackMode(self.values[self.playback_value])
        self.playbackButton.setText(self.text[self.playback_value])

    def jump(self, index):
        if index.isValid() and not self.blocked:
            self.playlist.setCurrentIndex(index.row())
            self.jumping = True
            self.play()

    def play(self):
        if self.blocked:
            return
        if self.player.state() != QMediaPlayer.PlayingState or self.jumping:
            self.player.play()
            self.playButton.setIcon(self.style().standardIcon(
                QStyle.SP_MediaPause))
            self.jumping = False
        else:
            self.player.pause()
            self.playButton.setIcon(self.style().standardIcon(
                QStyle.SP_MediaPlay))
        self.stopButton.setEnabled(True)

    def change_volume(self, value):
        self.player.setVolume(value)

    def stop(self):
        if self.player.state() != QMediaPlayer.StoppedState:
            self.player.stop()
        self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
        self.stopButton.setEnabled(False)

    def next_song(self):
        self.playlist.next()
        self.playlistView.setCurrentIndex(
            self.playlistModel.index(self.playlist.currentIndex(), 0))

    def previous_song(self):
        self.playlist.previous()
        self.playlistView.setCurrentIndex(
            self.playlistModel.index(self.playlist.currentIndex(), 0))

    def mute_clicked(self):
        self.player.setMuted(not self.player.isMuted())
        self.muteButton.setIcon(self.style().standardIcon(
            QStyle.SP_MediaVolume if not self.player.isMuted() else QStyle.
            SP_MediaVolumeMuted))

    def durationChanged(self, duration):
        duration /= 1000
        self.slider.setMaximum(duration)

    def positionChanged(self, progress):
        progress /= 1000

        if not self.slider.isSliderDown():
            self.slider.setValue(progress)

    def seek(self, seconds):
        self.player.setPosition(seconds * 1000)
Esempio n. 6
0
class QmyMainWindow(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)  #调用父类构造函数,创建窗体
        self.ui = Ui_MainWindow()  #创建UI对象
        self.ui.setupUi(self)  #构造UI界面

        ##      self.setWindowTitle("Demo10_1,音乐播放器")
        self.player = QMediaPlayer(self)
        self.playlist = QMediaPlaylist(self)
        self.player.setPlaylist(self.playlist)
        self.playlist.setPlaybackMode(QMediaPlaylist.Loop)  #循环模式

        self.__duration = ""  #文件总时间长度
        self.__curPos = ""  #当前播放到位置

        self.player.stateChanged.connect(self.do_stateChanged)

        self.player.positionChanged.connect(self.do_positionChanged)

        self.player.durationChanged.connect(self.do_durationChanged)

        self.playlist.currentIndexChanged.connect(self.do_currentChanged)

##  ==============自定义功能函数============

##  ===============event 处理函数==========

    def closeEvent(self, event):  ##窗体关闭时
        ##  窗口关闭时不能自动停止播放,需手动停止
        if (self.player.state() == QMediaPlayer.PlayingState):
            self.player.stop()

##  ==========由connectSlotsByName() 自动连接的槽函数==================
#  播放列表管理

    @pyqtSlot()  ##添加文件
    def on_btnAdd_clicked(self):
        ##      curPath=os.getcwd()     #获取系统当前目录
        ##      curPath=QDir.homePath()
        curPath = QDir.currentPath()
        dlgTitle = "选择音频文件"
        filt = "音频文件(*.mp3 *.wav *.wma);;所有文件(*.*)"
        fileList, flt = QFileDialog.getOpenFileNames(self, dlgTitle, curPath,
                                                     filt)
        count = len(fileList)
        if count < 1:
            return

        filename = fileList[0]
        fileInfo = QFileInfo(filename)  #文件信息
        QDir.setCurrent(fileInfo.absolutePath())  #重设当前路径

        for i in range(count):
            filename = fileList[i]
            fileInfo.setFile(filename)
            song = QMediaContent(QUrl.fromLocalFile(filename))
            self.playlist.addMedia(song)  #添加播放媒体
            ##         basename=os.path.basename(filename)    #文件名和后缀
            basename = fileInfo.baseName()
            self.ui.listWidget.addItem(basename)  #添加到界面文件列表

        if (self.player.state() != QMediaPlayer.PlayingState):
            self.playlist.setCurrentIndex(0)
            self.player.play()

    @pyqtSlot()  ##移除一个文件
    def on_btnRemove_clicked(self):
        pos = self.ui.listWidget.currentRow()
        item = self.ui.listWidget.takeItem(pos)  #python会自动删除

        if (self.playlist.currentIndex() == pos):  #是当前播放的曲目
            nextPos = 0
            if pos >= 1:
                nextPos = pos - 1

            self.playlist.removeMedia(pos)  #从播放列表里移除
            if self.ui.listWidget.count() > 0:  #剩余个数
                self.playlist.setCurrentIndex(nextPos)
                self.do_currentChanged(nextPos)  #当前曲目变化
            else:
                self.player.stop()
                self.ui.LabCurMedia.setText("无曲目")
        else:
            self.playlist.removeMedia(pos)

    @pyqtSlot()  ##清空播放列表
    def on_btnClear_clicked(self):
        self.playlist.clear()  #清空播放列表
        self.ui.listWidget.clear()
        self.player.stop()  #停止播放

    ##   @pyqtSlot()    ##双击时切换播放文件
    def on_listWidget_doubleClicked(self, index):
        rowNo = index.row()  #行号
        self.playlist.setCurrentIndex(rowNo)
        self.player.play()

#  播放控制

    @pyqtSlot()  ##播放
    def on_btnPlay_clicked(self):
        if (self.playlist.currentIndex() < 0):
            self.playlist.setCurrentIndex(0)
        self.player.play()

    @pyqtSlot()  ##暂停
    def on_btnPause_clicked(self):
        self.player.pause()

    @pyqtSlot()  ##停止
    def on_btnStop_clicked(self):
        self.player.stop()

    @pyqtSlot()  ##上一曲目
    def on_btnPrevious_clicked(self):
        self.playlist.previous()

    @pyqtSlot()  ##下一曲目
    def on_btnNext_clicked(self):
        self.playlist.next()

    @pyqtSlot()  ##静音控制
    def on_btnSound_clicked(self):
        mute = self.player.isMuted()
        self.player.setMuted(not mute)
        if mute:
            self.ui.btnSound.setIcon(QIcon(":/icons/images/volumn.bmp"))
        else:
            self.ui.btnSound.setIcon(QIcon(":/icons/images/mute.bmp"))

    @pyqtSlot(int)  ##调节音量
    def on_sliderVolumn_valueChanged(self, value):
        self.player.setVolume(value)

    @pyqtSlot(int)  ##文件进度调控
    def on_sliderPosition_valueChanged(self, value):
        self.player.setPosition(value)

##  =============自定义槽函数===============================

    def do_stateChanged(self, state):  ##播放器状态变化
        self.ui.btnPlay.setEnabled(state != QMediaPlayer.PlayingState)
        self.ui.btnPause.setEnabled(state == QMediaPlayer.PlayingState)
        self.ui.btnStop.setEnabled(state == QMediaPlayer.PlayingState)

    def do_positionChanged(self, position):  ##当前文件播放位置变化,更新进度显示
        if (self.ui.sliderPosition.isSliderDown()):  #在拖动滑块调整进度
            return
        self.ui.sliderPosition.setSliderPosition(position)
        secs = position / 1000  #秒
        mins = secs / 60  #分钟
        secs = secs % 60  #余数秒
        self.__curPos = "%d:%d" % (mins, secs)
        self.ui.LabRatio.setText(self.__curPos + "/" + self.__duration)

    def do_durationChanged(self, duration):  ##文件时长变化
        self.ui.sliderPosition.setMaximum(duration)

        secs = duration / 1000  #秒
        mins = secs / 60  #分钟
        secs = secs % 60  #余数秒
        self.__duration = "%d:%d" % (mins, secs)
        self.ui.LabRatio.setText(self.__curPos + "/" + self.__duration)

    def do_currentChanged(self, position):  ##playlist当前曲目变化
        self.ui.listWidget.setCurrentRow(position)
        item = self.ui.listWidget.currentItem()  #QListWidgetItem
        if (item != None):
            self.ui.LabCurMedia.setText(item.text())
Esempio n. 7
0
class DPlayerCore(QWidget):
    def __init__(self):
        """Initialize player and load playlist if any."""
        super().__init__()

        self.player = QMediaPlayer()
        self.playlist = QMediaPlaylist()
        self.player.setPlaylist(self.playlist)

        self.shuffling = False
        self.repeatingPlaylist = False
        self.repeatingSong = False

        self.musicOrder = []
        self.loadPlaylist(QUrl(
            'file://{}/lastListened.m3u'.format(os.getcwd())))

        self.lyricsApi = 'http://api.musixmatch.com/ws/1.1/'
        self.lyricsApiKey = '4b364f0652e471aa50813a22cdf830ea'
        self.lastFMapi = 'http://ws.audioscrobbler.com/2.0/'
        self.lastFMapikey = '052c43a00a4fc294bb3c9e0c38bdf710'
        self.lastFMsecret = '14c66392fa9c6c142a41ccc2b0674e19'
        self.username = None
        self.password = None
        self.network = None
        self.error = 'Something went wrong! Try again later.'

    def play(self):
        """Start the player."""
        self.player.play()

    def pause(self):
        """Pause the player."""
        self.player.pause()

    def stop(self):
        """Stop the player."""
        self.player.stop()

    def previous(self):
        """Play previous song."""
        self.playlist.previous()

    def next(self):
        """Play next song."""
        self.playlist.next()

    def mute(self):
        """Mute the player."""
        self.player.setMuted(True)

    def unmute(self):
        """Unmute the player."""
        self.player.setMuted(False)

    def setVolume(self, value):
        """Set player's volume to value."""
        self.player.setVolume(value)

    def add(self, fileNames):
        """Add fileNames to the playlist."""
        for name in fileNames:
            url = QUrl.fromLocalFile(QFileInfo(name).absoluteFilePath())
            self.playlist.addMedia(QMediaContent(url))
            self.musicOrder.append([name])

        self.added(len(fileNames))

    def added(self, added):
        """Saves music info in musicOrder."""
        for name, index in zip(
                self.musicOrder[self.playlist.mediaCount() - added:],
                range(self.playlist.mediaCount() - added,
                      len(self.musicOrder))):
            name = name[0]
            artist = self.getArtist(name)[0]
            title = self.getTitle(name)[0]
            album = self.getAlbum(name)[0]
            seconds = self.getDuration(name)
            duration = QTime(0, seconds // 60, seconds % 60)
            duration = duration.toString('mm:ss')
            self.musicOrder[index].extend(
                [artist, title, album, duration])

    def remove(self, songIndexes):
        """Remove songIndexes from the playlist."""
        for index in songIndexes:
            self.songChanged = True
            del self.musicOrder[index]
            self.playlist.removeMedia(index)
        self.songChanged = False

    def savePlaylist(self, path):
        """Save playlist to path."""
        if path.toString()[len(path.toString()) - 4:] != '.m3u':
            path = QUrl('{}.m3u'.format(path.toString()))
        self.playlist.save(path, 'm3u')

    def loadPlaylist(self, path):
        """Load playlist form path."""
        count = self.playlist.mediaCount()
        self.playlist.load(path)

        for index in range(count, self.playlist.mediaCount()):
            self.musicOrder.append(
                [self.playlist.media(index).canonicalUrl().path()])

        self.added(self.playlist.mediaCount() - count)

    def clearPlaylist(self):
        """Delete all songs in the playlist."""
        self.playlist.clear()
        self.musicOrder = []

    def shuffle(self, value):
        """Shuffle playlist if value = True."""
        self.shuffling = value

        if self.repeatingSong:
            return
        if self.shuffling:
            self.playlist.setPlaybackMode(QMediaPlaylist.Random)
        elif self.repeatingPlaylist:
            self.playlist.setPlaybackMode(QMediaPlaylist.Loop)
        else:
            self.playlist.setPlaybackMode(QMediaPlaylist.Sequential)

    def repeatPlaylist(self, value):
        """Repeat playlist after the last song is finished if value = True."""
        self.repeatingPlaylist = value

        if self.repeatingSong or self.shuffling:
            return
        if self.repeatingPlaylist:
            self.playlist.setPlaybackMode(QMediaPlaylist.Loop)
        else:
            self.playlist.setPlaybackMode(QMediaPlaylist.Sequential)

    def repeatSong(self, value):
        """Repeat current song if value = True."""
        self.repeatingSong = value

        if self.repeatingSong:
            self.playlist.setPlaybackMode(QMediaPlaylist.CurrentItemInLoop)
        elif self.shuffling:
            self.playlist.setPlaybackMode(QMediaPlaylist.Random)
        elif self.repeatingPlaylist:
            self.playlist.setPlaybackMode(QMediaPlaylist.Loop)
        else:
            self.playlist.setPlaybackMode(QMediaPlaylist.Sequential)

    def sort(self, column, order):
        """Sort playlist by column in order."""
        ordered = sorted(self.musicOrder, key=itemgetter(column + 1),
                         reverse=order)

        self.clearPlaylist()
        for song in ordered:
            url = QUrl.fromLocalFile(QFileInfo(song[0]).absoluteFilePath())
            self.playlist.addMedia(QMediaContent(url))

        self.musicOrder = ordered

    def findLyrics(self, index):
        """Returns lyrics for song at index."""
        if self.musicOrder[index][2] == 'Unknown':
            return 'Unknown song.'

        searchSong = '{}track.search?q_track={}'.format(
            self.lyricsApi, self.musicOrder[index][2].replace(' ', '%20'))
        if self.musicOrder[index][1] != 'Unknown':
            searchSong = '{}&q_artist={}'.format(
                searchSong, self.musicOrder[index][1].replace(' ', '%20'))
        searchSong = '{}&f_has_lyrics=1&apikey={}'.format(
            searchSong, self.lyricsApiKey)

        try:
            requestSong = requests.get(searchSong)
        except requests.ConnectionError:
            return self.error

        songJson = requestSong.json()
        if requestSong.status_code != 200 or \
                songJson['message']['header']['available'] == 0:
            return self.error

        songId = songJson[
            'message']['body']['track_list'][0]["track"]["track_id"]
        searchLyrics = '{}track.lyrics.get?track_id={}&apikey={}'.format(
            self.lyricsApi, songId, self.lyricsApiKey)

        try:
            requestLyrics = requests.get(searchLyrics)
        except requests.ConnectionError:
            return self.error

        if requestLyrics.status_code != 200 or \
                songJson['message']['header']['available'] == 0:
            return self.error
        return requestLyrics.json()[
            'message']['body']['lyrics']['lyrics_body'][:-58]  # spam and bacon

    def findInfo(self, index):
        """Returns info about artist and album for index if any."""
        info = []

        if self.musicOrder[index][1] != 'Unknown':
            artist = self.artistInfo(self.musicOrder[index][1])
            if artist != self.error:
                info += artist

        if self.musicOrder[index][1] != 'Unknown' and \
                self.musicOrder[index][3] != 'Unknown':
            album = self.albumInfo(self.musicOrder[index][1],
                                   self.musicOrder[index][3])
            if album != self.error:
                info += album

        if info:
            return info
        else:
            return ['Unknown artist and song!']

    def artistInfo(self, artist):
        """Returns info about artist if any."""
        try:
            response = requests.get(
                ('{}/?method=artist.getinfo&artist={}&api_key={}&'
                    'format=json&autocorrect=1').format(
                    self.lastFMapi, artist, self.lastFMapikey))
        except Exception:
            return self.error

        if response.status_code != 200:
            return self.error

        artist = 'Artist: {}'.format(response.json()['artist']['name'])
        bio = 'Bio: {}'.format(
            response.json()['artist']['bio']['summary'].replace('.', '.\n'))
        spam = bio.find('<a')
        bio = bio[:spam]

        return [artist, bio]

    def albumInfo(self, artist, album):
        """Returns info about album if any."""
        try:
            response = requests.get(
                ('{}/?method=album.getinfo&artist={}&album={}&api_key={}&'
                    'format=json&autocorrect=1').format(
                    self.lastFMapi, artist, album, self.lastFMapikey))
        except Exception:
            return self.error

        if response.status_code != 200 or \
                'album' not in response.json().keys():
            return self.error

        album = 'Album: {}'.format(response.json()['album']['name'])
        tracks = ['Tracks: ']
        t = response.json()['album']['tracks']['track']
        for track, index in zip(t, range(len(t))):
            tracks.append('{}. {}'.format(index + 1, track['name']))

        info = [album, '\n'.join(tracks)]

        if 'wiki' in response.json()['album'].keys():
            wiki = response.json()['album']['wiki']
            if 'published' in wiki.keys():
                info.append('Published: {}'.format(wiki['published']))
            if 'summary' in wiki.keys():
                summary = wiki['summary'].replace('.', '.\n')
                spam = summary.find('<a')
                info.append('Summary: {}'.format(summary[:spam]))
            if 'Musical style' in wiki.keys():
                info.append('Musical style: {}'.format(wiki['Musical style']))

        return info

    def login(self, username, password):
        """Creates lastFM network."""
        self.username = username
        self.password = pylast.md5(password)
        try:
            self.network = pylast.LastFMNetwork(api_key=self.lastFMapikey,
                                                api_secret=self.lastFMsecret,
                                                username=self.username,
                                                password_hash=self.password)
        except Exception:
            self.username = None
            self.password = None
            self.network = None
            return False
        return True

    def logout(self):
        """Destoys lastFM network and current user info."""
        self.username = None
        self.password = None
        self.network = None

    def loveTrack(self, index):
        """Love track at index in lastFM."""
        if self.network is None:
            return False

        track = self.network.get_track(self.musicOrder[index][1],
                                       self.musicOrder[index][2])
        try:
            track.love()
        except Exception:
            return False
        return True

    def unloveTrack(self, index):
        """Unlove track at index in lastFM."""
        if self.network is None:
            return False

        track = self.network.get_track(self.musicOrder[index][1],
                                       self.musicOrder[index][2])
        try:
            track.unlove()
        except Exception:
            return False
        return True

    def isMuted(self):
        """Returns True if player is muted."""
        return self.player.isMuted()

    def getArtist(self, song):
        """Returns the artist of song."""
        if song[-4:] == '.mp3':
            obj = EasyID3(song)
            if 'artist' in obj.keys():
                return obj['artist']
        elif 'TAG' in mediainfo(song).keys():
            obj = mediainfo(song)['TAG']
            if 'artist' in obj.keys():
                return [obj['artist']]
            elif 'ARTIST' in obj.keys():
                return [obj['ARTIST']]
            else:
                return ['Unknown']
        else:
            return ['Unknown']

    def getTitle(self, song):
        """Returns the title of song."""
        if song[-4:] == '.mp3':
            obj = EasyID3(song)
            if 'title' in obj.keys():
                return obj['title']
        elif 'TAG' in mediainfo(song).keys():
            obj = mediainfo(song)['TAG']
            if 'title' in obj.keys():
                return [obj['title']]
            elif 'TITLE' in obj.keys():
                return [obj['TITLE']]
            else:
                return ['Unknown']
        else:
            return ['Unknown']

    def getAlbum(self, song):
        """Returns the album of song."""
        if song[-4:] == '.mp3':
            obj = EasyID3(song)
            if 'album' in obj.keys():
                return obj['album']
        elif 'TAG' in mediainfo(song).keys():
            obj = mediainfo(song)['TAG']
            if 'album' in obj.keys():
                return [obj['album']]
            elif 'ALBUM' in obj.keys():
                return [obj['ALBUM']]
            else:
                return ['Unknown']
        else:
            return ['Unknown']

    def getDuration(self, song):
        """Returns the duration of song."""
        if song[-4:] == '.mp3':
            return MP3(song).info.length
        return int(float(mediainfo(song)['duration']))
Esempio n. 8
0
class YtdlMusic(QMainWindow, Ui_MainWindow):
    def __init__(self, *args, **kwargs):
        QMainWindow.__init__(self, *args, **kwargs)
        self.setupUi(self)
        self.setWindowIcon(QIcon(LOCAL_DIR + '/ytdl_music.svg'))

        self.player = QMediaPlayer()
        self.player.isSeekable()
        self.playList = QMediaPlaylist()
        self.playListData = []
        self.player.setPlaylist(self.playList)
        self.currentPos = 0
        self.currentTrackDuration = '0:00:00'

        self.player.positionChanged.connect(self.positionChanged)
        self.player.durationChanged.connect(self.durationChanged)
        self.playList.currentIndexChanged.connect(self.playlistPosChanged)
        self.playlistTable.itemDoubleClicked.connect(self.changeTrack)
        self.playlistTable.itemSelectionChanged.connect(self.selectedTracks)
        # self.timeSlider.valueChanged.connect(self.setPosition)
        self.addBtn.clicked.connect(self.addDialog)
        self.removeBtn.clicked.connect(self.delTracks)
        self.playBtn.clicked.connect(self.playPause)
        self.stopBtn.clicked.connect(self.stop)
        self.prevBtn.clicked.connect(self.playList.previous)
        self.nextBtn.clicked.connect(self.playList.next)

        self.playlistTable.setHorizontalHeaderLabels([
            '',
            _translate('MainWindow', 'Channel'),
            _translate('MainWindow', 'Title')
        ])
        header = self.playlistTable.horizontalHeader()
        header.setSectionResizeMode(1, QHeaderView.Stretch)
        header.setSectionResizeMode(2, QHeaderView.Stretch)

    def setPosition(self, pos):
        self.player.setPosition(pos)

    def durationChanged(self, duration):
        total_time = '0:00:00'
        duration = self.player.duration()
        total_time = ms_to_time(duration)
        # self.timeSlider.setMaximum(duration)
        self.currentTrackDuration = duration
        # self.totalTimeLabel.setText(total_time)
        self.currentTrackDuration = total_time

    def mediaStatusChanged(self, status):
        icon = QIcon.fromTheme("media-playback-pause")
        if self.player.state() == QMediaPlayer.StoppedState:
            icon = QIcon.fromTheme("media-playback-start")
        elif self.player.state() == QMediaPlayer.PausedState:
            icon = QIcon.fromTheme("media-playback-start")

        self.playBtn.setIcon(icon)

    def positionChanged(self, position, senderType=False):
        self.currentTime = position
        current_time = '0:00:00'

        if position != -1:
            current_time = ms_to_time(position)
            self.timeLabel.setText('{0} / {1}'.format(
                current_time, self.currentTrackDuration))
        '''self.timeSlider.blockSignals(True)
        self.timeSlider.setValue(position)
        self.timeSlider.blockSignals(False)'''

    def playlistPosChanged(self):
        pos = self.playList.currentIndex()
        data = self.playListData[pos]
        self.setWindowTitle('YouTube-dl Music: ' + data['title'])
        duration = ms_to_time(data['duration'] * 1000)
        self.timeLabel.setText('0:00:00 / {0}'.format(duration))
        # self.totalTimeLabel.setText(ms_to_time(data['duration'] * 1000))
        if self.playList.mediaCount() > 1:
            if pos < self.playList.mediaCount() - 1:
                self.nextBtn.setEnabled(True)
            else:
                self.nextBtn.setEnabled(False)

            if pos > 0:
                self.prevBtn.setEnabled(True)
            else:
                self.prevBtn.setEnabled(False)

            prevPos = 0
            if pos < self.playList.mediaCount():
                if self.currentPos < pos:
                    prevPos = pos - 1
                else:
                    prevPos = pos + 1

                statusItem = QLabel()
                icon = QIcon.fromTheme("media-playback-start")
                statusItem.setPixmap(icon.pixmap(16, 16))
                statusItem.setAlignment(Qt.AlignCenter)
                self.playlistTable.setCellWidget(pos, 0, statusItem)
                if prevPos > -1:
                    self.playlistTable.setCellWidget(prevPos, 0, QLabel())
            else:
                self.playlistTable.setItem(pos, 0, QTableWidgetItem(''))

            self.currentPos = pos

        else:
            statusItem = QLabel()
            icon = QIcon.fromTheme("media-playback-start")
            statusItem.setPixmap(icon.pixmap(16, 16))
            statusItem.setAlignment(Qt.AlignCenter)
            self.playlistTable.setCellWidget(pos, 0, statusItem)

    def playPause(self):
        icon = QIcon.fromTheme("media-playback-pause")

        if self.player.state() == QMediaPlayer.StoppedState:
            if self.player.mediaStatus() == QMediaPlayer.NoMedia:
                if self.playList.mediaCount() != 0:
                    self.player.play()
            elif self.player.mediaStatus() == QMediaPlayer.LoadedMedia:
                self.playList.setCurrentIndex(self.currentPos)
                self.player.play()
            elif self.player.mediaStatus() == QMediaPlayer.BufferedMedia:
                self.player.play()
        elif self.player.state() == QMediaPlayer.PlayingState:
            icon = QIcon.fromTheme("media-playback-start")
            self.player.pause()
        elif self.player.state() == QMediaPlayer.PausedState:
            self.player.play()

        self.playBtn.setIcon(icon)

    def stop(self):
        self.player.stop()
        icon = QIcon.fromTheme("media-playback-start")
        self.playBtn.setIcon(icon)

    def insertTrack(self, data):
        if data:
            self.playListData.append(data)
            totalTracks = self.playList.mediaCount()
            pos = totalTracks
            self.playlistTable.insertRow(totalTracks)
            self.playlistTable.setRowCount(totalTracks + 1)
            self.playlistTable.setItem(pos, 0, QTableWidgetItem(''))
            self.playlistTable.setItem(pos, 1,
                                       QTableWidgetItem(data['channel']))
            self.playlistTable.setItem(pos, 2, QTableWidgetItem(data['title']))
            media = QMediaContent(QUrl(data['url']))
            self.playList.addMedia(media)
            if totalTracks > 1:
                self.nextBtn.setEnabled(True)
        else:
            self.statusBar().showMessage('Total pistas: {0}'.format(
                self.playList.mediaCount()))

    def addDialog(self):
        url, ok = QInputDialog.getText(
            self, _translate('MainWindow', 'Add video/playlist'), 'URL:')
        if ok and url != '':
            self.addPCThread = addVideos(self, url)
            self.addPCThread.video.connect(self.insertTrack)
            self.addPCThread.start()

    def changeTrack(self, item):
        pos = item.row()
        self.playlistTable.setCellWidget(self.currentPos, 0, QLabel())
        self.playList.setCurrentIndex(pos)
        if self.player.state() == QMediaPlayer.StoppedState:
            icon = QIcon.fromTheme("media-playback-pause")
            self.playBtn.setIcon(icon)
            self.player.play()

    def delTracks(self):
        indexes = self.playlistTable.selectionModel().selectedRows()
        del_first = True
        for index in sorted(indexes):
            pos = index.row()
            if not del_first:
                pos -= 1
            else:
                del_first = False

            self.playlistTable.removeRow(pos)
            self.playListData.pop(pos)
            self.playList.removeMedia(pos)

        self.playlistPosChanged()

    def selectedTracks(self):
        totalSelected = len(self.playlistTable.selectedItems())
        if totalSelected > 0:
            self.removeBtn.setEnabled(True)
        else:
            self.removeBtn.setEnabled(False)
Esempio n. 9
0
class MediaPlayerTab(GalacteekTab):
    statePlaying = QMediaPlayer.PlayingState
    statePaused = QMediaPlayer.PausedState
    stateStopped = QMediaPlayer.StoppedState

    def __init__(self, *args, **kw):
        super(MediaPlayerTab, self).__init__(*args, **kw)

        self.playlistIpfsPath = None
        self.playlist = QMediaPlaylist()
        self.model = ListModel(self.playlist)

        self.playlistsMenu = QMenu(self)
        self.playlistsMenu.triggered.connect(self.onPlaylistsMenu)

        self.pListWidget = QWidget(self)
        self.uipList = ui_mediaplaylist.Ui_MediaPlaylist()
        self.uipList.setupUi(self.pListWidget)
        self.uipList.savePlaylistButton.clicked.connect(self.onSavePlaylist)
        self.uipList.savePlaylistButton.setEnabled(False)
        self.uipList.loadPlaylistButton.setPopupMode(QToolButton.InstantPopup)
        self.uipList.loadPlaylistButton.setMenu(self.playlistsMenu)

        self.clipMenu = QMenu(self)
        self.copyPathAction = QAction(getIconIpfsIce(),
                                      iCopyPlaylistPath(),
                                      self,
                                      triggered=self.onCopyPlaylistPath)
        self.loadPathAction = QAction(getIconIpfsIce(),
                                      iLoadPlaylistFromPath(),
                                      self,
                                      triggered=self.onLoadPlaylistPath)

        self.copyPathAction.setEnabled(False)
        self.clipMenu.addAction(self.copyPathAction)
        self.clipMenu.addAction(self.loadPathAction)

        self.uipList.clipPlaylistButton.setPopupMode(QToolButton.InstantPopup)
        self.uipList.clipPlaylistButton.setMenu(self.clipMenu)
        self.uipList.clearButton.clicked.connect(self.onClearPlaylist)

        self.uipList.nextButton.clicked.connect(self.onPlaylistNext)
        self.uipList.previousButton.clicked.connect(self.onPlaylistPrevious)
        self.uipList.nextButton.setIcon(self.style().standardIcon(
            QStyle.SP_MediaSkipForward))
        self.uipList.previousButton.setIcon(self.style().standardIcon(
            QStyle.SP_MediaSkipBackward))

        self.pListView = self.uipList.listView
        self.pListView.mousePressEvent = self.playlistMousePressEvent
        self.pListView.setModel(self.model)
        self.pListView.setResizeMode(QListView.Adjust)
        self.pListView.setMinimumWidth(self.width() / 2)

        self.duration = None
        self.playerState = None
        self.player = QMediaPlayer(self)
        self.player.setPlaylist(self.playlist)

        self.videoWidget = MPlayerVideoWidget(self.player, self)
        self.useUpdates(True)
        self.videoWidget.setSizePolicy(QSizePolicy.Expanding,
                                       QSizePolicy.Expanding)

        self.player.setVideoOutput(self.videoWidget)

        self.player.error.connect(self.onError)
        self.player.stateChanged.connect(self.onStateChanged)
        self.player.metaDataChanged.connect(self.onMetaData)
        self.player.durationChanged.connect(self.mediaDurationChanged)
        self.player.positionChanged.connect(self.mediaPositionChanged)
        self.player.videoAvailableChanged.connect(self.onVideoAvailable)

        self.pListView.activated.connect(self.onListActivated)
        self.playlist.currentIndexChanged.connect(self.playlistPositionChanged)
        self.playlist.currentMediaChanged.connect(self.playlistMediaChanged)
        self.playlist.mediaInserted.connect(self.playlistMediaInserted)
        self.playlist.mediaRemoved.connect(self.playlistMediaRemoved)

        self.togglePList = QToolButton(self)
        self.togglePList.setIcon(self.style().standardIcon(
            QStyle.SP_ArrowRight))
        self.togglePList.setFixedSize(32, 128)
        self.togglePList.clicked.connect(self.onTogglePlaylist)

        self.clipboardMediaItem = None
        self.clipboardButton = QToolButton(clicked=self.onClipboardClicked)
        self.clipboardButton.setIcon(getIconClipboard())
        self.clipboardButton.setEnabled(False)
        self.clipboardButton.setToolTip('Load media from clipboard')

        self.pinButton = QToolButton(clicked=self.onPinMediaClicked)
        self.pinButton.setIcon(getIcon('pin.png'))

        self.processClipboardItem(self.app.clipTracker.current, force=True)
        self.app.clipTracker.currentItemChanged.connect(self.onClipItemChange)

        self.playButton = QToolButton(clicked=self.onPlayClicked)
        self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))

        self.pauseButton = QToolButton(clicked=self.onPauseClicked)
        self.pauseButton.setIcon(self.style().standardIcon(
            QStyle.SP_MediaPause))
        self.pauseButton.setEnabled(False)

        self.stopButton = QToolButton(clicked=self.onStopClicked)
        self.stopButton.setIcon(self.style().standardIcon(QStyle.SP_MediaStop))
        self.stopButton.setEnabled(False)

        self.fullscreenButton = QToolButton(clicked=self.onFullScreen)
        self.fullscreenButton.setIcon(getIcon('fullscreen.png'))
        self.fullscreenButton.setToolTip(iFullScreen())

        self.seekSlider = QSlider(Qt.Horizontal, sliderMoved=self.onSeek)
        self.seekSlider.sliderReleased.connect(self.onSliderReleased)
        self.seekSlider.setObjectName('mediaPlayerSlider')
        self.durationLabel = QLabel()

        vLayout = QVBoxLayout()
        hLayoutControls = QHBoxLayout()
        hLayoutControls.setContentsMargins(0, 0, 0, 0)
        hLayoutControls.addWidget(self.clipboardButton)
        hLayoutControls.addWidget(self.pinButton)
        hLayoutControls.addWidget(self.playButton)
        hLayoutControls.addWidget(self.pauseButton)
        hLayoutControls.addWidget(self.stopButton)
        hLayoutControls.addWidget(self.seekSlider)
        hLayoutControls.addWidget(self.durationLabel)
        hLayoutControls.addWidget(self.fullscreenButton)
        vLayout.addWidget(self.videoWidget)
        vLayout.addLayout(hLayoutControls)

        hLayout = QHBoxLayout()
        hLayout.addLayout(vLayout)
        hLayout.addWidget(self.pListWidget)
        hLayout.addWidget(self.togglePList)

        self.pListWidget.hide()

        self.vLayout.addLayout(hLayout)
        self.update()
        self.videoWidget.changeFocus()

    @property
    def isPlaying(self):
        return self.playerState == self.statePlaying

    @property
    def isPaused(self):
        return self.playerState == self.statePaused

    @property
    def isStopped(self):
        return self.playerState == self.stateStopped

    def useUpdates(self, updates=True):
        # Enable widget updates or not on the video widget
        self.videoWidget.setUpdatesEnabled(updates)

    def update(self):
        self.app.task(self.updatePlaylistsMenu)

    def onFullScreen(self):
        self.videoWidget.viewFullScreen(True)

    def onClearPlaylist(self):
        self.copyPathAction.setEnabled(False)
        self.player.stop()
        self.clearPlaylist()

    def onLoadPlaylistPath(self):
        current = self.app.clipTracker.getCurrent()
        if current:
            self.app.task(self.loadPlaylistFromPath, current.path)

    def onCopyPlaylistPath(self):
        if self.playlistIpfsPath:
            self.app.setClipboardText(self.playlistIpfsPath)

    def onPinMediaClicked(self):
        currentMedia = self.playlist.currentMedia()
        if currentMedia.isNull():
            return messageBox(iNoMediaInPlaylist())

        ensure(self.pinMedia(currentMedia))

    @ipfsOp
    async def pinMedia(self, ipfsop, media):
        mediaUrl = qurlPercentDecode(media.canonicalUrl())
        path = IPFSPath(mediaUrl, autoCidConv=True)

        if path.valid:
            await ipfsop.ctx.pin(str(path), qname='mediaplayer')

    @ipfsOp
    async def updatePlaylistsMenu(self, ipfsop):
        currentList = [
            action.text() for action in self.playlistsMenu.actions()
        ]
        listing = await ipfsop.filesList(self.profile.pathPlaylists)
        for entry in listing:
            if entry['Name'] in currentList:
                continue
            action = QAction(entry['Name'], self)
            action.setData(entry)
            self.playlistsMenu.addAction(action)

    def playlistShowContextMenu(self, event):
        selModel = self.pListView.selectionModel()
        idx = self.pListView.indexAt(event.pos())
        if not idx.isValid():
            return

        path = self.model.data(idx)
        if path:
            selModel.reset()
            selModel.select(idx, QItemSelectionModel.Select)

            menu = QMenu(self)
            menu.addAction(getIcon('clear-all.png'), iPlaylistRemoveMedia(),
                           functools.partial(self.onRemoveMediaFromIndex, idx))
            menu.exec_(event.globalPos())

    def onRemoveMediaFromIndex(self, idx):
        self.playlist.removeMedia(idx.row())

    def playlistMousePressEvent(self, event):
        if event.button() == Qt.RightButton:
            self.pListView.selectionModel().reset()
            self.playlistShowContextMenu(event)
        else:
            if not self.pListView.indexAt(event.pos()).isValid():
                self.deselectPlaylistItems()

            QListView.mousePressEvent(self.pListView, event)

    def onPlaylistsMenu(self, action):
        entry = action.data()
        self.app.task(self.loadPlaylistFromPath, joinIpfs(entry['Hash']))

    def onSavePlaylist(self):
        paths = self.playlistGetPaths()
        listName = inputText(title=iPlaylistName(), label=iPlaylistName())
        if not listName:
            return

        obj = JSONPlaylistV1(listName=listName, itemPaths=paths)
        self.app.task(self.savePlaylist, obj, listName)

    @ipfsOp
    async def savePlaylist(self, ipfsop, obj, name):
        objPath = os.path.join(self.profile.pathPlaylists, name)
        exists = await ipfsop.filesStat(objPath)

        if exists:
            await ipfsop.filesRm(objPath)

        ent = await ipfsop.client.core.add_json(obj.root)

        if ent:
            await ipfsop.filesLinkFp(ent, objPath)
            self.playlistIpfsPath = joinIpfs(ent['Hash'])
            self.copyPathAction.setEnabled(True)

        self.update()

    @ipfsOp
    async def loadPlaylistFromPath(self, ipfsop, path):
        try:
            obj = await ipfsop.jsonLoad(path)
        except Exception:
            return messageBox(iCannotLoadPlaylist())

        if obj is None:
            return messageBox(iCannotLoadPlaylist())

        try:
            # Assume v1 format for now, when the format evolves we'll just
            # use json validation
            pList = JSONPlaylistV1(data=obj)
            self.clearPlaylist()

            for item in pList.items():
                self.queueFromPath(item['path'])

            self.playlistIpfsPath = path
            self.copyPathAction.setEnabled(True)
        except Exception:
            return messageBox(iCannotLoadPlaylist())

    def playlistMediaInserted(self, start, end):
        self.uipList.savePlaylistButton.setEnabled(
            self.playlist.mediaCount() > 0)

    def playlistMediaRemoved(self, start, end):
        self.uipList.savePlaylistButton.setEnabled(
            self.playlist.mediaCount() > 0)

        self.model.modelReset.emit()

    def playlistGetPaths(self):
        return [u.path() for u in self.playlistGetUrls()]

    def playlistGetUrls(self):
        urls = []
        for idx in range(0, self.playlist.mediaCount()):
            media = self.playlist.media(idx)
            urls.append(media.canonicalUrl())
        return urls

    def onClipItemChange(self, item):
        self.processClipboardItem(item)

    def processClipboardItem(self, item, force=False):
        if not item:
            return

        def analyzeMimeType(cItem):
            if cItem.mimeCategory in ['audio', 'video', 'image']:
                self.clipboardMediaItem = cItem
                self.clipboardButton.setEnabled(True)
                self.clipboardButton.setToolTip(cItem.path)
            else:
                self.clipboardButton.setEnabled(False)
                self.clipboardButton.setToolTip(iClipboardEmpty())

        if force:
            analyzeMimeType(item)
        else:
            item.mimeTypeAvailable.connect(lambda mType: analyzeMimeType(item))

    def onClipboardClicked(self):
        if self.clipboardMediaItem:
            self.playFromPath(self.clipboardMediaItem.path)
        else:
            messageBox('Not a multimedia resource')

    def onSliderReleased(self):
        pass

    def onPlaylistNext(self):
        self.playlist.next()

    def onPlaylistPrevious(self):
        self.playlist.previous()

    def onPlayClicked(self):
        self.player.play()

    def onPauseClicked(self):
        if self.isPlaying:
            self.player.pause()
        elif self.isPaused:
            self.player.play()

    def onStopClicked(self):
        self.player.stop()
        self.player.setPosition(0)
        self.seekSlider.setValue(0)
        self.seekSlider.setRange(0, 0)

    def onSeek(self, seconds):
        if self.player.isSeekable():
            self.player.setPosition(seconds * 1000)

    def onTogglePlaylist(self):
        self.pListWidget.setVisible(self.pListWidget.isHidden())

    def onError(self, error):
        messageBox(iPlayerError(error))

    def onStateChanged(self, state):
        self.playerState = state
        self.updateControls(state)

    def updateControls(self, state):
        if self.isStopped:
            self.stopButton.setEnabled(False)
            self.pauseButton.setEnabled(False)
            self.playButton.setEnabled(True)
            self.seekSlider.setEnabled(False)
            self.duration = None
        elif self.isPlaying:
            self.seekSlider.setRange(0, self.player.duration() / 1000)
            self.seekSlider.setEnabled(True)
            self.pauseButton.setEnabled(True)
            self.playButton.setEnabled(False)
            self.stopButton.setEnabled(True)

    def onListActivated(self, index):
        if index.isValid():
            self.playlist.setCurrentIndex(index.row())
            self.player.play()

    def onMetaData(self):
        # Unfinished
        if self.player.isMetaDataAvailable():
            availableKeys = self.player.availableMetaData()

            for key in availableKeys:
                self.player.metaData(key)

    def playFromUrl(self, url, mediaName=None):
        if self.isPlaying:
            self.player.stop()

        cUrls = self.playlistGetUrls()
        for u in cUrls:
            if u.toString() == url.toString():
                return messageBox(iAlreadyInPlaylist())

        media = QMediaContent(url)
        if self.playlist.addMedia(media):
            count = self.model.rowCount()
            if count > 0:
                self.playlist.setCurrentIndex(count - 1)

        self.player.play()

    def playFromPath(self, path, mediaName=None):
        mediaUrl = self.app.subUrl(path)
        self.playFromUrl(mediaUrl)

    def queueFromPath(self, path, playLast=False, mediaName=None):
        mediaUrl = self.app.subUrl(path)
        self.playlist.addMedia(QMediaContent(mediaUrl))

        if playLast:
            count = self.playlist.mediaCount()

            if count > 0:
                self.player.stop()
                self.playlist.setCurrentIndex(count - 1)
                self.player.play()

    def clearPlaylist(self):
        self.playlist.clear()
        self.pListView.reset()

    def playlistPositionChanged(self, position):
        self.pListView.setCurrentIndex(self.model.index(position, 0))

    def deselectPlaylistItems(self):
        self.pListView.selectionModel().reset()

    def playlistMediaChanged(self, media):
        selModel = self.pListView.selectionModel()

        self.deselectPlaylistItems()

        self.model.modelReset.emit()
        idx = self.model.index(self.playlist.currentIndex(), 0)
        if idx.isValid():
            selModel.select(idx, QItemSelectionModel.Select)

    def onVideoAvailable(self, available):
        if available:
            if self.isPlaying:
                self.useUpdates(False)
            elif self.isStopped or self.isPaused:
                self.useUpdates(True)
        else:
            self.useUpdates(True)

    def mediaDurationChanged(self, duration):
        self.duration = duration / 1000
        self.seekSlider.setMaximum(self.duration)

    def mediaPositionChanged(self, progress):
        progress /= 1000

        if self.duration:
            cTime = durationConvert(progress)
            tTime = durationConvert(self.duration)
            self.durationLabel.setText('{0} ({1})'.format(
                cTime.toString(), tTime.toString()))

        if not self.seekSlider.isSliderDown():
            self.seekSlider.setValue(progress)

    async def onClose(self):
        self.player.stop()
        self.player.setMedia(QMediaContent(None))
        return True

    def playerAvailable(self):
        return mediaPlayerAvailable(player=self.player)
Esempio n. 10
0
class BuddingVoice(QtWidgets.QPushButton):
    def __init__(self, parent, controller):
        super(BuddingVoice, self).__init__(parent)
        # music button config.
        self.controller = controller
        self.controller.register_observer("music_button", self)
        self.clicked.connect(self.voice_clicked)
        self.musicon = 1

        #playlist, set according to level
        self.playlist = QMediaPlaylist()
        self.url = None
        self.level = self.controller.get_level()
        print("user level", self.level)
        #below is for test
        #self.level = 3
        self.url = self.choose_music(self.level)
        self.playlist.addMedia(QMediaContent(self.url))
        self.playlist.setPlaybackMode(QMediaPlaylist.Loop)
        
        #player
        self.player = QMediaPlayer()
        self.player.setPlaylist(self.playlist)
        self.player.play()

    def voice_clicked(self):
        if self.musicon:
            self.player.pause()
        else:
            #below is for test on_level_update
            #self.on_level_update()
            self.player.play()
        self.musicon = 1 - self.musicon

    def on_level_update(self, newlevel):
        #below is for test
        #newlevel = self.level + 1
        print("newlevel", newlevel, "oldlevel", self.level)
        self.player.pause()
        self.playlist.removeMedia(0)
        self.url = self.choose_music(newlevel)
        self.playlist.addMedia(QMediaContent(self.url))
        self.player.setPlaylist(self.playlist)
        self.player.play()
        self.level = newlevel

    def music_path(self, music):
        fp = "./music/" + music
        return os.path.abspath(fp)

    def choose_music(self, level):
        mystr = ""
        if level == 1:
            mystr = self.music_path("sad.mp3")
        elif level == 2:
            mystr = self.music_path("neutral.mp3")
        else:
            mystr = self.music_path("happy.mp3")
        return QUrl.fromLocalFile(mystr)

    def on_logout(self):
        self.musicon = 0
        self.player.stop()
Esempio n. 11
0
class DBPlayer(QWidget):
    # signal
    signaltxt = pyqtSignal(str)
    signalnum = pyqtSignal(int)

    def __init__(self):
        super(DBPlayer, self).__init__()
        self.setMaximumSize(16777215, 35)
        # Init Player
        self.messtitle = TITL_PROG
        self.namemedia = ''
        self.albumname = ''
        self.currentPlaylist = QMediaPlaylist()
        self.player = QMediaPlayer()
        self.player.stateChanged.connect(self.qmp_stateChanged)
        self.player.positionChanged.connect(self.qmp_positionChanged)
        self.player.volumeChanged.connect(self.qmp_volumeChanged)
        self.player.durationChanged.connect(self.qmp_durationChanged)
        self.player.setVolume(60)
        # Init GUI
        self.setLayout(self.addControls())
        self.infoBox = None

    def addControls(self):
        # buttons
        self.playBtn = QPushButton()
        self.playBtn.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
        self.playBtn.setStyleSheet('border: 0px;')
        stopBtn = QPushButton()
        stopBtn.setIcon(self.style().standardIcon(QStyle.SP_MediaStop))
        stopBtn.setStyleSheet('border: 0px;')
        prevBtn = QPushButton()
        prevBtn.setIcon(self.style().standardIcon(QStyle.SP_MediaSkipBackward))
        prevBtn.setStyleSheet('border: 0px;')
        nextBtn = QPushButton()
        nextBtn.setIcon(self.style().standardIcon(QStyle.SP_MediaSkipForward))
        nextBtn.setStyleSheet('border: 0px;')
        volumeDescBtn = QPushButton('▼')
        volumeDescBtn.setIcon(self.style().standardIcon(QStyle.SP_MediaVolume))
        volumeDescBtn.setMaximumWidth(30)
        volumeDescBtn.setStyleSheet('border: 0px;')
        volumeIncBtn = QPushButton('▲')
        volumeIncBtn.setIcon(self.style().standardIcon(QStyle.SP_MediaVolume))
        volumeIncBtn.setMaximumWidth(40)
        volumeIncBtn.setStyleSheet('border: 0px;')
        infoBtn = QPushButton()
        infoBtn.setIcon(self.style().standardIcon(
            QStyle.SP_FileDialogContentsView))
        infoBtn.setStyleSheet('border: 0px;')

        # seek slider
        self.seekSlider = QSlider(Qt.Horizontal, self)
        self.seekSlider.setMinimum(0)
        self.seekSlider.setMaximum(100)
        self.seekSlider.setTracking(False)

        # labels position start/end
        self.seekSliderLabel1 = QLabel('0:00')
        self.seekSliderLabel2 = QLabel('0:00')

        # layout
        controlArea = QHBoxLayout()
        controlArea.addWidget(prevBtn)
        controlArea.addWidget(self.playBtn)
        controlArea.addWidget(stopBtn)
        controlArea.addWidget(nextBtn)
        controlArea.addWidget(self.seekSliderLabel1)
        controlArea.addWidget(self.seekSlider)
        controlArea.addWidget(self.seekSliderLabel2)
        controlArea.addWidget(infoBtn)
        controlArea.addWidget(volumeDescBtn)
        controlArea.addWidget(volumeIncBtn)

        # link buttons to media
        self.seekSlider.sliderMoved.connect(self.seekPosition)
        self.playBtn.clicked.connect(self.playHandler)
        stopBtn.clicked.connect(self.stopHandler)
        volumeDescBtn.clicked.connect(self.decreaseVolume)
        volumeIncBtn.clicked.connect(self.increaseVolume)
        prevBtn.clicked.connect(self.prevItemPlaylist)
        nextBtn.clicked.connect(self.nextItemPlaylist)
        infoBtn.clicked.connect(self.displaySongInfo)

        return controlArea

    def playHandler(self):
        if self.player.state() == QMediaPlayer.PlayingState:
            self.player.pause()
            message = (' [Paused at position %s]' %
                       self.seekSliderLabel1.text())
            self.messtitle = self.namemedia + message
            self.signaltxt.emit(self.messtitle)
        else:
            if self.player.state() == QMediaPlayer.StoppedState:
                if self.player.mediaStatus() == QMediaPlayer.NoMedia:
                    if self.currentPlaylist.mediaCount() != 0:
                        self.player.setPlaylist(self.currentPlaylist)
                elif self.player.mediaStatus() == QMediaPlayer.LoadedMedia:
                    self.player.play()
                elif self.player.mediaStatus() == QMediaPlayer.BufferedMedia:
                    self.player.play()
            elif self.player.state() == QMediaPlayer.PlayingState:
                pass
            elif self.player.state() == QMediaPlayer.PausedState:
                self.player.play()
            if self.player.volume() is not None and self.player.state(
            ) == QMediaPlayer.PlayingState:
                message = ' [Volume %d]' % self.player.volume()
                self.messtitle = self.namemedia + message
                self.signaltxt.emit(self.messtitle)

    def stopHandler(self):
        if self.player.state() == QMediaPlayer.PlayingState:
            self.stopState = True
            self.player.stop()
        elif self.player.state() == QMediaPlayer.PausedState:
            self.player.stop()
        elif self.player.state() == QMediaPlayer.StoppedState:
            pass
        if self.player.volume() is not None and self.player.state(
        ) == QMediaPlayer.PlayingState:
            self.messtitle = self.namemedia + (' [Stopped]')
            self.signaltxt.emit(self.messtitle)

    def qmp_stateChanged(self):
        if self.player.state() == QMediaPlayer.StoppedState:
            self.player.stop()
        # buttons icon play/pause change
        if self.player.state() == QMediaPlayer.PlayingState:
            self.playBtn.setIcon(self.style().standardIcon(
                QStyle.SP_MediaPause))
        else:
            self.playBtn.setIcon(self.style().standardIcon(
                QStyle.SP_MediaPlay))

    def qmp_positionChanged(self, position):
        # update position slider
        self.seekSlider.setValue(position)
        # update the text label
        self.seekSliderLabel1.setText(
            '%d:%02d' % (int(position / 60000), int((position / 1000) % 60)))

    def seekPosition(self, position):
        sender = self.sender()
        if isinstance(sender, QSlider):
            if self.player.isSeekable():
                self.player.setPosition(position)

    def qmp_volumeChanged(self):
        if self.player.volume() is not None:
            message = (' [Playing at Volume %d]' % (self.player.volume()))
            if self.namemedia != '':
                self.messtitle = self.namemedia + message
            else:
                self.messtitle = "Initialisation player " + message
            self.signaltxt.emit(self.messtitle)

    def qmp_durationChanged(self, duration):
        self.seekSlider.setRange(0, duration)
        self.seekSliderLabel2.setText(
            '%d:%02d' % (int(duration / 60000), int((duration / 1000) % 60)))
        nummedia = self.currentPlaylist.mediaCount()
        curmedia = self.currentPlaylist.currentIndex()
        #artist = self.player.metaData(QMediaMetaData.Author)
        #tittle = self.player.metaData(QMediaMetaData.Title)
        self.namemedia = path.basename(self.homMed[curmedia])
        self.namemedia = '[%02d/%02d' % (
            curmedia + 1, nummedia) + '] "' + self.namemedia + '"'
        self.buildPlaylist()
        message = (' [Playing at Volume %d]' % (self.player.volume()))
        if self.player.volume() is not None and self.player.state(
        ) == QMediaPlayer.PlayingState:
            self.messtitle = self.namemedia + message
            self.signaltxt.emit(self.messtitle)

    def buildPlaylist(self):
        """Build play list."""
        nummedia = self.currentPlaylist.mediaCount()
        curmedia = self.currentPlaylist.currentIndex() + 1
        compteur = 1
        self.textplaylist = '<b>' + self.albumname + '</b>'
        self.textplaylist += '<table class="tftable" border="0">'
        for namemedia in self.homMed:
            media = path.basename(namemedia)
            media = '[%02d/%02d' % (compteur, nummedia) + '] "' + media + '"'
            if curmedia == compteur:
                self.textplaylist += '<tr><td><b>' + media + '</b></td></tr>'
            else:
                self.textplaylist += '<tr><td>' + media + '</td></tr>'
            compteur += 1
        self.textplaylist = self.textplaylist + '</table>'
        self.playBtn.setToolTip(self.textplaylist)
        self.signalnum.emit(curmedia - 1)

    def increaseVolume(self):
        """Volume +."""
        vol = self.player.volume()
        vol = min(vol + 5, 100)
        self.player.setVolume(vol)

    def decreaseVolume(self):
        """Volume -."""
        vol = self.player.volume()
        vol = max(vol - 5, 0)
        self.player.setVolume(vol)

    def prevItemPlaylist(self):
        self.player.playlist().previous()
        if self.currentPlaylist.currentIndex() == -1:
            self.player.playlist().previous()

    def nextItemPlaylist(self):
        self.player.playlist().next()
        if self.currentPlaylist.currentIndex() == -1:
            self.player.playlist().next()

    def addMediaslist(self, listmedias, position, albumname):
        if self.currentPlaylist.mediaCount() > 0:
            self.currentPlaylist.removeMedia(0,
                                             self.currentPlaylist.mediaCount())
            self.player.stop()
            self.stopHandler()
        self.currentPlaylist.removeMedia(0, self.currentPlaylist.mediaCount())
        self.albumname = albumname
        if listmedias:
            self.homMed = listmedias
            for media in self.homMed:
                self.currentPlaylist.addMedia(
                    QMediaContent(QUrl.fromLocalFile(media)))
            self.currentPlaylist.setCurrentIndex(position)
            self.playHandler()

    def displaySongInfo(self):
        # extract datas
        metaDataKeyList = self.player.availableMetaData()
        fullText = '<table class="tftable" border="0">'
        for key in metaDataKeyList:
            value = str(self.player.metaData(key)).replace("'", "").replace(
                "[", "").replace("]", "")
            if key == 'Duration':
                value = '%d:%02d' % (int(
                    int(value) / 60000), int((int(value) / 1000) % 60))
            fullText = fullText + '<tr><td>' + key + '</td><td>' + value + '</td></tr>'
        fullText = fullText + '</table>'
        # re-init
        if self.infoBox is not None:
            self.infoBox.destroy()
        # infos box
        self.infoBox = QMessageBox(self)
        self.infoBox.setWindowTitle('Detailed Song Information')
        self.infoBox.setTextFormat(Qt.RichText)
        self.infoBox.addButton('OK', QMessageBox.AcceptRole)
        self.infoBox.setText(fullText)
        self.infoBox.show()
class DirectoryPlaylist(Playlist):

    def __init__(self, parent=None):
        super(DirectoryPlaylist, self).__init__(parent)

        self._directories_urls = set()
        self._added_song_urls = set()
        self._tracks = []

        self._qPlaylist = QMediaPlaylist(parent)
        self._qPlaylist.mediaAboutToBeInserted.connect(
            lambda s, e: self.mediaAboutToBeInserted.emit(s, e))
        self._qPlaylist.mediaInserted.connect(
            lambda s, e: self.mediaInserted.emit(s, e))
        self._qPlaylist.mediaAboutToBeRemoved.connect(
            lambda s, e: self.mediaAboutToBeRemoved.emit(s, e))
        self._qPlaylist.mediaRemoved.connect(
            lambda s, e: self.mediaRemoved.emit(s, e))
        self._qPlaylist.mediaChanged.connect(
            lambda s, e: self.mediaChanged.emit(s, e))

    def songs(self):
        return self._tracks

    def albums_data(self):
        return self._albumsData

    def artists_data(self):
        return self._artistsData

    def __traverse_directory(self, url, func):
        songs = []
        for root, dirs, files in os.walk(url):
                for file in files:
                    abs_path = os.path.join(root, file)
                    song = func(abs_path)
                    if song:
                        songs.append(song)
        return songs

    def is_empty(self):
        return not self._tracks

    def add_song(self, abs_path):
        if abs_path not in self._added_song_urls:
            url = QtCore.QUrl.fromLocalFile(abs_path)
            song = MediaFactory.create_media(abs_path)
            if not song:
                return None
            added = self._qPlaylist.addMedia(QMediaContent(url))
            if not added:
                return None

            self._tracks.append(song)
            self._added_song_urls.add(abs_path)
            return song

    def remove_song(self, row):
        if row < 0 or row > self.song_count() - 1:
            return None

        if self._qPlaylist.currentIndex() == row:
            if row < self.song_count():
                self._qPlaylist.setCurrentIndex(row + 1)
            else:
                self._qPlaylist.setCurrentIndex(row - 1)

        removed = self._qPlaylist.removeMedia(row)
        if not removed:
            return

        del self._tracks[row]
        url = self.get_song_abs_path(row)
        self._added_song_urls.discard(url)

    def add_directory(self, directory):
        if directory not in self._directories_urls:
            self._directories_urls.add(directory)
            songs = self.__traverse_directory(directory, self.add_song)
            return songs
        return None

    def add_directories(self, directories):
        songs = []
        for directory in directories:
            if directory not in self._directories_urls:
                current_songs = self.add_directory(directory)
                if current_songs:
                    songs.extend(current_songs)
        return songs

    def remove_directory(self, directory):
        if directory not in self._directories_urls:
            return False  # raise Error

        self.__traverse_directory(directory, self.remove_song)

    def setCurrentIndex(self, index):
        self._qPlaylist.setCurrentIndex(index)

    def getCurrentIndex(self):
        return self._qPlaylist.currentIndex()

    def getCurrentSong(self):
        return self._qPlaylist.currentMedia()

    def clear(self):
        self._tracks = []
        self._directories_urls = set()
        self._added_song_urls = set()
        self._qPlaylist.clear()

    @property
    def internalPlaylist(self):
        return self._qPlaylist

    def song_count(self):
        return len(self._tracks)

    def get_song_metadata(self, row, tags):
        if row < 0 or row > self.song_count() - 1:
            return None
        if not isinstance(tags, list):
            tags = [tags]
        return self._tracks[row].get_values(tags)

    def get_song(self, row):
        if row < 0 or row > self.song_count() - 1:
            return None

        return self._tracks[row]

    def get_song_title(self, row):
        if row < 0 or row > self.song_count() - 1:
            return None
        k, v = self.get_song_metadata(row, 'title').popitem()
        try:
            return v[0]
        except IndexError:
            return None

    def get_song_album(self, row):
        if row < 0 or row > self.song_count() - 1:
            return None
        k, v = self.get_song_metadata(row, 'album').popitem()
        try:
            return v[0]
        except IndexError:
            return None

    def get_song_artist(self, row):
        if row < 0 or row > self.song_count() - 1:
            return None

        k, v = self.get_song_metadata(row, 'artist').popitem()
        try:
            return v[0]
        except IndexError:
            return None

    def get_song_genre(self, row):
        if row < 0 or row > self.song_count() - 1:
            return None

        k, v = self.get_song_metadata(row, 'genre').popitem()
        try:
            return v[0]
        except IndexError:
            return None

    def get_song_abs_path(self, row):
        if row < 0 or row > self.song_count() - 1:
            return None

        return self._tracks[row].get_abs_path()

    def getDirectories(self):
        return self._directories_urls

    def getAddedSongUrls(self):
        return self._added_song_urls

    def __str__(self):
        return str(self._tracks)

    def __eq__(self, other):
        return (isinstance(other, self.__class__) and
                self._directories_urls == other._directories_urls and
                self._added_song_urls == other._added_song_urls)
Esempio n. 13
0
class MainWindow(QMainWindow):
    def __init__(self):

        super(MainWindow, self).__init__()

        #Load  mainwindow.ui from Qt Designer
        uic.loadUi('../ui/mainwindow.ui', self)
        #Load all settings
        self.initMainWindow()
        #Show the main window
        self.show()

    def initMainWindow(self):
        """
            This function initialize all the buttons and all the setting for
            displaying and control the video.
        """
        self.setWindowIcon(
            QtGui.QIcon(
                "../resources/icons/GUI_Icons/1519132568_movavi-video-editor.png"
            ))
        #delete all temp files
        self.deleteAllTemp()
        self.setWindowTitle("VideoEditor")
        """<Objects>"""
        #Object for controling all edit functions
        self.edit = Panel()
        """</Objects>"""
        """--------------------<Global variables>----------------------------"""
        #Thread manager variable
        self.threadmanager = True
        #Audio file holder
        self.audioFile = ''
        #frame time variable
        self.frameTime = ''
        #frame file path
        self.frameFilePath = ''
        #Total number of curent media opened
        self.totalIndex = -1
        #Dictionary for index and path for the media
        self.curentFiles = {}
        #Index that indicates the curent media selected
        self.curentIndex = self.totalIndex
        #Curent index of Concatenate ListView
        self.curentIndexOfConcatenateList = -1
        #Dictionary with all video path for concatenate
        self.concatenateVideos = {}
        #Total number of videos added to the concatenate list view
        self.totalNrOfVideosConcat = -1
        #Media play speed
        self.speed = 1
        #
        self.subtitlesFileVG = ''
        """-------------------</Global variables>----------------------------"""
        """----------------<Media player settings>---------------------------"""
        #Create a mediaplayer object to control the video
        self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface)
        self.mediaPlayer.setVolume(50)

        #Create a playlist object for multiple videos
        self.playlist = QMediaPlaylist()
        self.mediaPlayer.setPlaylist(self.playlist)

        self.model = PlaylistModel(self.playlist)
        self.videoFiles.setModel(self.model)
        self.playlist.currentIndexChanged.connect(
            self.playlist_position_changed)
        selection_model = self.videoFiles.selectionModel()
        selection_model.selectionChanged.connect(
            self.playlist_selection_changed)

        #Create videoWidget object for displaying the video
        videoWidget = QVideoWidget()
        #videoWidget set
        self.videoPreviewLayout = QVBoxLayout()
        self.videoPreviewLayout.addWidget(videoWidget)
        self.vpfVideoPreview.setLayout(self.videoPreviewLayout)
        """----------------</Media player settings>--------------------------"""
        """-----------------<Buttons&Labels settings>-------------------------"""
        #Create Open Video button in taskbar
        self.open = QAction('&Open Video', self)
        self.open.setStatusTip('Open Video')
        self.menuFiles.addAction(self.open)
        self.setAcceptDrops(True)

        #PlayButton
        self.playButton.setEnabled(False)
        #Speed label
        self.speedLabel.setText("1.0x")
        #Slider settings timeline
        self.videoTimeSlider.setRange(0, 0)
        #Slider settings volume
        self.volume.setRange(0, 100)
        self.volume.setValue(50)
        self.volumeTextDisplay.setText("50%")
        #Cut lock buttons
        self.lockButtonStart.setCheckable(True)
        self.lockButtonFinish.setCheckable(True)
        #Cut text editor settings
        self.cutStart.setReadOnly(True)
        self.cutFinish.setReadOnly(True)
        self.cutStart.setText("0:00:00")
        self.cutFinish.setText("0:00:00")
        #Resolution Image settings
        self.resolutionIcon.setPixmap(
            QPixmap("../resources/icons/GUI_Icons/720.png"))
        """-----------------</Buttons&Labels settings>-------------------------"""
        """-----------------<Buttons connections>------------------------------"""
        """           -----------<Player buttons>---------                    """
        #Play button
        self.playButton.clicked.connect(self.playVideo)

        #back 15 seconds Button
        self.skipbackButton.clicked.connect(self.skipbackFunction)
        #skip 15 seconds forward Button
        self.skipforwardButton.clicked.connect(self.skipforwadFunction)

        #fastorForward button
        self.fastForward.clicked.connect(self.fastForwardFunction)
        #rewind button
        self.rewind.clicked.connect(self.rewindFunction)

        #Add video button
        self.addButton.clicked.connect(self.openFile)
        #Remove video button
        self.deleteButton.clicked.connect(self.RemoveVideo)
        #save video
        self.saveVideo.clicked.connect(self.saveVideoFunction)
        #Time slider
        self.videoTimeSlider.sliderMoved.connect(self.setPosition)

        #Volume slider
        self.volume.sliderMoved.connect(self.volumeControl)

        #media player change state
        self.mediaPlayer.stateChanged.connect(self.mediaStateChange)
        self.mediaPlayer.positionChanged.connect(self.positionChange)
        self.mediaPlayer.durationChanged.connect(self.durationChange)
        """         -----------</Player buttons>---------                   """
        #Open file
        self.open.triggered.connect(self.openFile)
        """         -------------<Edit  Buttons>-----------------           """
        """<Concatenate>"""
        #Create a model for Concatenate Lisview
        self.concatenateModel = QtGui.QStandardItemModel()
        #Set the model to the Concatenate List view
        self.concatenateList.setModel(self.concatenateModel)
        #Concatenate list of videos
        self.concatenateList.clicked[QtCore.QModelIndex].connect(
            self.setConcatenateIndex)
        #Add button to concatenate list
        self.addConcatenate.clicked.connect(self.addVideoToConcatenate)
        # When you receive the signal, you call QtGui.QStandardItemModel.itemFromIndex()
        # on the given model index to get a pointer to the item
        self.removeConcatenate.clicked.connect(self.removeVideoToConcatenate)
        #Concatenate Button
        self.concatenateButton.clicked.connect(self.concatenateThreadFunction)
        """</Concatenate>"""
        """<Cut>"""
        #Lock cut filed1
        self.lockButtonStart.clicked.connect(self.lockButtonChangeIconStart)
        #Lock cut filed2
        self.lockButtonFinish.clicked.connect(self.lockButtonChangeIconFinish)
        #Cut button
        self.cutButton.clicked.connect(self.cutThreadFunction)
        """</Cut>"""
        """<Resolution>"""
        #Resoluiton ComboBox for selecting the desired resolution
        self.ResolutionsList.currentIndexChanged.connect(
            self.changeResolutionDisplay)
        #Change resolution button
        self.changeResolution.clicked.connect(self.changeResolutionThread)
        """</Resoluton>"""
        """<Mirror>"""
        #Mirror button
        self.mirroringButton.clicked.connect(self.mirrorThread)
        """</Mirror>"""
        """<Audio replace>"""

        self.openAudioFile.clicked.connect(self.openAudio)
        self.removeAudioFile.clicked.connect(self.removeAudioFileFunction)
        self.audioModeSelect.currentIndexChanged.connect(
            self.changeAudioBackground)
        self.AddAudio.clicked.connect(self.SoundReplaceThread)
        """</Audio replace>"""
        """<GetFrame>"""

        self.getFrameButton.clicked.connect(self.GetFrameFunction)
        #self.saveFrameButton.setShortcut("Ctrl+S")
        self.saveFrameButton.setStatusTip('Save File')
        self.saveFrameButton.clicked.connect(self.saveFrame)
        """"</GetFrame>"""
        """<AddSubtitles>"""

        self.loadSubtitles.clicked.connect(self.loadSubtitlesFunction)
        self.cleanButton.clicked.connect(self.removeSubtitlesFunction)
        self.addSubtitle.clicked.connect(self.addSubtitlesThread)
        """</AddSubtitles>"""
        """<VideoGrep>"""
        self.loadSubtitlesVG.clicked.connect(self.loadSubtitlesVideoGrep)
        self.cleanButtonVideoGrep.clicked.connect(
            self.removeSubtitlesVideoGrep)
        self.videoGrep.clicked.connect(self.videoGrepThread)
        """</VideoGrep>"""
        """         -------------</Edit  Buttons>-----------------            """
        """         -------------<Shortcut Buttons>---------------            """

        self.soundShortcut.clicked.connect(self.soundShortcutKey)
        self.getFrameShortcut.clicked.connect(self.getFrameShortcutKey)
        self.cutShortcut.clicked.connect(self.cutShortcutKey)
        self.concatShortcut.clicked.connect(self.concatShortcutKey)
        self.mirrorShortcut.clicked.connect(self.mirrorShortcutKey)
        """         -------------</Shortcut Buttons>---------------            """
        """-----------------</Buttons connections>----------------------------"""
        """-------------------<Threads for editing>--------------------------"""

        self.pool = QThreadPool()
        """-------------------</Threads for editing>--------------------------"""
        """<Experimental>"""
        qtimeline = QTimeLine(360, 1)
        self.test = QVBoxLayout()
        qtimeline2 = QTimeLine(360, 1)
        self.test.addWidget(qtimeline)
        self.test.addWidget(qtimeline2)

        self.sfTimeLineFrame.setLayout(self.test)
        #self.editMenu.setCurrentIndex(5)
        """</Experimental>"""

        #Set output to the video
        self.mediaPlayer.setVideoOutput(videoWidget)

    """ ------------------<Concatenate functions>---------------------------"""

    def setConcatenateIndex(self, index):
        """
                Set the curent index of the selected
                item from concatenate list view (self.concatenateList)
            """
        #Get the item from the model of index "index"
        item = self.concatenateModel.itemFromIndex(index)
        #Get the "item " row index
        self.curentIndexOfConcatenateList = item.index().row()
        print(self.concatenateVideos)

    def addVideoToConcatenate(self):
        """
                Add from 'concatenateVideoList'
                a video to 'concatenateList'
            """

        try:
            #Add the current video selected from the ComboBox to the concatenate list view
            item = QtGui.QStandardItem(self.curentFiles[
                self.concatenateVideoList.currentIndex()].split('/')[-1])
            self.concatenateModel.appendRow(item)
            self.totalNrOfVideosConcat += 1
            self.concatenateVideos[
                self.totalNrOfVideosConcat] = self.curentFiles[
                    self.concatenateVideoList.currentIndex()]

        except:
            print("No video")
            QMessageBox.about(self, "No video", "Please add a video!       ")
            print(self.concatenateVideoList.currentIndex())

    def removeVideoToConcatenate(self):
        """
                Remove video from concatenation list.
            """
        try:
            self.concatenateModel.removeRow(self.curentIndexOfConcatenateList)
            del self.concatenateVideos[self.curentIndexOfConcatenateList]
            self.totalNrOfVideosConcat -= 1
            self.SortFilesIndexConcat()

        except:
            QMessageBox.about(self, "No video",
                              "No video to remove or not selected!     ")
            print("Error when removing video from list")

    def concatenate(self):
        """
                This function is used for concatenate multiple videos
                from the list.
            """
        if (self.curentIndex == -1 and self.totalIndex != -1):
            self.curentIndex = 0
        try:
            videosToConcatenate = []

            #I'm adding the main video
            videosToConcatenate.append(self.curentFiles[self.curentIndex])

            #Add the path to all videos to be concatenated
            for key in self.concatenateVideos:
                videosToConcatenate.append(self.concatenateVideos[key])

            #Save the current index befor concatenation because
            #during the concatenation the user could change the curent video
            #so it would affect curentFiles
            indexOfRootVideo = self.curentIndex
            print(videosToConcatenate)
            #Call concatenate function and save the url of the modified video
            self.curentFiles[indexOfRootVideo] = self.edit.concat(
                videosToConcatenate)

            #Update the media with the edited video
            self.mediaPlayer.setMedia(
                QMediaContent(
                    QUrl.fromLocalFile("../resources/videos/blackvideo.mp4")))
            self.mediaPlayer.setMedia(
                QMediaContent(
                    QUrl.fromLocalFile(self.curentFiles[indexOfRootVideo])))

            #Clear concatenate list view
            self.concatenateModel.removeRows(0,
                                             self.concatenateModel.rowCount())
            #Clear the dict that holds all the data for concatenation
            self.concatenateVideos.clear()
            #Reste the number of videos to be concatenated
            self.totalNrOfVideosConcat = -1

        except:
            print(
                "A problem occured during concatenation process.Check 'concatenate' function"
            )

    def SortFilesIndexConcat(self):
        """
                This function sort the curentFiles dictionary.
                When an element is deleted from curentFiles the function
                sort the index of the curentFiles ascending.
            """
        newIndex = 0
        newCurentFiles = {}
        #loop through the curentFiles and update the index
        for key in self.concatenateVideos:
            newCurentFiles[newIndex] = self.concatenateVideos[key]
            newIndex += 1

        #curentFiles files is updated to the new dictionary of files
        self.concatenateVideos = newCurentFiles.copy()

    """-------------------</Concatenate functions>--------------------------"""
    """-----------------------<Cut functions>--------------------------------"""

    def lockButtonChangeIconStart(self):
        """
                Function for changing the icon
                when the button is pressed.
            """
        if (self.lockButtonStart.isChecked() == True):
            self.lockButtonStart.setIcon(
                QIcon("../resources/icons/GUI_Icons/icons8-lock-80.png"))
        else:
            self.lockButtonStart.setIcon(
                QIcon("../resources/icons/GUI_Icons/icons8-padlock-80.png"))

    def lockButtonChangeIconFinish(self):
        """
                Function for changing the icon
                when the button is pressed.
            """

        if (self.lockButtonFinish.isChecked() == True):
            self.lockButtonFinish.setIcon(
                QIcon("../resources/icons/GUI_Icons/icons8-lock-80.png"))
        else:
            self.lockButtonFinish.setIcon(
                QIcon("../resources/icons/GUI_Icons/icons8-padlock-80.png"))

    def restCutButtons(self):
        """
                This function is used to reset
                all the value for cut section.
            """
        self.cutStart.setText("0:00:00")
        self.cutFinish.setText("0:00:00")
        if (self.lockButtonStart.isChecked() == True):
            self.lockButtonStart.toggle()
        if (self.lockButtonFinish.isChecked() == True):
            self.lockButtonFinish.toggle()
        self.lockButtonChangeIconStart()
        self.lockButtonChangeIconFinish()

    def cutFunction(self):
        """
                Function used for cut a video from
                'start' to 'finish' and replace in the
                'curentFiles'(dictionary that holds all opend video)
                the curent video path with the path provided by the
                'edit.cut'
            """
        try:

            #if both buttons are pressed the the function can be called
            if (self.lockButtonStart.isChecked() == True
                    and self.lockButtonFinish.isChecked() == True):
                try:
                    #save the current index before cut because user can change the 'curentIndex' during execution
                    indexOfRootVideo = self.curentIndex

                    #call the cut function and the result path is saved in currentFiles
                    self.curentFiles[indexOfRootVideo] = self.edit.cut(
                        [self.curentFiles[indexOfRootVideo]], [
                            self.cutStart.toPlainText(),
                            self.cutFinish.toPlainText()
                        ])
                    #Set the new video
                    self.mediaPlayer.setMedia(
                        QMediaContent(
                            QUrl.fromLocalFile(
                                "../resources/videos/blackvideo.mp4")))
                    self.mediaPlayer.setMedia(
                        QMediaContent(
                            QUrl.fromLocalFile(
                                self.curentFiles[indexOfRootVideo])))
                    #Reset all values for cut function

                except:
                    print("Problem in  at self.edit.cut or mediaPlayer")
        except:
            print("Problem in cutFunction function")

    """-----------------------</Cut functions>-------------------------------"""
    """-----------------------<Mirror functions>-----------------------------"""

    def mirrorVideo(self):
        if (self.curentIndex == -1 and self.totalIndex != -1):
            self.curentIndex = 0
        try:
            indexOfRootVideo = self.curentIndex
            result = ''
            result = self.edit.video_mirroring(
                [self.curentFiles[indexOfRootVideo]])
            if (result != ''):
                self.curentFiles[indexOfRootVideo] = result
                self.mediaPlayer.setMedia(
                    QMediaContent(
                        QUrl.fromLocalFile(
                            "../resources/videos/blackvideo.mp4")))
                self.mediaPlayer.setMedia(
                    QMediaContent(
                        QUrl.fromLocalFile(
                            self.curentFiles[indexOfRootVideo])))
        except:
            print("Problem in change mirror function")

    """-----------------------</Mirror functions>-----------------------------"""
    """-----------------------<Resoluton functions>-----------------------------"""

    def changeResolutionF(self):
        """
            Function for changing the video resolution.
        """
        if (self.curentIndex == -1 and self.totalIndex != -1):
            self.curentIndex = 0
        currentResolution = self.ResolutionsList.currentText(
        )[0:len(self.ResolutionsList.currentText()) - 1]
        try:
            indexOfRootVideo = self.curentIndex
            self.curentFiles[indexOfRootVideo] = self.edit.video_resize(
                [self.curentFiles[indexOfRootVideo]], currentResolution)
            self.mediaPlayer.setMedia(
                QMediaContent(
                    QUrl.fromLocalFile("../resources/videos/blackvideo.mp4")))
            self.mediaPlayer.setMedia(
                QMediaContent(
                    QUrl.fromLocalFile(self.curentFiles[indexOfRootVideo])))
        except:
            print("Problem in change resolution")

    def changeResolutionDisplay(self):
        """
            Usef for changing image in resolution section
            when the user select a resolution.
        """
        resolutionsIconList = [
            "../resources/icons/GUI_Icons/720.png",
            "../resources/icons/GUI_Icons/540.png",
            "../resources/icons/GUI_Icons/360.png",
            "../resources/icons/GUI_Icons/240.png",
            "../resources/icons/GUI_Icons/144.png"
        ]
        if (self.ResolutionsList.currentIndex() == 0):
            self.resolutionIcon.setPixmap(QPixmap(resolutionsIconList[0]))
        elif (self.ResolutionsList.currentIndex() == 1):
            self.resolutionIcon.setPixmap(QPixmap(resolutionsIconList[1]))
        elif (self.ResolutionsList.currentIndex() == 2):
            self.resolutionIcon.setPixmap(QPixmap(resolutionsIconList[2]))
        elif (self.ResolutionsList.currentIndex() == 3):
            self.resolutionIcon.setPixmap(QPixmap(resolutionsIconList[3]))
        elif (self.ResolutionsList.currentIndex() == 4):
            self.resolutionIcon.setPixmap(QPixmap(resolutionsIconList[4]))

    """-----------------------</Resoluton functions>--------------------------"""
    """-----------------------<Sound Repalce functions>--------------------------"""

    def openAudio(self):
        try:
            fileName = QFileDialog.getOpenFileName(self, "Open Audio")
            mimetypes.init()
            mimestart = mimetypes.guess_type(fileName[0])[0]

            if mimestart != None:
                mimestart = mimestart.split('/')[0]
                if mimestart == 'audio':
                    print("Audio file detected")
                    self.audioFile = fileName[0]
                    self.audioFileCheck.setIcon(
                        QIcon("../resources/icons/GUI_Icons/check.png"))
                else:
                    QMessageBox.about(
                        self, "Audio",
                        "This is not an audio file.Please load an audio file.."
                    )
                    print("Non audio file detected")
            else:
                QMessageBox.about(self, "Audio",
                                  "This file format is not accepted.")
                print("Not accepted file")
        except:
            QMessageBox.about(self, "Audio", "Changing sound track failed.")
            print("Problem in open audio function")

    def removeAudioFileFunction(self):
        self.audioFile = ''
        self.audioFileCheck.setIcon(
            QIcon("../resources/icons/GUI_Icons/ezgif-7-e04c11fb7018.png"))

    def changeAudioBackground(self):
        if (self.audioModeSelect.currentIndex() == 0):
            self.aduioModeImage.setPixmap(
                QPixmap("../resources/img/soundAdd.png"))
        elif (self.audioModeSelect.currentIndex() == 1):
            self.aduioModeImage.setPixmap(
                QPixmap("../resources/img/soundReplace.png"))

    def SoundReplaceFunction(self):
        if (self.curentIndex == -1 and self.totalIndex != -1):
            self.curentIndex = 0
        audioMode = self.audioModeSelect.currentText()
        print(audioMode)
        try:
            if (self.audioFile != ''):
                if (self.totalIndex != -1):
                    indexOfRootVideo = self.curentIndex
                    self.curentFiles[
                        indexOfRootVideo] = self.edit.soundReplace(
                            [self.curentFiles[indexOfRootVideo]],
                            self.audioFile, audioMode)
                    self.mediaPlayer.setMedia(
                        QMediaContent(
                            QUrl.fromLocalFile(
                                "../resources/videos/blackvideo.mp4")))
                    self.mediaPlayer.setMedia(
                        QMediaContent(
                            QUrl.fromLocalFile(
                                self.curentFiles[indexOfRootVideo])))
                else:

                    print("No video uploaded")
            else:

                print("No aduio file uploaded")
        except:
            print("Problem in SoundReplaceFunction")

    """-----------------------</Sound Repalce functions>-------------------------"""
    """-----------------------<GetFrame functions>-------------------------"""

    def GetFrameFunction(self):
        if (self.curentIndex == -1 and self.totalIndex != -1):
            self.curentIndex = 0
        try:
            if (self.totalIndex != -1 and self.frameTime != ''):
                indexOfRootVideo = self.curentIndex
                print(self.frameTime)
                self.frameFilePath = ''
                self.frameFilePath = self.edit.getFrame(
                    [self.curentFiles[indexOfRootVideo]], self.frameTime)
                self.extractedFrame.setPixmap(QPixmap(self.frameFilePath))
            else:
                print("No video uploaded or frameTime is empty")

        except:
            print("Problem in GetFrameFunction")

    def saveFrame(self):
        try:
            fileName = QFileDialog.getSaveFileName(self, 'Save File',
                                                   "img.jpg", '*.jpg')
            img = cv2.imread(self.frameFilePath)
            cv2.imwrite(fileName[0], img)
        except:
            QMessageBox.about(self, "Save image",
                              "Problem during saving image.")
            print("Problem during saving image")

    """-----------------------</GetFrame functions>-------------------------"""
    """-----------------------<Add Subtitles functions>-----------------------"""

    def loadSubtitlesFunction(self):
        try:
            fileName = QFileDialog.getOpenFileName(self, "Open Subtitles")
            if (fileName[0].split('.')[-1] == "srt"):
                self.subtitlesFile = fileName[0]
                self.subtitlesCheck.setIcon(
                    QIcon("../resources/icons/GUI_Icons/check.png"))
            else:
                QMessageBox.about(
                    self, "Subtitles",
                    "Couldn't load subtitles.Please use file with .srt extension."
                )

        except:
            print("Problem in load Subtitles function")

    def removeSubtitlesFunction(self):
        self.subtitlesFile = ''
        self.subtitlesCheck.setIcon(
            QIcon("../resources/icons/GUI_Icons/ezgif-7-e04c11fb7018.png"))

    def addSubtitlesFunction(self):
        if (self.curentIndex == -1 and self.totalIndex != -1):
            self.curentIndex = 0
        try:
            if (self.subtitlesFile != ''):
                if (self.totalIndex != -1):
                    indexOfRootVideo = self.curentIndex
                    self.curentFiles[
                        indexOfRootVideo] = self.edit.addSubtitles(
                            [self.curentFiles[indexOfRootVideo]],
                            self.subtitlesFile)
                    self.mediaPlayer.setMedia(
                        QMediaContent(
                            QUrl.fromLocalFile(
                                "../resources/videos/blackvideo.mp4")))
                    self.mediaPlayer.setMedia(
                        QMediaContent(
                            QUrl.fromLocalFile(
                                self.curentFiles[indexOfRootVideo])))
                else:
                    print("No video uploaded")
            else:
                print("No subtitles file uploaded")
        except:
            print("Problem in addSubtitlesFunction")

    """-----------------------</Add Subtitles functions>----------------------"""
    """-----------------------</VideoGrep functions>--------------------------"""

    def loadSubtitlesVideoGrep(self):
        try:
            fileName = QFileDialog.getOpenFileName(self, "Open Subtitles")
            if (fileName[0].split('.')[-1] == "srt"):
                self.subtitlesFileVG = fileName[0]
                self.subtitlesCheckVideoGrep.setIcon(
                    QIcon("../resources/icons/GUI_Icons/check.png"))
            else:
                QMessageBox.about(
                    self, "Subtitles",
                    "Couldn't load subtitles.Please use file with .srt extension."
                )

        except:
            print("Problem in load Subtitles function")

    def removeSubtitlesVideoGrep(self):
        self.subtitlesFileVG = ''
        self.subtitlesCheckVideoGrep.setIcon(
            QIcon("../resources/icons/GUI_Icons/ezgif-7-e04c11fb7018.png"))

    def videoGrepFunction(self):
        try:
            if (self.subtitlesFileVG != ''):
                if (self.totalIndex != -1):
                    indexOfRootVideo = self.curentIndex
                    vgMode = self.VGMODE.currentText()
                    if (vgMode == "Clasic"):
                        words = self.vglistSpeciale.toPlainText().split(',')
                    else:
                        words = [
                            self.firstWord.toPlainText(),
                            self.secondWord.toPlainText()
                        ]

                    self.curentFiles[
                        indexOfRootVideo] = self.edit.find_sequence(
                            [self.curentFiles[indexOfRootVideo]], words,
                            self.subtitlesFileVG, vgMode)
                    self.mediaPlayer.setMedia(
                        QMediaContent(
                            QUrl.fromLocalFile(
                                "../resources/videos/blackvideo.mp4")))
                    self.mediaPlayer.setMedia(
                        QMediaContent(
                            QUrl.fromLocalFile(
                                self.curentFiles[indexOfRootVideo])))
                else:
                    print("No video uploaded")
            else:
                print("No subtitles file uploaded")
        except:
            print("Problem in video Grep Function")

    """-----------------------</VideoGrep functions>--------------------------"""
    """--------------------------<File functions>----------------------------"""

    def openFile(self):
        """
            Function for opening a video file from
            taskbar.
        """
        fileName = QFileDialog.getOpenFileName(self, "OpenVideo")
        #Checks if fileName is a valid file
        if fileName[0] != '':
            #Set to mediaPlayer object the file(video)
            self.mediaPlayer.setMedia(
                QMediaContent(QUrl.fromLocalFile(fileName[0])))
            #Update total number of curent media opened
            self.totalIndex = self.totalIndex + 1
            self.curentIndex = self.totalIndex
            #Update the curentFiles dict which holds the path for the opened videos
            self.curentFiles[self.totalIndex] = fileName[0]

            if (self.totalIndex == 0):
                self.curentIndex = 0
            #Enable the play button after the video was set
            self.playButton.setEnabled(True)
            #Add media to the playlist
            self.playlist.addMedia(
                QMediaContent(QUrl.fromLocalFile(fileName[0])))

            #Add item to the list for avalabile videos for concatenate
            self.concatenateVideoList.addItem(fileName[0].split('/')[-1])

            #A new media was added so we sent a signal to updated List view
            self.model.layoutChanged.emit()

    """------------------------</File functions>----------------------------"""
    """------------------<Media player functions>---------------------------"""

    def playVideo(self):
        """"
            Function for controling the video Play/Pause
        """
        #Checks the state of the video.If the video is playing it will be paused.
        if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            #Pause the video
            self.mediaPlayer.pause()
        else:
            #If the video is paused it will be playing
            self.mediaPlayer.play()

    def mediaStateChange(self, ):
        """
            This function is changing the icon of the playButton.
            If the video is going from playing to "pause",the icon
            will change to pause icon.Otherwise the playingButton
            will change to "play" icon
        """

        if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            #Change playButton icon into "Pause icon"
            self.playButton.setIcon(
                QIcon("../resources/icons/GUI_Icons/002-pause.png"))
        else:
            #Change playButton icon into "Play icon"
            self.playButton.setIcon(
                QIcon("../resources/icons/GUI_Icons/play.png"))

    def convert(self, seconds):
        """
            Function for converting duration of the video(seconds)
            into Hour/minute/seconds and returning that format.
        """

        seconds = seconds % (24 * 3600)
        hour = seconds // 3600
        seconds %= 3600
        minutes = seconds // 60
        seconds %= 60

        return "%d:%02d:%02d" % (hour, minutes, seconds)

    def positionChange(self, position):
        """
            This function is activated when the video is
            changing the time.When that happens is updating
            the Time slider the new position and the time label.
        """
        self.videoTimeSlider.setValue(position)

        #Convert position into seconds
        duration = position / 1000
        self.frameTime = int(duration)
        self.videoTimeDisplay.setText(self.convert(duration))
        if (self.lockButtonStart.isChecked() == False):
            self.cutStart.setText(self.convert(duration))
        if (self.lockButtonFinish.isChecked() == False):
            self.cutFinish.setText(self.convert(duration))

    def durationChange(self, duration):
        """
            This function update the range of the time splider
            when the video position is changing.
        """
        self.videoTimeSlider.setRange(0, duration)

    def setPosition(self, position):
        """
            Sets the video time based on time slider.
            When the user is changing the time slider
            to a specific time,the video position is updated.
        """
        self.mediaPlayer.setPosition(position)

    def skipforwadFunction(self):
        self.mediaPlayer.setPosition(self.mediaPlayer.position() + 15000)

    def skipbackFunction(self):
        self.mediaPlayer.setPosition(self.mediaPlayer.position() - 15000)

    def rewindFunction(self):
        if (self.speed - 0.1 >= 0.1):
            self.speed -= 0.1
        self.mediaPlayer.setPlaybackRate(self.speed)
        self.speedLabel.setText(str(round(self.speed, 2)) + "x")

    def fastForwardFunction(self):
        if (self.speed + 0.1 <= 2.1):
            self.speed += 0.1
        self.mediaPlayer.setPlaybackRate(self.speed)
        self.speedLabel.setText(str(round(self.speed, 2)) + "x")

    def volumeControl(self, volume):
        """
            This function is used for changing the volume of the video
            and update the volume label.Also,is used to change the color
            of the slider based on the volume.If is < 50 is green, <50 && < 75 yellow
            and red for 74 >
        """
        self.volume.setValue(volume)
        self.mediaPlayer.setVolume(volume)
        self.volumeTextDisplay.setText(str(volume) + "%")
        if (volume <= 50):
            #green
            self.volume.setStyleSheet(styleSheet.volumeStageOne)
        elif (volume > 50 and volume <= 75):
            #yellow
            self.volume.setStyleSheet(styleSheet.volumeStageTwo)
        else:
            #red
            self.volume.setStyleSheet(styleSheet.volumeStageThree)

        #If the volume is zero the icon of the volume is changed
        if (volume == 0):
            self.volumeIcon.setIcon(
                QIcon("../resources/icons/GUI_Icons/mute.png"))
        else:
            self.volumeIcon.setIcon(
                QIcon("../resources/icons/GUI_Icons/speaker.png"))

    def playlist_selection_changed(self, ix):
        # We receive a QItemSelection from selectionChanged.
        i = ix.indexes()[0].row()
        self.playlist.setCurrentIndex(i)
        self.curentIndex = i
        self.speed = 1
        self.mediaPlayer.setPlaybackRate(self.speed)
        self.speedLabel.setText(str(self.speed) + "x")
        try:
            self.mediaPlayer.setMedia(
                QMediaContent(
                    QUrl.fromLocalFile(self.curentFiles[self.curentIndex])))
            #Reset cut text and icon
            self.restCutButtons()
        except:
            print(
                "Error in  playlist_selection_changed function.Media player couldn't be updated"
            )

    def playlist_position_changed(self, i):
        if i > -1:
            ix = self.model.index(i)
            print("playlist_position_changed")
            self.videoFiles.setCurrentIndex(ix)

    def RemoveVideo(self):
        """
            This function is connected to the remove button on Project files.
            It removes the file from List view and curentFiles dictionary.It also
            changethe mediaPlayer output based on what media was left.If the is media
            left the video output will be the firs mediaFile from the list.If the List view
            have no media left,the video output will be an video of 1 seconds with black background
            to clear the screen.

        """
        if (self.totalIndex != -1):
            if (self.curentIndex != -1):
                try:
                    #Delete media from index "curentIndex"
                    self.playlist.removeMedia(self.curentIndex)

                    #Delete video from concatenate ComboBox
                    self.concatenateVideoList.removeItem(self.curentIndex)

                    try:
                        #Delete the file name from index "curentIndex" from curentFiles
                        del self.curentFiles[self.curentIndex]
                        #Decrement the total number of videos opened
                        self.totalIndex = self.totalIndex - 1
                        #Update index of every video from curentFiles
                        self.SortFilesIndex()

                        if (self.totalIndex == -1):
                            try:
                                self.mediaPlayer.setMedia(
                                    QMediaContent(
                                        QUrl.fromLocalFile(
                                            "../resources/videos/blackvideo.mp4"
                                        )))
                                #Block the play button
                                self.playButton.setEnabled(False)
                                #Reset the time slider
                                self.videoTimeSlider.setValue(0)
                                #Reset the time slider
                                self.videoTimeSlider.setRange(0, 0)
                            except:
                                print("The black video couldn't be loaded")
                        else:
                            try:
                                self.mediaPlayer.setMedia(
                                    QMediaContent(
                                        QUrl.fromLocalFile(
                                            self.curentFiles[0])))
                            except:
                                print("The mediaPlayer couldn't be updated")

                    except:
                        print("The value from index " + str(self.curentIndex) +
                              " could not be deleted form curentFiles")
                except:
                    print("Media cannot be deleted from playlist")
        else:
            #Block the play button
            self.playButton.setEnabled(False)
            #Reset the time slider
            self.videoTimeSlider.setValue(0)
            #Reset the time slider
            self.videoTimeSlider.setRange(0, 0)

    def saveVideoFunction(self):
        try:
            import shutil
            extension = self.curentFiles[self.curentIndex].split('.')[-1]
            extension = "*." + extension
            fileName = QFileDialog.getSaveFileName(self, 'Save video',
                                                   "video_name", extension)
            shutil.move(self.curentFiles[self.curentIndex], fileName[0])
            print("Save video Done")
        except:
            print("Error during the video saving")

    def SortFilesIndex(self):
        """
            This function sort the curentFiles dictionary.
            When an element is deleted from curentFiles the function
            sort the index of the curentFiles ascending.
        """
        newIndex = 0
        newCurentFiles = {}
        #loop through the curentFiles and update the index
        for key in self.curentFiles:
            newCurentFiles[newIndex] = self.curentFiles[key]
            newIndex += 1

        #curentFiles files is updated to the new dictionary of files
        self.curentFiles = newCurentFiles.copy()

    def soundShortcutKey(self):
        self.editMenu.setCurrentIndex(4)

    def getFrameShortcutKey(self):
        self.editMenu.setCurrentIndex(5)

    def cutShortcutKey(self):
        self.editMenu.setCurrentIndex(1)

    def concatShortcutKey(self):
        self.editMenu.setCurrentIndex(0)

    def mirrorShortcutKey(self):
        self.editMenu.setCurrentIndex(3)
        """------------------</Media player functions>-----------------------"""
        """----------------------<Thread functions>--------------------------"""

    """---<Concatenate>---"""

    def concatenateThreadFunction(self):
        #If is true that means that no thread is running and can start a thread
        if (self.threadmanager == True):
            try:
                #If is false that means that a thread is already executing
                self.threadmanager = False
                worker = Worker(self.concatenate)
                #When the thread is done threadmanager will be True
                worker.signals.finished.connect(self.ReleaseThread)
                self.pool.start(worker)
            except:
                print("Problem with concatenate thread")
        else:
            print("A thread is already running")

    """---</Concatenate>---"""
    """---<Cut>---"""

    def cutThreadFunction(self):
        if (self.threadmanager == True):
            try:
                self.threadmanager = False
                worker = Worker(self.cutFunction)
                worker.signals.finished.connect(self.restCutButtons)
                worker.signals.finished.connect(self.ReleaseThread)
                self.pool.start(worker)
            except:
                print("Problem with cut thread")
        else:
            print("A thread is already running")

    """---</Cut>---"""
    """---<Resolution>---"""

    def changeResolutionThread(self):
        if (self.threadmanager == True):
            try:
                self.threadmanager = False
                worker = Worker(self.changeResolutionF)
                worker.signals.finished.connect(self.ReleaseThread)
                self.pool.start(worker)
            except:
                print("Problem with resolution thread")
        else:
            print("A thread is already running")

    """---</Resolution>---"""
    """---<Mirror>---"""

    def mirrorThread(self):
        if (self.threadmanager == True):
            try:
                self.threadmanager = False
                worker = Worker(self.mirrorVideo)
                worker.signals.finished.connect(self.ReleaseThread)
                self.pool.start(worker)
            except:
                print("Problem with mirror thread")
        else:
            print("A thread is already running")

    """---</Mirror>---"""
    """---<SoundReplace>---"""

    def SoundReplaceThread(self):
        if (self.threadmanager == True):
            try:
                self.threadmanager = False
                worker = Worker(self.SoundReplaceFunction)
                worker.signals.finished.connect(self.ReleaseThread)
                worker.signals.finished.connect(self.removeAudioFileFunction)
                self.pool.start(worker)
            except:
                print("Problem with SoundReplace thread")
        else:
            print("A thread is already running")

    """---</SoundReplace>---"""

    def ReleaseThread(self):
        self.threadmanager = True
        print(self.curentFiles)

    """---<GetFrame>---"""

    def GetFrameThread(self):
        if (self.threadmanager == True):
            try:
                self.threadmanager = False
                worker = Worker(self.GetFrameFunction)
                worker.signals.finished.connect(self.ReleaseThread)
                self.pool.start(worker)
            except:
                print("Problem with getFrame thread")
        else:
            print("A thread is already running")

    """---</GetFrame>---"""
    """---<AddSubtitles>---"""

    def addSubtitlesThread(self):
        if (self.threadmanager == True):
            try:
                self.threadmanager = False
                worker = Worker(self.addSubtitlesFunction)
                worker.signals.finished.connect(self.ReleaseThread)
                worker.signals.finished.connect(self.removeSubtitlesFunction)
                self.pool.start(worker)
            except:
                print("Problem with add subtitles thread")
        else:
            print("A thread is already running")

    """---</AddSubtitles>--"""
    """---</VideoGrep>--"""

    def videoGrepThread(self):
        if (self.threadmanager == True):
            try:
                self.threadmanager = False
                worker = Worker(self.videoGrepFunction)
                worker.signals.finished.connect(self.ReleaseThread)
                worker.signals.finished.connect(self.removeSubtitlesVideoGrep)
                self.pool.start(worker)
            except:
                print("Problem with VideoGrep thread")
        else:
            print("A thread is already running")

    """---</VideoGrep>--"""
    """---------------------</Thread functions>--------------------------"""

    def deleteAllTemp(self):
        import os, shutil
        folder = ProjectFolders.tmpDir
        for filename in os.listdir(folder):
            file_path = os.path.join(folder, filename)
            try:
                if os.path.isfile(file_path) or os.path.islink(file_path):
                    os.unlink(file_path)
                elif os.path.isdir(file_path):
                    shutil.rmtree(file_path)
            except Exception as e:
                print('Failed to delete %s. Reason: %s' % (file_path, e))

    def closeEvent(self, event):
        """
                Popup a dialog when the user is trying to close the main app.
            """
        reply = QMessageBox.question(
            self, 'Window Close', 'Are you sure you want to close the window?',
            QMessageBox.Yes | QMessageBox.No, QMessageBox.No)

        if reply == QMessageBox.Yes:
            if (self.threadmanager == True):
                try:
                    self.threadmanager = False
                    worker = Worker(self.deleteAllTemp)
                    worker.signals.finished.connect(self.ReleaseThread)
                    self.pool.start(worker)
                except:
                    print("Problem with getFrame thread")
            else:
                print("A thread is already running")

            event.accept()
            print('Window closed')
        else:
            event.ignore()
Esempio n. 14
0
class Ui_MainWindow(object):

    # for stylizing progress bar
    progress_styleSheet = """
            QSlider::groove:horizontal {
                background: dark;
                height: 40px;
            }

            QSlider::sub-page:horizontal {
                background: qlineargradient(x1: 0, y1: 0,    x2: 0, y2: 1,
                    stop: 0 #07406a, stop: 1 #696969);
                background: qlineargradient(x1: 0, y1: 0.2, x2: 1, y2: 1,
                    stop: 0 #696969, stop: 1 #07406a);
                height: 40px;
            }

            QSlider::add-page:horizontal {
                background: #696969;
                height: 40px;
            }

            QSlider::handle:horizontal {
                background: #aaa;
                border: 0px;
                width: 5px;
                margin-top: 0px;
                margin-bottom: 0px;
                border-radius: 0px;
            }
        """

    def setupUi(self, MainWindow):
        MainWindow.setObjectName("Music Player")
        MainWindow.resize(640, 480)
        MainWindow.setAcceptDrops(True)

        self.playlist = QMediaPlaylist()
        self.player = QMediaPlayer()
        self.player.setPlaylist(self.playlist)
        self.player.setVolume(20)
        self.player.setPlaybackRate(jdata['playback'])
        self.playlist.setPlaybackMode(jdata['curr_playstyle'])

        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
        self.gridLayout.setObjectName("gridLayout")
        self.gridLayout_2 = QtWidgets.QGridLayout()
        self.gridLayout_2.setObjectName("gridLayout_2")
        self.progress = QtWidgets.QSlider(self.centralwidget)
        self.progress.setOrientation(QtCore.Qt.Horizontal)
        self.progress.setStyleSheet(self.progress_styleSheet)
        self.progress.setObjectName("progress")
        self.progress.setSingleStep(1)
        self.progress.setPageStep(1)
        self.gridLayout_2.addWidget(self.progress, 1, 0, 1, 3)
        self.stop_but = QtWidgets.QPushButton(self.centralwidget)
        self.stop_but.setObjectName("stop_but")
        self.gridLayout_2.addWidget(self.stop_but, 0, 2, 1, 1)
        self.backward_b = QtWidgets.QPushButton(self.centralwidget)
        self.backward_b.setObjectName("backward")
        self.gridLayout_2.addWidget(self.backward_b, 0, 0, 1, 1)
        self.cont_pau = QtWidgets.QPushButton(self.centralwidget)
        self.cont_pau.setObjectName("cont_pau")
        self.gridLayout_2.addWidget(self.cont_pau, 0, 1, 1, 1)
        self.playstyle = QtWidgets.QPushButton(self.centralwidget)
        self.playstyle.setObjectName("playstyle")
        self.playstyle.setText("Loop")
        self.gridLayout_2.addWidget(self.playstyle, 0, 4, 1, 1)
        self.forward_b = QtWidgets.QPushButton(self.centralwidget)
        self.forward_b.setObjectName("forward")
        self.gridLayout_2.addWidget(self.forward_b, 0, 3, 1, 1)
        self.time_remain = QtWidgets.QLabel(self.centralwidget)
        self.time_remain.setObjectName("time_remain")
        self.gridLayout_2.addWidget(self.time_remain, 1, 3, 1, 1)
        self.verticalLayout_2 = QtWidgets.QVBoxLayout()
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.volume = QtWidgets.QLabel(self.centralwidget)
        self.volume.setAlignment(QtCore.Qt.AlignCenter)
        self.volume.setObjectName("volume")
        self.verticalLayout_2.addWidget(self.volume)
        self.volume_ctrl = QtWidgets.QSlider(self.centralwidget)
        self.volume_ctrl.setOrientation(QtCore.Qt.Horizontal)
        self.volume_ctrl.setObjectName("volume_ctrl")
        self.volume_ctrl.setValue(jdata["volume"])
        self.volume_ctrl.setRange(-1, 101)
        self.volume_ctrl.setSingleStep(1)
        self.volume_ctrl.setPageStep(1)
        self.verticalLayout_2.addWidget(self.volume_ctrl)
        self.gridLayout_2.addLayout(self.verticalLayout_2, 1, 4, 1, 1)
        self.gridLayout.addLayout(self.gridLayout_2, 1, 0, 1, 1)
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setObjectName("verticalLayout")
        self.playing = QtWidgets.QLabel(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(12)
        self.playing.setFont(font)
        self.playing.setAlignment(QtCore.Qt.AlignCenter)
        self.playing.setObjectName("playing")
        self.playing.setScaledContents(True)
        self.playing.setWordWrap(True)
        self.verticalLayout.addWidget(self.playing)
        self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1)
        self.verticalLayout_3 = QtWidgets.QVBoxLayout()
        self.verticalLayout_3.setObjectName("verticalLayout_3")
        self.song_list = ListWidget(self.centralwidget)
        self.song_list.setMinimumSize(QtCore.QSize(183, 0))
        self.song_list.setAcceptDrops(True)
        self.song_list.setDragDropMode(
            QtWidgets.QAbstractItemView.InternalMove)
        self.song_list.setObjectName("song_list")
        self.song_list.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.song_list.setWordWrap(True)
        self.verticalLayout_3.addWidget(self.song_list)
        self.gridLayout.addLayout(self.verticalLayout_3, 0, 1, 2, 1)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 632, 25))
        self.menubar.setObjectName("menubar")
        self.menuFile = QtWidgets.QMenu(self.menubar)
        self.menuFile.setObjectName("menuFile")
        self.menuAbout = QtWidgets.QMenu(self.menubar)
        self.menuAbout.setObjectName("menuAbout")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.actionOpen = QtWidgets.QAction(MainWindow)
        self.actionOpen.setObjectName("actionOpen")
        self.actionLisence_Information = QtWidgets.QAction(MainWindow)
        self.actionLisence_Information.setObjectName(
            "actionLisence_Information")
        self.menuFile.addAction(self.actionOpen)
        self.menuAbout.addAction(self.actionLisence_Information)
        self.menubar.addAction(self.menuFile.menuAction())
        self.menubar.addAction(self.menuAbout.menuAction())
        spacerItem = QtWidgets.QSpacerItem(40, 20,
                                           QtWidgets.QSizePolicy.Expanding,
                                           QtWidgets.QSizePolicy.Minimum)
        self.gridLayout_2.addItem(spacerItem, 0, 5, 1, 1)
        self.gridLayout.addLayout(self.gridLayout_2, 1, 0, 1, 1)

        self.info = QMessageBox()
        self.info.setWindowTitle("License Information")
        self.info.setText("MIT License\n\nCopyright (c) 2021 Marganotvke")

        self.err = QMessageBox()
        self.err.setWindowTitle("Error Loading File")
        self.err.setText("Error Loading File!\n\nPlease check file type!")

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

        self.actionOpen.triggered.connect(lambda: self.open_trig())
        self.actionLisence_Information.triggered.connect(
            lambda: self.info.exec_())
        self.cont_pau.clicked.connect(lambda: self.cur_playing())
        self.volume_ctrl.sliderMoved.connect(lambda: self.change_vol())
        self.stop_but.clicked.connect(lambda: self.stop_playing())
        self.forward_b.clicked.connect(lambda: self.forward())
        self.backward_b.clicked.connect(lambda: self.backward())
        self.playstyle.clicked.connect(lambda: self.playlist_style())
        self.song_list.dropped.connect(lambda e: self.start_play(e))
        self.song_list.itemDoubleClicked.connect(
            lambda: self.jump_start(self.song_list.currentRow()))
        self.song_list.model().rowsAboutToBeMoved.connect(
            lambda e, f, g, h, i: self.re_arr(f, i))
        self.song_list.dele.connect(lambda e: self.delete_q(e))
        self.player.metaDataAvailableChanged.connect(lambda: self.set_meta())
        self.player.stateChanged.connect(lambda e: self.set_state(e))
        self.player.positionChanged.connect(lambda e: self.set_pos(e))
        self.progress.sliderMoved.connect(lambda e: self.skip_to(e))

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "Music Player"))
        self.stop_but.setText(_translate("MainWindow", "Stop"))
        self.backward_b.setText(_translate("MainWindow", "Backward"))
        self.cont_pau.setText(_translate("MainWindow", "Load Song"))
        self.playstyle.setText(
            _translate("MainWindow", f"{playstyles[jdata['curr_playstyle']]}"))
        self.forward_b.setText(_translate("MainWindow", "Forward"))
        self.time_remain.setText(_translate("MainWindow", "--/--"))
        self.volume.setText(
            _translate("MainWindow", f"Volume: {jdata['volume']}%"))
        self.playing.setText(
            _translate("MainWindow", "Currently playing: None"))
        self.menuFile.setTitle(_translate("MainWindow", "File"))
        self.menuAbout.setTitle(_translate("MainWindow", "About"))
        self.actionOpen.setText(_translate("MainWindow", "Open"))
        self.actionLisence_Information.setText(
            _translate("MainWindow", "License Information"))
        self.actionOpen.setShortcut(_translate("MainWindow", "Ctrl+O"))

    def re_arr(self, e=None, f=None):
        f -= 1 if self.song_list.count() == f else 0
        media = self.playlist.media(e)
        self.playlist.removeMedia(e)
        self.playlist.insertMedia(f, media)

    def jump_start(self, index=None):
        if self.playlist.currentIndex() != index:
            self.playlist.setCurrentIndex(index)
            self.player.stop()
            self.start_play()

    def start_play(self, url=None):
        if url:
            print("Emitted from drop event:", url)
            threading.Thread(target=self.open_file(url)).start()
        self.player.play()

    def stop_playing(self):
        self.player.stop()

    def cur_playing(self):
        if self.playlist.isEmpty():
            self.open_trig()
        else:
            if self.player.state() == 1:
                self.player.pause()
                self.cont_pau.setText("Play")
            else:
                self.player.play()
                self.cont_pau.setText("Pause")

    def forward(self):
        self.playlist.next()

    def backward(self):
        if round(self.player.position() / 1000) >= 5:
            self.player.setPosition(0)
            if not self.player.state():
                self.start_play()
        else:
            self.playlist.previous()

    def open_trig(self):
        filename = QFileDialog.getOpenFileName(
            None,
            'Open File',
            filter=
            "MPEG-2 (*.mp3);;WAVE Audio (*.wav,*.aif,*.aiff);;MPEG-1/DD/ACC (*.aac);;MIDI (*.mid,*.midi);;Windows Media Audio (*.wma);;Xiph.Org OGG Vorbis (*.ogg);;NeXT SouND (*.snd);;FLAC (*.flac);;All files(*)"
        )
        threading.Thread(target=self.open_file(filename[0])).start()

    def open_file(self, file_url):
        url = QUrl.fromLocalFile(file_url)
        avai = ('.mp3', '.wav', 'ogg', '.aac', '.aif', '.aiff', '.mid',
                '.midi', '.wma', '.snd', '.flac')
        if url.url() != '' and not url.fileName().lower().endswith(avai):
            self.err.exec_()
        elif url.fileName().lower().endswith(avai):
            song_name = url.fileName().split(".")[0]
            self.song_list.addItem(song_name)
            if self.playlist.isEmpty():
                self.playlist.addMedia(QMediaContent(url))
                self.cont_pau.setText("Pause")
                self.start_play()
            else:
                self.playlist.addMedia(QMediaContent(url))

    def delete_q(self, i):
        self.playlist.removeMedia(i)
        self.song_list.takeItem(i)

    def playlist_style(self):
        i = self.playlist.playbackMode()
        i += 1
        i *= (i < 5)
        self.playlist.setPlaybackMode(i)
        self.playstyle.setText(f"{playstyles[self.playlist.playbackMode()]}")
        jdata["curr_playstyle"] = self.playlist.playbackMode()
        print(playstyles[self.playlist.playbackMode()])

    def change_vol(self):
        vol = self.volume_ctrl.value()
        self.player.setVolume(vol)
        self.volume.setText(f"Volume: {vol}%")
        jdata["volume"] = vol

    def set_pos(self, e):
        if self.player.isMetaDataAvailable():
            t = self.player.duration()
            dt = datetime.datetime.fromtimestamp(
                round(self.player.duration() / 1000)).strftime('%M:%S')
            dt_ct = datetime.datetime.fromtimestamp(round(
                e / 1000)).strftime('%M:%S')
            self.thread = threading.Thread(
                target=self.progress.setValue(round((e / t) * 100)))
            self.thread.start()
            self.time_remain.setText(f"{dt_ct}/{dt}")

    def set_meta(self):
        if self.player.isMetaDataAvailable():
            self.duraiton = self.player.duration()
            self.progress.setRange(0, 100)
            self.playing.setText(
                f"Currently playing: {self.player.currentMedia().canonicalUrl().fileName().split('.')[0]}"
            )
        else:
            if self.playlist.isEmpty():
                self.cont_pau.setText("Load Song")
                self.time_remain.setText("--/--")

    def set_state(self, e):
        if not e:
            self.playing.setText("Currently playing: None")
            if self.playlist.isEmpty():
                self.cont_pau.setText("Load Song")
            else:
                self.cont_pau.setText("Play")
        else:
            if self.player.isMetaDataAvailable():
                self.playing.setText(
                    f"Currently playing: {self.player.currentMedia().canonicalUrl().fileName().split('.')[0]}"
                )

    def skip_to(self, e):
        self.player.setPosition(round((e / 100) * self.player.duration()))
Esempio n. 15
0
class Win(QWidget):
    """
    重写 QWidget 类,自定义创建一个播放器窗体
    """
    def __init__(self):
        """
        构造方法:类的初始化
        """
        # 初始化父类
        super().__init__()

        # 定义类属性
        self.moveMode = False  # 是否开启窗口移动模式
        self.windowPoint = QPoint(0, 0)  # 记录窗口移动时位置
        self.startPoint = QPoint(0, 0)  # 记录窗口移动前位置

        # 设定窗体基本属性(标题、尺寸、图标等)
        self.setWindowTitle("简易MP3音乐播放器")  # 窗口标题
        self.setObjectName("app")  # 对象名称
        self.resize(260, 348)  # 窗口尺寸
        self.setFixedSize(self.width(), self.height())  # 禁止拉伸窗口大小
        self.setWindowFlags(Qt.FramelessWindowHint)  # 窗口无边框
        self.setAutoFillBackground(True)  # 允许背景填充
        palette = QPalette()
        palette.setBrush(QPalette.Background, QBrush(QPixmap('./img/bg.jpg')))
        self.setPalette(palette)  # 填充背景图片

        # 定义窗体组件(定义按钮、标签、列表框、滑块条等)及其基本属性
        self.create_widgets()

        # 创建播放器和播放列表
        self.media = QMediaPlayer()
        self.list = QMediaPlaylist()
        self.media.setPlaylist(self.list)

        # 设置窗体组件的样式和布局(颜色、图片、背景、位置、字体等)
        self.set_style()

        # 绑定事件(信号槽)
        self.bind_slots()

        # 其他初始化操作
        self.list.setPlaybackMode(QMediaPlaylist.Loop)  # 循环播放
        self.media.setVolume(100)  # 设置默认音量
        self.slider_volume.setRange(0, 100)
        self.slider_volume.setValue(100)
        self.slider_pos.setRange(0, 0)  # 设置默认进度
        self.slider_pos.setValue(0)
        self.label_title.setText("简易MP3音乐播放器")  # 设置默认标题

        # 加载已保存的播放列表
        self.load_playlist()

    def create_widgets(self):
        """
        定义窗体组件(定义按钮、标签、列表框、滑块条等)及其基本属性
        :return:
        """
        self.frame_player = QFrame(self)  # 播放器布局框
        self.frame_player.setObjectName("frame_player")
        self.frame_player.setGeometry(0, 0, 260, 120)
        self.frame_list = QFrame(self)  # 播放列表布局框
        self.frame_list.setObjectName("frame_list")
        self.frame_list.setGeometry(0, 120, 260, 228)
        self.btn_play = QPushButton(self.frame_player)  # 播放按钮
        self.btn_play.setObjectName("btn_play")
        self.btn_play.setGeometry(8, 78, 32, 32)
        self.btn_pause = QPushButton(self.frame_player)  # 暂停按钮
        self.btn_pause.setObjectName("btn_pause")
        self.btn_pause.setGeometry(8, 78, 32, 32)
        self.btn_pause.setVisible(False)
        self.btn_stop = QPushButton(self.frame_player)  # 停止按钮
        self.btn_stop.setObjectName("btn_stop")
        self.btn_stop.setGeometry(40, 78, 32, 32)
        self.btn_previous = QPushButton(self.frame_player)  # 上一曲按钮
        self.btn_previous.setObjectName("btn_previous")
        self.btn_previous.setGeometry(72, 78, 32, 32)
        self.btn_next = QPushButton(self.frame_player)  # 下一曲按钮
        self.btn_next.setObjectName("btn_next")
        self.btn_next.setGeometry(104, 78, 32, 32)
        self.btn_mute = QPushButton(self.frame_player)  # 静音按钮
        self.btn_mute.setObjectName("btn_mute")
        self.btn_mute.setGeometry(144, 90, 20, 20)
        self.label_title = QLabel(self.frame_player)  # 歌曲标题标签
        self.label_title.setObjectName("label_title")
        self.label_title.setGeometry(8, 38, 244, 32)
        self.label_time = QLabel("00:00", self.frame_player)  # 时间标签
        self.label_time.setObjectName("label_time")
        self.label_time.setGeometry(220, 74, 30, 20)
        self.slider_volume = QSlider(self.frame_player)  # 音量滑块条
        self.slider_volume.setObjectName("slider_volume")
        self.slider_volume.setGeometry(172, 90, 80, 20)
        self.slider_volume.setOrientation(Qt.Horizontal)
        self.slider_pos = QSlider(self.frame_player)  # 播放进度条
        self.slider_pos.setObjectName("slider_pos")
        self.slider_pos.setGeometry(0, 114, 260, 6)
        self.slider_pos.setOrientation(Qt.Horizontal)
        self.btn_add = QPushButton(self.frame_list)  # 添加文件按钮
        self.btn_add.setObjectName("btn_add")
        self.btn_add.setGeometry(8, 196, 24, 24)
        self.btn_remove = QPushButton(self.frame_list)  # 移除文件按钮
        self.btn_remove.setObjectName("btn_remove")
        self.btn_remove.setGeometry(40, 196, 24, 24)
        self.play_list = QListWidget(self.frame_list)  # 播放列表
        self.play_list.setObjectName("play_list")
        self.play_list.setGeometry(8, 8, 244, 180)
        self.label_version = QLabel(self.frame_list)  # 软件版本说明标签
        self.label_version.setObjectName("label_version")
        self.label_version.setGeometry(72, 196, 172, 24)
        self.label_version.setText("Version: 1.0 | By: yowfung")
        self.btn_close = QPushButton(self)  # 关闭窗口按钮
        self.btn_close.setObjectName("btn_close")
        self.btn_close.setGeometry(236, 0, 24, 24)

    def set_style(self):
        """
        设置窗体组件的样式和布局(颜色、图片、背景、位置、字体等)
        :return:
        """
        # 通过各种选择器的方式,并结合qss样式表对组件进行样式美化和布局
        self.setStyleSheet(
            # 窗体全局样式
            "QWidget{font-family:'微软雅黑'}"

            # 播放器框和播放列表框的样式
            "QFrame#frame_player{background-color: rgba(0,0,0,0)}"
            "QFrame#frame_list{background-color:rgba(200, 240, 255, 0.3)}"

            # 歌曲标题标签、时间标签、版本说明标签的样式
            "QLabel#label_title{font-size:18px; color: white}"
            "QLabel#label_time{color: #bbb; font-size:11px; }"
            "QLabel#label_version{color:#444;font-size:12px}"

            # 音量滑块条、进度条样式
            "QSlider#slider_volume::groove:horizontal{height:4px;background:rgb(167,176,255)}"
            "QSlider#slider_volume::handle:horizontal{width:4px;background:rgb(180, 220, 255);margin:-2px 0px;}"
            "QSlider#slider_pos::groove:horizontal{height:2px;background:rgb(50,80,120)}"
            "QSlider#slider_pos::handle:horizontal{width:6px;background:rgb(240, 50, 60);margin:-2px 0;border-radius:1px}"

            # 按钮样式
            "QPushButton{border: none;}"
            "QPushButton:pressed{margin: 1px}"
            "QPushButton#btn_play:pressed, QPushButton#btn_pause:pressed, QPushButton#btn_stop:pressed{margin:2px}"
            "QPushButton#btn_play{image: url('./img/icons/play.png')}"
            "QPushButton#btn_pause{image: url('./img/icons/pause.png')}"
            "QPushButton#btn_stop{image: url('./img/icons/stop.png')}"
            "QPushButton#btn_previous{image: url('./img/icons/previous.png')}"
            "QPushButton#btn_next{image: url('./img/icons/next.png')}"
            "QPushButton#btn_mute{image: url('./img/icons/volume-normal.png')}"
            "QPushButton#btn_add{image: url('./img/icons/add.png')}"
            "QPushButton#btn_remove{image: url('./img/icons/remove.png')}"
            "QPushButton#btn_close{image: url('./img/icons/close.png')}"

            # 播放列表框样式
            "QListWidget{border:1px solid #ccc; font-size:12px;color:#444;background-color:rgba(255,255,255,0.6)}"
            "QListWidget::Item:hover{background-color:rgba(120,120,120,0.15);color:black}"
            "QListWidget::Item:selected{background-color:rgba(120,120,150,0.3);color:#208}"

            # 垂直滚动条样式
            "QScrollBar:vertical{background-color:#eee;width:6px}"
            "QScrollBar::handle:vertical{background-color:#ccc;min-height:16px}"
            "QScrollBar::handle::vertical:hover {background-color:#bbb;}"
            "QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {height: 0}"

            # 水平滚动条样式
            "QScrollBar:horizontal{background-color: #eee;height: 6px;}"
            "QScrollBar::handle:horizontal {background-color:#ccc;min-width:16px;}"
            "QScrollBar::handle::horizontal:hover {background-color:#bbb;}"
            "QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal {width:0;}"
        )

    def bind_slots(self):
        """
        绑定信号槽
        :return:
        """
        self.btn_close.clicked.connect(self.close)  # 关闭按钮:被单击
        self.btn_play.clicked.connect(self.con_btn_clicked_event)  # 播放按钮:被单击
        self.btn_pause.clicked.connect(self.con_btn_clicked_event)  # 暂停按钮:被单击
        self.btn_stop.clicked.connect(self.con_btn_clicked_event)  # 停止按钮:被单击
        self.btn_next.clicked.connect(self.con_btn_clicked_event)  # 下一曲按钮:被单击
        self.btn_previous.clicked.connect(
            self.con_btn_clicked_event)  # 上一曲按钮:被单击
        self.btn_mute.clicked.connect(self.con_btn_clicked_event)  # 静音按钮:被单击
        self.btn_add.clicked.connect(self.con_btn_clicked_event)  # 添加文件按钮:被单击
        self.btn_remove.clicked.connect(
            self.con_btn_clicked_event)  # 移除项目按钮:被单击
        self.slider_volume.valueChanged.connect(
            self.vol_changed_event)  # 音量滑块条:值被改变
        self.slider_pos.sliderReleased.connect(
            self.progress_changed_event)  # 进度滑块条:值被改变
        self.play_list.doubleClicked.connect(
            self.list_double_clicked_event)  # 播放列表:被双击

        self.media.positionChanged.connect(
            self.pos_changed_event)  # 媒体播放器:播放位置被改变
        self.media.durationChanged.connect(
            self.duration_changed_event)  # 媒体播放器:时间区间被改变
        self.media.stateChanged.connect(
            self.state_changed_event)  # 媒体播放器:播放状态被改变
        self.list.currentIndexChanged.connect(
            self.cur_changed_event)  # 媒体播放列表:当前播放项被改变

    def closeEvent(self, e):
        """
        重写 closeEvent:关闭程序前停止播放并保存记录
        :param e:
        :return:
        """
        self.save_playlist()
        self.media.stop()

    def mousePressEvent(self, e):
        """
        重写 mousePressEvent:鼠标在顶部按下时进入“移动窗口模式”
        :param e:
        :return:
        """
        if e.button() == Qt.LeftButton \
                and 2 <= e.pos().x() <= self.width() - 2 \
                and 2 <= e.pos().y() <= 28:
            self.moveMode = True
            self.startPoint = e.globalPos()
            self.windowPoint = self.geometry().topLeft()

    def mouseMoveEvent(self, e):
        """
        重写 mouseMoveEvent:在“移动窗口模式”中进行窗口位置移动
        :param e:
        :return:
        """
        if e.buttons() & Qt.LeftButton and self.moveMode:
            re_point = e.globalPos() - self.startPoint
            self.move(self.windowPoint + re_point)

    def mouseReleaseEvent(self, e):
        """
        重写 mouseReleaseEvent:鼠标释放时离开“移动窗口模式”
        :param e:
        :return:
        """
        self.moveMode = False

    def con_btn_clicked_event(self):
        """
        播放/暂停/上一曲/下一曲/停止/静音/添加文件/移除文件等控制按钮鼠标被单击事件
        :return:
        """
        # 取出事件对象(信号发送者)
        sender = self.sender()

        # 播放
        if sender.objectName() == "btn_play":
            self.media.play()

        # 暂停
        if sender.objectName() == "btn_pause":
            self.media.pause()

        # 停止
        if sender.objectName() == "btn_stop":
            self.media.stop()

        # 上一曲
        if sender.objectName() == "btn_previous":
            self.previous()

        # 下一曲
        if sender.objectName() == "btn_next":
            self.next()

        # 静音
        if sender.objectName() == "btn_mute":
            # 如果音量为0,或者原先未静音,则设置成静音
            status = self.slider_volume.value() is 0 or not self.media.isMuted(
            )
            self.media.setMuted(status)
            img = "./img/icons/volume-mute.png" if status else "./img/icons/volume-normal.png"
            self.btn_mute.setStyleSheet("QPushButton#btn_mute{image:url('" +
                                        img + "')}")  # 静音按钮的图标样式

        # 添加文件到列表
        if sender.objectName() == "btn_add":
            self.add_to_list()

        # 从列表移除
        if sender.objectName() == "btn_remove":
            self.remove_from_list()

    def list_double_clicked_event(self):
        """
        播放列表框中的项目被双击事件
        :return:
        """
        # 播放选中的歌曲
        index = self.play_list.currentRow()
        if 0 <= index < self.play_list.count():
            self.list.setCurrentIndex(index)
            self.media.play()

    def vol_changed_event(self, val):
        """
        音量滑块条值被改变事件:调节音量
        :return:
        """
        self.media.setVolume(val)
        self.media.setMuted(val is 0)  # 音量为0时设置为静音
        img = "./img/icons/volume-mute.png" if val is 0 else "./img/icons/volume-normal.png"
        self.btn_mute.setStyleSheet("QPushButton#btn_mute{image:url('" + img +
                                    "')}")  # 静音按钮图标样式

    def state_changed_event(self, state):
        """
        播放状态被改变事件:变化播放按钮和暂停按钮的可视状态
        :param state:
        :return:
        """
        self.btn_play.setVisible(
            True if state != 1 else False)  # 只要不是“正在播放”,就显示播放按钮
        self.btn_pause.setVisible(
            True if state == 1 else False)  # 只要“正在播放”,就显示暂停按钮

    def progress_changed_event(self):
        """
        播放进度条值被改变事件:定位播放
        :return:
        """
        # 将播放位置设置为进度条调节的位置
        self.media.setPosition(self.slider_pos.value())

    def pos_changed_event(self, position):
        """
        播放位置被改变事件:更新播放时间和进度条位置
        :return:
        """
        time = self.calc_time(position)  # 计算播放时间
        self.label_time.setText(time)  # 显示播放时间
        self.slider_pos.setValue(position)  # 设置播放进度

    def duration_changed_event(self, duration):
        """
        时间区间被改变事件:更新进度条区间
        :param duration:
        :return:
        """
        self.slider_pos.setRange(0, duration)

    def cur_changed_event(self, index):
        """
        当前播放索引被改变事件:更新播放列表索引
        :param index:
        :return:
        """
        self.play_list.setCurrentRow(index)  # 在列表中选中已显示正在播放的项
        self.label_title.setText(self.play_list.currentItem().text())  # 显示歌曲标题
        self.label_time.setText("00:00")  # 设置为默认时间
        self.save_playlist()  # 保存播放列表记录

    def next(self):
        """
        下一曲
        :return:
        """
        # 判断播放列表是否为空
        if self.list.mediaCount() < 0:
            return

        index = self.list.currentIndex()
        index = 0 if index + 1 >= self.list.mediaCount(
        ) else index + 1  # 如果当前是最后一首,则下一首就为第一首
        self.list.setCurrentIndex(index)
        self.media.play()

    def previous(self):
        """
        上一曲
        :return:
        """
        # 判断播放列表是否为空
        if self.list.mediaCount() < 0:
            return

        index = self.list.currentIndex()
        index = self.list.mediaCount(
        ) - 1 if index - 1 < 0 else index - 1  # 如果当前为第一首,则上一首就为最后一首
        self.list.setCurrentIndex(index)
        self.media.play()

    def add_to_list(self):
        """
        添加项目到播放列表中
        """
        # 弹出多文件选择对话框,默认目录为程序当前目录,仅支持MP3格式
        files = QFileDialog.getOpenFileNames(self, "打开MP3文件",
                                             QDir.currentPath(),
                                             "MP3文件(*.mp3)")
        for filename in files[0]:
            # 判断是否为MP3文件
            if os.path.exists(filename) and str.lower(filename[-4:]) == ".mp3":
                tmp = filename.replace('\\', '/')  # 路径转义
                name = tmp.split('/')[-1][:-4]  # 提取出简单的歌曲文件名
                index = self.list.mediaCount() - 1
                song = QMediaContent(QUrl.fromLocalFile(filename))
                if self.list.insertMedia(index, song):
                    self.play_list.insertItem(index, name)

        # 保存当前播放列表
        self.save_playlist()

    def remove_from_list(self):
        """
        从播放列表中移除选中的项目
        :return: bool 是否移除成功
        """
        index = self.play_list.currentRow()  # 记录将要删除的索引
        tmp_index = -1  # 记录删除后继续播放的索引
        tmp_position = 0  # 记录删除后继续播放的位置
        exec_play = self.media.state() == 1  # 删除后是否要执行播放操作

        if index < 0:
            # 索引无效,不作处理
            return
        elif index == self.list.currentIndex():
            # 如果要删除的歌曲为正在播放的那首歌曲,则先停止播放,删除后播放下一曲
            tmp_index = index if index + 1 < self.list.mediaCount() else 0
            self.media.stop()
        elif index <= self.list.currentIndex():
            # 如果要删除的歌曲是当前播放歌曲之前的歌曲,则改变索引继续播放
            tmp_index = self.list.currentIndex() - 1
            tmp_position = self.media.position()
            self.media.stop()

        # 删除操作
        if self.list.removeMedia(index):
            self.play_list.takeItem(index)

        # 设置新的播放
        self.label_title.setText("简易MP3音乐播放器")
        if 0 <= tmp_index <= self.list.mediaCount() - 1:
            self.list.setCurrentIndex(tmp_index)
            if exec_play:
                self.media.setPosition(tmp_position)
                self.media.play()
                self.label_title.setText(self.play_list.item(tmp_index).text())

        # 保存当前播放列表
        self.save_playlist()

    def load_playlist(self):
        """
        加载播放列表
        :return:
        """
        if not os.path.exists("./default.playlist"):
            return

        try:
            with open("./default.playlist", 'rb') as f:
                data = eval(f.read())

                self.list.clear()
                self.play_list.clear()
                for item in data["list"]:
                    # 判断是否为MP3文件
                    if os.path.exists(item["path"]) and str.lower(
                            item["path"][-4:]) == ".mp3":
                        index = self.list.mediaCount()
                        song = QMediaContent(QUrl.fromLocalFile(item["path"]))
                        if self.list.insertMedia(index, song):
                            self.play_list.insertItem(index, item["name"])

                if -1 <= data["currentItem"] < self.list.mediaCount():
                    self.list.setCurrentIndex(data["currentItem"])
                    self.play_list.setCurrentRow(data["currentItem"])
                    self.label_title.setText(
                        self.play_list.currentItem().text())

                self.slider_pos.setRange(0, data["duration"])
                self.slider_pos.setValue(data["currentPosition"])
                self.media.setPosition(data["currentPosition"])
                self.media.setVolume(data["volume"])
                self.slider_volume.setValue(data["volume"])
                self.media.setMuted(data["muted"])
                img = "./img/icons/volume-mute.png" if data[
                    "muted"] else "./img/icons/volume-normal.png"
                self.btn_mute.setStyleSheet(
                    "QPushButton#btn_mute{image:url('" + img +
                    "')}")  # 静音按钮图标样式
        except:
            pass

    def save_playlist(self):
        """
        保存播放列表
        :return:
        """
        data = {"list": [], "current": -1}
        for i in range(self.list.mediaCount()):
            path = self.list.media(i).canonicalUrl().path()
            name = self.list.media(i).canonicalUrl().fileName()[:-4]
            data["list"].append({"path": path[1:], "name": name})

        data["currentItem"] = self.list.currentIndex()
        data["currentPosition"] = self.media.position()
        data["duration"] = self.media.duration()
        data["volume"] = self.media.volume()
        data["muted"] = self.media.isMuted()

        with open("./default.playlist", 'wb') as f:
            f.write(str(data).encode('utf-8'))

    @staticmethod
    def calc_time(millisecond):
        """
        计算时间(毫秒数格式转“分:秒”格式)
        :param millisecond: int 毫秒数
        :return: string min:sec
        """
        second = millisecond / 1000
        minute = int(second / 60)
        second = int(second % 60)
        # 如果是个位数的话,就在前面加个“0”
        minute = "0" + str(minute) if minute < 10 else str(minute)
        second = "0" + str(second) if second < 10 else str(second)
        return str(minute) + ":" + str(second)