Ejemplo n.º 1
0
class MyMainWindow(QMainWindow, Ui_musicPlayer):
    def __init__(self, parent=None):
        super(MyMainWindow, self).__init__(parent)
        self.setupUi(self)
        self.player = QMediaPlayer()
        # --计时器
        self.timer = QTimer(self)
        self.timer.start(1000)
        self.songs_list_select = []
        self.song_formats = ['mp3', 'm4a', 'flac', 'wav', 'ogg']
        self.settingfilename = 'setting.ini'
        self.cur_path = os.path.abspath(os.path.dirname(__file__))
        self.cur_playing_song = ''
        self.is_switching = False
        self.is_pause = True
        self.openDir.clicked.connect(self.open_Dir)
        self.playStop.clicked.connect(self.playMusic)
        self.horizontalSlider.sliderMoved[int].connect(
            lambda: self.player.setPosition(self.horizontalSlider.value()))
        self.songUp.clicked.connect(self.previewMusic)
        self.songDn.clicked.connect(self.nextMusic)
        self.listWidget.doubleClicked.connect(self.doubleClicked)
        self.timer.timeout.connect(self.playByMode)

    def loadsetting(self):
        if os.path.isfile(self.settingfilename):
            config = configparser.ConfigParser()
            config.read(self.settingfilename)
            self.cur_path = config.get('MusicPlayer', 'PATH')
            self.showMusicList()

    def savesetting(self):
        config = configparser.ConfigParser()
        config.read(self.settingfilename)
        if not os.path.isfile(self.settingfilename):
            config.add_section('MusicPlayer')
        config.set('MusicPlayer', 'PATH', self.cur_path)
        config.write(open(self.settingfilename, 'w'))

    '''提示'''

    def Tips(self, message):
        QMessageBox.about(self, "提示", message)

    def open_Dir(self):
        self.cur_path = QFileDialog.getExistingDirectory(
            self, "选取文件夹", self.cur_path)
        if self.cur_path:
            self.showMusicList()
            self.cur_playing_song = ''
            self.setCurPlaying()
            self.label.setText('00:00')
            self.label_2.setText('00:00')
            self.horizontalSlider.setSliderPosition(0)
            self.is_pause = True
            self.playStop.setText('播放')

    def showMusicList(self):
        self.listWidget.clear()
        # self.updateSetting()
        for song in os.listdir(self.cur_path):
            if song.split('.')[-1] in self.song_formats:
                self.songs_list_select.append([
                    song,
                    os.path.join(self.cur_path, song).replace('\\', '/')
                ])
                self.listWidget.addItem(song)
        self.listWidget.setCurrentRow(0)
        if self.songs_list_select:
            self.cur_playing_song = self.songs_list_select[
                self.listWidget.currentRow()][-1]

    '''根据播放模式播放音乐'''

    def playByMode(self):
        if (not self.is_pause) and (not self.is_switching):
            self.horizontalSlider.setMinimum(0)
            self.horizontalSlider.setMaximum(self.player.duration())
            self.horizontalSlider.setValue(self.horizontalSlider.value() +
                                           1000)
            self.label.setText(
                time.strftime('%M:%S',
                              time.localtime(self.player.position() / 1000)))
            self.label_2.setText(
                time.strftime('%M:%S',
                              time.localtime(self.player.duration() / 1000)))

            if (self.playType.currentIndex()
                    == 0) and (not self.is_pause) and (not self.is_switching):
                if self.listWidget.count() == 0:
                    return
                if self.player.position() == self.player.duration():
                    self.nextMusic()
            elif (self.playType.currentIndex()
                  == 1) and (not self.is_pause) and (not self.is_switching):
                if self.listWidget.count() == 0:
                    return
                if self.player.position() == self.player.duration():
                    self.is_switching = True
                    self.setCurPlaying()
                    self.horizontalSlider.setValue(0)
                    self.playMusic()
                    self.is_switching = False
            elif (self.playType.currentIndex()
                  == 2) and (not self.is_pause) and (not self.is_switching):
                if self.listWidget.count() == 0:
                    return
                if self.player.position() == self.player.duration():
                    self.is_switching = True
                    self.listWidget.setCurrentRow(
                        random.randint(0,
                                       self.qlist.count() - 1))
                    self.setCurPlaying()
                    self.horizontalSlider.setValue(0)
                    self.playMusic()
                    self.is_switching = False

    '''双击播放音乐'''

    def doubleClicked(self):
        self.horizontalSlider.setValue(0)
        self.is_switching = True
        self.setCurPlaying()
        self.playMusic()
        self.is_switching = False

    '''设置当前播放的音乐'''

    def setCurPlaying(self):
        self.cur_playing_song = self.songs_list_select[
            self.listWidget.currentRow()][-1]
        self.player.setMedia(QMediaContent(QUrl(self.cur_playing_song)))

    '''播放音乐'''

    def playMusic(self):
        if self.listWidget.count() == 0:
            self.Tips('当前路径内无可播放的音乐文件')
            return
        if not self.player.isAudioAvailable():
            self.setCurPlaying()
        if self.is_switching or self.is_pause:
            self.player.play()
            self.is_pause = False
            self.playStop.setText('暂停')
        elif (not self.is_pause) and (not self.is_switching):
            self.player.pause()
            self.is_pause = True
            self.playStop.setText('播放')

    '''上一首'''

    def previewMusic(self):
        self.horizontalSlider.setValue(0)
        if self.listWidget.count() == 0:
            self.Tips('当前路径内无可播放的音乐文件')
            return
        pre_row = self.listWidget.currentRow(
        ) - 1 if self.listWidget.currentRow(
        ) != 0 else self.listWidget.count() - 1
        self.listWidget.setCurrentRow(pre_row)
        self.is_switching = True
        self.setCurPlaying()
        self.playMusic()
        self.is_switching = False

    '''下一首'''

    def nextMusic(self):
        self.horizontalSlider.setValue(0)
        if self.listWidget.count() == 0:
            self.Tips('当前路径内无可播放的音乐文件')
            return
        next_row = self.listWidget.currentRow(
        ) + 1 if self.listWidget.currentRow(
        ) != self.listWidget.count() - 1 else 0
        self.listWidget.setCurrentRow(next_row)
        self.is_switching = True
        self.setCurPlaying()
        self.playMusic()
        self.is_switching = False
Ejemplo n.º 2
0
class videoPlayer(QVideoWidget):
    def __init__(self, parent):

        super(QVideoWidget, self).__init__(parent)
        self.player = QMediaPlayer(self)
        self.player.setVolume(50)
        self.player.setVideoOutput(self)
        self.player.mediaStatusChanged.connect(self.mediaStatusChanged)

    def update(self, path):

        if path: path = QUrl.fromLocalFile(path)
        self.player.setMedia(QMediaContent(path))
        self.player.play()

    def rotate(self, path, sign):

        self.update(None)
        clip = VideoFileClip(path)
        clip.rotate(90 * sign)

        if path.endswith(('gif')):
            clip.write_gif(path)
        else:
            clip.write_videofile(path)

        clip.close()

    def pause(self):

        status = self.player.state()
        if status == QMediaPlayer.PlayingState: self.player.pause()
        elif status == QMediaPlayer.PausedState: self.player.play()

    def position(self, delta):

        self.player.setPosition(self.player.position() + delta)

    def volume(self, delta):

        if self.player.isAudioAvailable():

            self.player.setVolume(self.player.volume() + delta)

    def mute(self):

        if self.player.isAudioAvailable():

            self.player.setMuted(not self.player.isMuted())

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

    def mediaStatusChanged(self, status):

        if status == QMediaPlayer.EndOfMedia: self.player.play()

        elif status not in (2, 1):

            self.parent().setCurrentIndex(1)

    def wheelEvent(self, event):

        self.volume(event.angleDelta().y() // 12)
Ejemplo n.º 3
0
class MP3Player(QWidget):
    def __init__(self):
        super().__init__()

        self.startTimeLabel = QLabel('00:00')
        self.endTimeLabel = QLabel('00:00')
        self.slider = QSlider(Qt.Horizontal, self)
        self.PlayModeBtn = QPushButton(self)
        self.playBtn = QPushButton(self)
        self.prevBtn = QPushButton(self)
        self.nextBtn = QPushButton(self)
        self.openBtn = QPushButton(self)
        self.musicList = QListWidget()
        self.song_formats = ['mp3', 'm4a', 'flac', 'wav', 'ogg']
        self.songs_list = []
        self.cur_playing_song = ''
        self.is_pause = True
        self.player = QMediaPlayer()
        self.is_switching = False
        self.playMode = 0
        self.settingfilename = 'config.ini'
        self.textLable = QLabel('前进的路上,也要记得欣赏沿途的风景呀!')
        self.infoLabel = QLabel('Mculover666 v2.0.0')

        self.playBtn.setStyleSheet(
            "QPushButton{border-image: url(resource/image/play.png)}")
        self.playBtn.setFixedSize(48, 48)
        self.nextBtn.setStyleSheet(
            "QPushButton{border-image: url(resource/image/next.png)}")
        self.nextBtn.setFixedSize(48, 48)
        self.prevBtn.setStyleSheet(
            "QPushButton{border-image: url(resource/image/prev.png)}")
        self.prevBtn.setFixedSize(48, 48)
        self.openBtn.setStyleSheet(
            "QPushButton{border-image: url(resource/image/open.png)}")
        self.openBtn.setFixedSize(24, 24)
        self.PlayModeBtn.setStyleSheet(
            "QPushButton{border-image: url(resource/image/sequential.png)}")
        self.PlayModeBtn.setFixedSize(24, 24)

        self.timer = QTimer(self)
        self.timer.start(1000)
        self.timer.timeout.connect(self.playByMode)

        self.hBoxSlider = QHBoxLayout()
        self.hBoxSlider.addWidget(self.startTimeLabel)
        self.hBoxSlider.addWidget(self.slider)
        self.hBoxSlider.addWidget(self.endTimeLabel)

        self.hBoxButton = QHBoxLayout()
        self.hBoxButton.addWidget(self.PlayModeBtn)
        self.hBoxButton.addStretch(1)
        self.hBoxButton.addWidget(self.prevBtn)
        self.hBoxButton.addWidget(self.playBtn)
        self.hBoxButton.addWidget(self.nextBtn)
        self.hBoxButton.addStretch(1)
        self.hBoxButton.addWidget(self.openBtn)

        self.vBoxControl = QVBoxLayout()
        self.vBoxControl.addLayout(self.hBoxSlider)
        self.vBoxControl.addLayout(self.hBoxButton)

        self.hBoxAbout = QHBoxLayout()
        self.hBoxAbout.addWidget(self.textLable)
        self.hBoxAbout.addStretch(1)
        self.hBoxAbout.addWidget(self.infoLabel)

        self.vboxMain = QVBoxLayout()
        self.vboxMain.addWidget(self.musicList)
        self.vboxMain.addLayout(self.vBoxControl)
        self.vboxMain.addLayout(self.hBoxAbout)

        self.setLayout(self.vboxMain)

        self.openBtn.clicked.connect(self.openMusicFloder)
        self.playBtn.clicked.connect(self.playMusic)
        self.prevBtn.clicked.connect(self.prevMusic)
        self.nextBtn.clicked.connect(self.nextMusic)
        self.musicList.itemDoubleClicked.connect(self.doubleClicked)
        self.slider.sliderMoved[int].connect(
            lambda: self.player.setPosition(self.slider.value()))
        self.PlayModeBtn.clicked.connect(self.playModeSet)

        self.loadingSetting()

        self.initUI()

    # 初始化界面
    def initUI(self):
        self.resize(600, 400)
        self.center()
        self.setWindowTitle('音乐播放器')
        self.setWindowIcon(QIcon('resource/image/favicon.ico'))
        self.show()

    # 窗口显示居中
    def center(self):
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())

    # 打开文件夹
    def openMusicFloder(self):
        self.cur_path = QFileDialog.getExistingDirectory(self, "选取音乐文件夹", './')
        if self.cur_path:
            self.showMusicList()
            self.cur_playing_song = ''
            self.startTimeLabel.setText('00:00')
            self.endTimeLabel.setText('00:00')
            self.slider.setSliderPosition(0)
            self.updateSetting()
            self.is_pause = True
            self.playBtn.setStyleSheet(
                "QPushButton{border-image: url(resource/image/play.png)}")

    # 显示音乐列表
    def showMusicList(self):
        self.musicList.clear()
        for song in os.listdir(self.cur_path):
            if song.split('.')[-1] in self.song_formats:
                self.songs_list.append([
                    song,
                    os.path.join(self.cur_path, song).replace('\\', '/')
                ])
                self.musicList.addItem(song)
        self.musicList.setCurrentRow(0)
        if self.songs_list:
            self.cur_playing_song = self.songs_list[
                self.musicList.currentRow()][-1]

    # 提示
    def Tips(self, message):
        QMessageBox.about(self, "提示", message)

    # 设置当前播放的音乐
    def setCurPlaying(self):
        self.cur_playing_song = self.songs_list[
            self.musicList.currentRow()][-1]
        self.player.setMedia(QMediaContent(QUrl(self.cur_playing_song)))

    # 播放/暂停播放
    def playMusic(self):
        if self.musicList.count() == 0:
            self.Tips('当前路径内无可播放的音乐文件')
            return
        if not self.player.isAudioAvailable():
            self.setCurPlaying()
        if self.is_pause or self.is_switching:
            self.player.play()
            self.is_pause = False
            self.playBtn.setStyleSheet(
                "QPushButton{border-image: url(resource/image/pause.png)}")
        elif (not self.is_pause) and (not self.is_switching):
            self.player.pause()
            self.is_pause = True
            self.playBtn.setStyleSheet(
                "QPushButton{border-image: url(resource/image/play.png)}")

    # 上一曲
    def prevMusic(self):
        self.slider.setValue(0)
        if self.musicList.count() == 0:
            self.Tips('当前路径内无可播放的音乐文件')
            return
        pre_row = self.musicList.currentRow() - 1 if self.musicList.currentRow(
        ) != 0 else self.musicList.count() - 1
        self.musicList.setCurrentRow(pre_row)
        self.is_switching = True
        self.setCurPlaying()
        self.playMusic()
        self.is_switching = False

    # 下一曲
    def nextMusic(self):
        self.slider.setValue(0)
        if self.musicList.count() == 0:
            self.Tips('当前路径内无可播放的音乐文件')
            return
        next_row = self.musicList.currentRow(
        ) + 1 if self.musicList.currentRow(
        ) != self.musicList.count() - 1 else 0
        self.musicList.setCurrentRow(next_row)
        self.is_switching = True
        self.setCurPlaying()
        self.playMusic()
        self.is_switching = False

    # 双击歌曲名称播放音乐
    def doubleClicked(self):
        self.slider.setValue(0)
        self.is_switching = True
        self.setCurPlaying()
        self.playMusic()
        self.is_switching = False

    # 根据播放模式自动播放,并刷新进度条
    def playByMode(self):
        # 刷新进度条
        if (not self.is_pause) and (not self.is_switching):
            self.slider.setMinimum(0)
            self.slider.setMaximum(self.player.duration())
            self.slider.setValue(self.slider.value() + 1000)
        self.startTimeLabel.setText(
            time.strftime('%M:%S',
                          time.localtime(self.player.position() / 1000)))
        self.endTimeLabel.setText(
            time.strftime('%M:%S',
                          time.localtime(self.player.duration() / 1000)))
        # 顺序播放
        if (self.playMode
                == 0) and (not self.is_pause) and (not self.is_switching):
            if self.musicList.count() == 0:
                return
            if self.player.position() == self.player.duration():
                self.nextMusic()
        # 单曲循环
        elif (self.playMode
              == 1) and (not self.is_pause) and (not self.is_switching):
            if self.musicList.count() == 0:
                return
            if self.player.position() == self.player.duration():
                self.is_switching = True
                self.setCurPlaying()
                self.slider.setValue(0)
                self.playMusic()
                self.is_switching = False
        # 随机播放
        elif (self.playMode
              == 2) and (not self.is_pause) and (not self.is_switching):
            if self.musicList.count() == 0:
                return
            if self.player.position() == self.player.duration():
                self.is_switching = True
                self.musicList.setCurrentRow(
                    random.randint(0,
                                   self.musicList.count() - 1))
                self.setCurPlaying()
                self.slider.setValue(0)
                self.playMusic()
                self.is_switching = False

    # 更新配置文件
    def updateSetting(self):
        config = configparser.ConfigParser()
        config.read(self.settingfilename)
        if not os.path.isfile(self.settingfilename):
            config.add_section('MP3Player')
        config.set('MP3Player', 'PATH', self.cur_path)
        config.write(open(self.settingfilename, 'w'))

    # 加载配置文件
    def loadingSetting(self):
        config = configparser.ConfigParser()
        config.read(self.settingfilename)
        if not os.path.isfile(self.settingfilename):
            return
        self.cur_path = config.get('MP3Player', 'PATH')
        self.showMusicList()

    # 播放模式设置
    def playModeSet(self):
        # 设置为单曲循环模式
        if self.playMode == 0:
            self.playMode = 1
            self.PlayModeBtn.setStyleSheet(
                "QPushButton{border-image: url(resource/image/circulation.png)}"
            )
        # 设置为随机播放模式
        elif self.playMode == 1:
            self.playMode = 2
            self.PlayModeBtn.setStyleSheet(
                "QPushButton{border-image: url(resource/image/random.png)}")
        # 设置为顺序播放模式
        elif self.playMode == 2:
            self.playMode = 0
            self.PlayModeBtn.setStyleSheet(
                "QPushButton{border-image: url(resource/image/sequential.png)}"
            )

    # 确认用户是否要真正退出
    def closeEvent(self, event):
        reply = QMessageBox.question(self, 'Message', "确定要退出吗?",
                                     QMessageBox.Yes | QMessageBox.No,
                                     QMessageBox.No)
        if reply == QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()
Ejemplo n.º 4
0
class VidCutter(QWidget):
    def __init__(self, parent):
        super(VidCutter, self).__init__(parent)
        self.parent = parent
        self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface)
        self.videoWidget = VideoWidget()
        self.videoService = VideoService(self)

        QFontDatabase.addApplicationFont(
            os.path.join(self.getAppPath(), 'fonts', 'DroidSansMono.ttf'))
        QFontDatabase.addApplicationFont(
            os.path.join(self.getAppPath(), 'fonts', 'HelveticaNeue.ttf'))
        qApp.setFont(QFont('Helvetica Neue', 10))

        self.clipTimes = []
        self.inCut = False
        self.movieFilename = ''
        self.movieLoaded = False
        self.timeformat = 'hh:mm:ss'
        self.finalFilename = ''
        self.totalRuntime = 0

        self.initIcons()
        self.initActions()

        self.toolbar = QToolBar(
            floatable=False,
            movable=False,
            iconSize=QSize(28, 28),
            toolButtonStyle=Qt.ToolButtonTextUnderIcon,
            styleSheet=
            'QToolBar QToolButton { min-width:82px; margin-left:10px; margin-right:10px; font-size:14px; }'
        )
        self.initToolbar()

        self.aboutMenu, self.cliplistMenu = QMenu(), QMenu()
        self.initMenus()

        self.seekSlider = VideoSlider(parent=self,
                                      sliderMoved=self.setPosition)
        self.seekSlider.installEventFilter(self)

        self.initNoVideo()

        self.cliplist = QListWidget(
            sizePolicy=QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding),
            contextMenuPolicy=Qt.CustomContextMenu,
            uniformItemSizes=True,
            iconSize=QSize(100, 700),
            dragDropMode=QAbstractItemView.InternalMove,
            alternatingRowColors=True,
            customContextMenuRequested=self.itemMenu,
            styleSheet='QListView::item { margin:10px 5px; }')
        self.cliplist.setFixedWidth(185)
        self.cliplist.model().rowsMoved.connect(self.syncClipList)

        listHeader = QLabel(pixmap=QPixmap(
            os.path.join(self.getAppPath(), 'images', 'clipindex.png'), 'PNG'),
                            alignment=Qt.AlignCenter)
        listHeader.setStyleSheet(
            '''padding:5px; padding-top:8px; border:1px solid #b9b9b9; border-bottom:none;
                                    background-color:qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #FFF,
                                    stop: 0.5 #EAEAEA, stop: 0.6 #EAEAEA stop:1 #FFF);'''
        )

        self.runtimeLabel = QLabel('<div align="right">00:00:00</div>',
                                   textFormat=Qt.RichText)
        self.runtimeLabel.setStyleSheet(
            '''font-family:Droid Sans Mono; font-size:10pt; color:#FFF;
                                           background:rgb(106, 69, 114) url(:images/runtime.png)
                                           no-repeat left center; padding:2px; padding-right:8px;
                                           border:1px solid #b9b9b9; border-top:none;'''
        )

        self.clipindexLayout = QVBoxLayout(spacing=0)
        self.clipindexLayout.setContentsMargins(0, 0, 0, 0)
        self.clipindexLayout.addWidget(listHeader)
        self.clipindexLayout.addWidget(self.cliplist)
        self.clipindexLayout.addWidget(self.runtimeLabel)

        self.videoLayout = QHBoxLayout()
        self.videoLayout.setContentsMargins(0, 0, 0, 0)
        self.videoLayout.addWidget(self.novideoWidget)
        self.videoLayout.addLayout(self.clipindexLayout)

        self.timeCounter = QLabel('00:00:00 / 00:00:00',
                                  autoFillBackground=True,
                                  alignment=Qt.AlignCenter,
                                  sizePolicy=QSizePolicy(
                                      QSizePolicy.Expanding,
                                      QSizePolicy.Fixed))
        self.timeCounter.setStyleSheet(
            'color:#FFF; background:#000; font-family:Droid Sans Mono; font-size:10.5pt; padding:4px;'
        )

        videoplayerLayout = QVBoxLayout(spacing=0)
        videoplayerLayout.setContentsMargins(0, 0, 0, 0)
        videoplayerLayout.addWidget(self.videoWidget)
        videoplayerLayout.addWidget(self.timeCounter)

        self.videoplayerWidget = QWidget(self, visible=False)
        self.videoplayerWidget.setLayout(videoplayerLayout)

        self.menuButton = QPushButton(icon=self.aboutIcon,
                                      flat=True,
                                      toolTip='About',
                                      statusTip='About',
                                      iconSize=QSize(24, 24),
                                      cursor=Qt.PointingHandCursor)
        self.menuButton.setMenu(self.aboutMenu)

        self.muteButton = QPushButton(icon=self.unmuteIcon,
                                      flat=True,
                                      toolTip='Mute',
                                      statusTip='Toggle audio mute',
                                      cursor=Qt.PointingHandCursor,
                                      clicked=self.muteAudio)

        self.volumeSlider = QSlider(Qt.Horizontal,
                                    toolTip='Volume',
                                    statusTip='Adjust volume level',
                                    cursor=Qt.PointingHandCursor,
                                    value=50,
                                    sizePolicy=QSizePolicy(
                                        QSizePolicy.Fixed,
                                        QSizePolicy.Minimum),
                                    minimum=0,
                                    maximum=100,
                                    sliderMoved=self.setVolume)
        self.volumeSlider.setStyleSheet(
            '''QSlider::groove:horizontal { height:40px; }
                                           QSlider::sub-page:horizontal { border:1px outset #6A4572; background:#6A4572; margin:2px; }
                                           QSlider::handle:horizontal { image: url(:images/knob.png) no-repeat top left; width:20px; }'''
        )

        self.saveAction = QPushButton(
            self.parent,
            icon=self.saveIcon,
            text='Save Video',
            flat=True,
            toolTip='Save Video',
            clicked=self.cutVideo,
            cursor=Qt.PointingHandCursor,
            iconSize=QSize(30, 30),
            statusTip='Save video clips merged as a new video file',
            enabled=False)
        self.saveAction.setStyleSheet(
            '''QPushButton { color:#FFF; padding:8px; font-size:12pt;
                                            border:1px inset #481953; border-radius:4px;
                                            background-color:rgb(106, 69, 114); }
                                         QPushButton:!enabled { background-color:rgba(0, 0, 0, 0.1);
                                            color:rgba(0, 0, 0, 0.3); border:1px inset #CDCDCD; }
                                         QPushButton:hover { background-color:rgba(255, 255, 255, 0.8); color:#444; }
                                         QPushButton:pressed { background-color:rgba(218, 218, 219, 0.8); color:#444; }'''
        )

        controlsLayout = QHBoxLayout()
        controlsLayout.addStretch(1)
        controlsLayout.addWidget(self.toolbar)
        controlsLayout.addSpacerItem(QSpacerItem(20, 1))
        controlsLayout.addWidget(self.saveAction)
        controlsLayout.addStretch(1)
        controlsLayout.addWidget(self.muteButton)
        controlsLayout.addWidget(self.volumeSlider)
        controlsLayout.addSpacing(1)
        controlsLayout.addWidget(self.menuButton)

        layout = QVBoxLayout()
        layout.setContentsMargins(10, 10, 10, 4)
        layout.addLayout(self.videoLayout)
        layout.addWidget(self.seekSlider)
        layout.addLayout(controlsLayout)

        self.setLayout(layout)

        self.mediaPlayer.setVideoOutput(self.videoWidget)
        self.mediaPlayer.stateChanged.connect(self.mediaStateChanged)
        self.mediaPlayer.positionChanged.connect(self.positionChanged)
        self.mediaPlayer.durationChanged.connect(self.durationChanged)
        self.mediaPlayer.error.connect(self.handleError)

    def initNoVideo(self) -> None:
        novideoImage = QLabel(alignment=Qt.AlignCenter,
                              autoFillBackground=False,
                              pixmap=QPixmap(
                                  os.path.join(self.getAppPath(), 'images',
                                               'novideo.png'), 'PNG'),
                              sizePolicy=QSizePolicy(
                                  QSizePolicy.Expanding,
                                  QSizePolicy.MinimumExpanding))
        novideoImage.setBackgroundRole(QPalette.Dark)
        novideoImage.setContentsMargins(0, 20, 0, 20)
        self.novideoLabel = QLabel(alignment=Qt.AlignCenter,
                                   autoFillBackground=True,
                                   sizePolicy=QSizePolicy(
                                       QSizePolicy.Expanding,
                                       QSizePolicy.Minimum))
        self.novideoLabel.setBackgroundRole(QPalette.Dark)
        self.novideoLabel.setContentsMargins(0, 20, 15, 60)
        novideoLayout = QVBoxLayout(spacing=0)
        novideoLayout.addWidget(novideoImage)
        novideoLayout.addWidget(self.novideoLabel, alignment=Qt.AlignTop)
        self.novideoMovie = QMovie(
            os.path.join(self.getAppPath(), 'images', 'novideotext.gif'))
        self.novideoMovie.frameChanged.connect(self.setNoVideoText)
        self.novideoMovie.start()
        self.novideoWidget = QWidget(self, autoFillBackground=True)
        self.novideoWidget.setBackgroundRole(QPalette.Dark)
        self.novideoWidget.setLayout(novideoLayout)

    def initIcons(self) -> None:
        self.appIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'vidcutter.png'))
        self.openIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'addmedia.png'))
        self.playIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'play.png'))
        self.pauseIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'pause.png'))
        self.cutStartIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'cut-start.png'))
        self.cutEndIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'cut-end.png'))
        self.saveIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'save.png'))
        self.muteIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'muted.png'))
        self.unmuteIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'unmuted.png'))
        self.upIcon = QIcon(os.path.join(self.getAppPath(), 'images',
                                         'up.png'))
        self.downIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'down.png'))
        self.removeIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'remove.png'))
        self.removeAllIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'remove-all.png'))
        self.successIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'success.png'))
        self.aboutIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'about.png'))
        self.completePlayIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'complete-play.png'))
        self.completeOpenIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'complete-open.png'))
        self.completeRestartIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'complete-restart.png'))
        self.completeExitIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'complete-exit.png'))

    def initActions(self) -> None:
        self.openAction = QAction(self.openIcon,
                                  'Add Media',
                                  self,
                                  statusTip='Select media source',
                                  triggered=self.openFile)
        self.playAction = QAction(self.playIcon,
                                  'Play Video',
                                  self,
                                  statusTip='Play selected media',
                                  triggered=self.playVideo,
                                  enabled=False)
        self.cutStartAction = QAction(self.cutStartIcon,
                                      'Set Start',
                                      self,
                                      toolTip='Set Start',
                                      statusTip='Set start marker',
                                      triggered=self.cutStart,
                                      enabled=False)
        self.cutEndAction = QAction(self.cutEndIcon,
                                    'Set End',
                                    self,
                                    statusTip='Set end marker',
                                    triggered=self.cutEnd,
                                    enabled=False)
        self.moveItemUpAction = QAction(
            self.upIcon,
            'Move Up',
            self,
            statusTip='Move clip position up in list',
            triggered=self.moveItemUp,
            enabled=False)
        self.moveItemDownAction = QAction(
            self.downIcon,
            'Move Down',
            self,
            statusTip='Move clip position down in list',
            triggered=self.moveItemDown,
            enabled=False)
        self.removeItemAction = QAction(
            self.removeIcon,
            'Remove clip',
            self,
            statusTip='Remove selected clip from list',
            triggered=self.removeItem,
            enabled=False)
        self.removeAllAction = QAction(self.removeAllIcon,
                                       'Clear list',
                                       self,
                                       statusTip='Clear all clips from list',
                                       triggered=self.clearList,
                                       enabled=False)
        self.aboutAction = QAction('About %s' % qApp.applicationName(),
                                   self,
                                   statusTip='Credits and acknowledgements',
                                   triggered=self.aboutInfo)
        self.aboutQtAction = QAction('About Qt',
                                     self,
                                     statusTip='About Qt',
                                     triggered=qApp.aboutQt)
        self.mediaInfoAction = QAction(
            'Media Information',
            self,
            statusTip='Media information from loaded video file',
            triggered=self.mediaInfo,
            enabled=False)

    def initToolbar(self) -> None:
        self.toolbar.addAction(self.openAction)
        self.toolbar.addAction(self.playAction)
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.cutStartAction)
        self.toolbar.addAction(self.cutEndAction)
        self.toolbar.addSeparator()

    def initMenus(self) -> None:
        self.aboutMenu.addAction(self.mediaInfoAction)
        self.aboutMenu.addSeparator()
        self.aboutMenu.addAction(self.aboutQtAction)
        self.aboutMenu.addAction(self.aboutAction)
        self.cliplistMenu.addAction(self.moveItemUpAction)
        self.cliplistMenu.addAction(self.moveItemDownAction)
        self.cliplistMenu.addSeparator()
        self.cliplistMenu.addAction(self.removeItemAction)
        self.cliplistMenu.addAction(self.removeAllAction)

    def setRunningTime(self, runtime: str) -> None:
        self.runtimeLabel.setText('<div align="right">%s</div>' % runtime)

    @pyqtSlot(int)
    def setNoVideoText(self, frame: int) -> None:
        self.novideoLabel.setPixmap(self.novideoMovie.currentPixmap())

    def itemMenu(self, pos: QPoint) -> None:
        globalPos = self.cliplist.mapToGlobal(pos)
        self.moveItemUpAction.setEnabled(False)
        self.moveItemDownAction.setEnabled(False)
        self.removeItemAction.setEnabled(False)
        self.removeAllAction.setEnabled(False)
        index = self.cliplist.currentRow()
        if index != -1:
            if not self.inCut:
                if index > 0:
                    self.moveItemUpAction.setEnabled(True)
                if index < self.cliplist.count() - 1:
                    self.moveItemDownAction.setEnabled(True)
            if self.cliplist.count() > 0:
                self.removeItemAction.setEnabled(True)
        if self.cliplist.count() > 0:
            self.removeAllAction.setEnabled(True)
        self.cliplistMenu.exec_(globalPos)

    def moveItemUp(self) -> None:
        index = self.cliplist.currentRow()
        tmpItem = self.clipTimes[index]
        del self.clipTimes[index]
        self.clipTimes.insert(index - 1, tmpItem)
        self.renderTimes()

    def moveItemDown(self) -> None:
        index = self.cliplist.currentRow()
        tmpItem = self.clipTimes[index]
        del self.clipTimes[index]
        self.clipTimes.insert(index + 1, tmpItem)
        self.renderTimes()

    def removeItem(self) -> None:
        index = self.cliplist.currentRow()
        del self.clipTimes[index]
        if self.inCut and index == self.cliplist.count() - 1:
            self.inCut = False
            self.initMediaControls()
        self.renderTimes()

    def clearList(self) -> None:
        self.clipTimes.clear()
        self.cliplist.clear()
        self.inCut = False
        self.renderTimes()
        self.initMediaControls()

    def mediaInfo(self) -> None:
        if self.mediaPlayer.isMetaDataAvailable():
            content = '<table cellpadding="4">'
            for key in self.mediaPlayer.availableMetaData():
                val = self.mediaPlayer.metaData(key)
                if type(val) is QSize:
                    val = '%s x %s' % (val.width(), val.height())
                content += '<tr><td align="right"><b>%s:</b></td><td>%s</td></tr>\n' % (
                    key, val)
            content += '</table>'
            mbox = QMessageBox(windowTitle='Media Information',
                               windowIcon=self.parent.windowIcon(),
                               textFormat=Qt.RichText)
            mbox.setText('<b>%s</b>' % os.path.basename(
                self.mediaPlayer.currentMedia().canonicalUrl().toLocalFile()))
            mbox.setInformativeText(content)
            mbox.exec_()
        else:
            QMessageBox.critical(
                self.parent, 'Could not retrieve media information',
                '''There was a problem in tring to retrieve media information.
                                    This DOES NOT mean there is a problem with the file and you should
                                    be able to continue using it.''')

    def aboutInfo(self) -> None:
        about_html = '''<style>
    a { color:#441d4e; text-decoration:none; font-weight:bold; }
    a:hover { text-decoration:underline; }
</style>
<p style="font-size:26pt; font-weight:bold;">%s</p>
<p>
    <span style="font-size:13pt;"><b>Version: %s</b></span>
    <span style="font-size:10pt;position:relative;left:5px;">( %s )</span>
</p>
<p style="font-size:13px;">
    Copyright &copy; 2016 <a href="mailto:[email protected]">Pete Alexandrou</a>
    <br/>
    Website: <a href="%s">%s</a>
</p>
<p style="font-size:13px;">
    Thanks to the folks behind the <b>Qt</b>, <b>PyQt</b> and <b>FFmpeg</b>
    projects for all their hard and much appreciated work.
</p>
<p style="font-size:11px;">
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.
</p>
<p style="font-size:11px;">
    This software uses libraries from the <a href="https://www.ffmpeg.org">FFmpeg</a> project under the
    <a href="https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html">LGPLv2.1</a>
</p>''' % (qApp.applicationName(), qApp.applicationVersion(),
           platform.architecture()[0], qApp.organizationDomain(),
           qApp.organizationDomain())
        QMessageBox.about(self.parent, 'About %s' % qApp.applicationName(),
                          about_html)

    def openFile(self) -> None:
        filename, _ = QFileDialog.getOpenFileName(self.parent,
                                                  caption='Select video',
                                                  directory=QDir.homePath())
        if filename != '':
            self.loadFile(filename)

    def loadFile(self, filename: str) -> None:
        self.movieFilename = filename
        if not os.path.exists(filename):
            return
        self.mediaPlayer.setMedia(QMediaContent(QUrl.fromLocalFile(filename)))
        self.initMediaControls(True)
        self.cliplist.clear()
        self.clipTimes = []
        self.parent.setWindowTitle(
            '%s - %s' % (qApp.applicationName(), os.path.basename(filename)))
        if not self.movieLoaded:
            self.videoLayout.replaceWidget(self.novideoWidget,
                                           self.videoplayerWidget)
            self.novideoMovie.stop()
            self.novideoMovie.deleteLater()
            self.novideoWidget.deleteLater()
            self.videoplayerWidget.show()
            self.videoWidget.show()
            self.movieLoaded = True
        if self.mediaPlayer.isVideoAvailable():
            self.mediaPlayer.setPosition(1)
        self.mediaPlayer.play()
        self.mediaPlayer.pause()

    def playVideo(self) -> None:
        if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            self.mediaPlayer.pause()
            self.playAction.setText('Play Video')
        else:
            self.mediaPlayer.play()
            self.playAction.setText('Pause Video')

    def initMediaControls(self, flag: bool = True) -> None:
        self.playAction.setEnabled(flag)
        self.saveAction.setEnabled(False)
        self.cutStartAction.setEnabled(flag)
        self.cutEndAction.setEnabled(False)
        self.mediaInfoAction.setEnabled(flag)
        if flag:
            self.seekSlider.setRestrictValue(0)

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

    def positionChanged(self, progress: int) -> None:
        self.seekSlider.setValue(progress)
        currentTime = self.deltaToQTime(progress)
        totalTime = self.deltaToQTime(self.mediaPlayer.duration())
        self.timeCounter.setText('%s / %s' % (currentTime.toString(
            self.timeformat), totalTime.toString(self.timeformat)))

    @pyqtSlot()
    def mediaStateChanged(self) -> None:
        if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            self.playAction.setIcon(self.pauseIcon)
        else:
            self.playAction.setIcon(self.playIcon)

    def durationChanged(self, duration: int) -> None:
        self.seekSlider.setRange(0, duration)

    def muteAudio(self, muted: bool) -> None:
        if self.mediaPlayer.isMuted():
            self.mediaPlayer.setMuted(not self.mediaPlayer.isMuted())
            self.muteButton.setIcon(self.unmuteIcon)
            self.muteButton.setToolTip('Mute')
        else:
            self.mediaPlayer.setMuted(not self.mediaPlayer.isMuted())
            self.muteButton.setIcon(self.muteIcon)
            self.muteButton.setToolTip('Unmute')

    def setVolume(self, volume: int) -> None:
        self.mediaPlayer.setVolume(volume)

    def toggleFullscreen(self) -> None:
        self.videoWidget.setFullScreen(not self.videoWidget.isFullScreen())

    def cutStart(self) -> None:
        self.clipTimes.append([
            self.deltaToQTime(self.mediaPlayer.position()), '',
            self.captureImage()
        ])
        self.cutStartAction.setDisabled(True)
        self.cutEndAction.setEnabled(True)
        self.seekSlider.setRestrictValue(self.seekSlider.value() + 1000)
        self.mediaPlayer.setPosition(self.seekSlider.restrictValue)
        self.inCut = True
        self.renderTimes()

    def cutEnd(self) -> None:
        item = self.clipTimes[len(self.clipTimes) - 1]
        selected = self.deltaToQTime(self.mediaPlayer.position())
        if selected.__lt__(item[0]):
            QMessageBox.critical(
                self.parent, 'Invalid END Time',
                'The clip end time must come AFTER it\'s start time. Please try again.'
            )
            return
        item[1] = selected
        self.cutStartAction.setEnabled(True)
        self.cutEndAction.setDisabled(True)
        self.seekSlider.setRestrictValue(0)
        self.inCut = False
        self.renderTimes()

    @pyqtSlot(QModelIndex, int, int, QModelIndex, int)
    def syncClipList(self, parent: QModelIndex, start: int, end: int,
                     destination: QModelIndex, row: int) -> None:
        if start < row:
            index = row - 1
        else:
            index = row
        clip = self.clipTimes.pop(start)
        self.clipTimes.insert(index, clip)

    def renderTimes(self) -> None:
        self.cliplist.clear()
        self.seekSlider.setCutMode(self.inCut)
        if len(self.clipTimes) > 4:
            self.cliplist.setFixedWidth(200)
        else:
            self.cliplist.setFixedWidth(185)
        self.totalRuntime = 0
        for item in self.clipTimes:
            endItem = ''
            if type(item[1]) is QTime:
                endItem = item[1].toString(self.timeformat)
                self.totalRuntime += item[0].msecsTo(item[1])
            listitem = QListWidgetItem()
            listitem.setTextAlignment(Qt.AlignVCenter)
            if type(item[2]) is QPixmap:
                listitem.setIcon(QIcon(item[2]))
            self.cliplist.addItem(listitem)
            marker = QLabel(
                '''<style>b { font-size:8pt; } p { margin:5px; }</style>
                            <p><b>START</b><br/>%s</p><p><b>END</b><br/>%s</p>'''
                % (item[0].toString(self.timeformat), endItem))
            self.cliplist.setItemWidget(listitem, marker)
            listitem.setFlags(Qt.ItemIsSelectable | Qt.ItemIsDragEnabled
                              | Qt.ItemIsEnabled)
        if len(self.clipTimes) and not self.inCut:
            self.saveAction.setEnabled(True)
        if self.inCut or len(self.clipTimes) == 0 or not type(
                self.clipTimes[0][1]) is QTime:
            self.saveAction.setEnabled(False)
        self.setRunningTime(
            self.deltaToQTime(self.totalRuntime).toString(self.timeformat))

    @staticmethod
    def deltaToQTime(millisecs: int) -> QTime:
        secs = millisecs / 1000
        return QTime((secs / 3600) % 60, (secs / 60) % 60, secs % 60,
                     (secs * 1000) % 1000)

    def captureImage(self) -> None:
        frametime = self.deltaToQTime(
            self.mediaPlayer.position()).addSecs(1).toString(self.timeformat)
        inputfile = self.mediaPlayer.currentMedia().canonicalUrl().toLocalFile(
        )
        imagecap = self.videoService.capture(inputfile, frametime)
        if type(imagecap) is QPixmap:
            return imagecap

    def cutVideo(self) -> bool:
        self.setCursor(Qt.BusyCursor)
        clips = len(self.clipTimes)
        filename, filelist = '', []
        source = self.mediaPlayer.currentMedia().canonicalUrl().toLocalFile()
        _, sourceext = os.path.splitext(source)
        if clips > 0:
            self.finalFilename, _ = QFileDialog.getSaveFileName(
                self.parent, 'Save video', source,
                'Video files (*%s)' % sourceext)
            if self.finalFilename != '':
                self.saveAction.setDisabled(True)
                self.showProgress(clips)
                file, ext = os.path.splitext(self.finalFilename)
                index = 1
                self.progress.setLabelText('Cutting video clips...')
                qApp.processEvents()
                for clip in self.clipTimes:
                    duration = self.deltaToQTime(clip[0].msecsTo(
                        clip[1])).toString(self.timeformat)
                    filename = '%s_%s%s' % (file, '{0:0>2}'.format(index), ext)
                    filelist.append(filename)
                    self.videoService.cut(source, filename,
                                          clip[0].toString(self.timeformat),
                                          duration)
                    index += 1
                if len(filelist) > 1:
                    self.joinVideos(filelist, self.finalFilename)
                else:
                    QFile.remove(self.finalFilename)
                    QFile.rename(filename, self.finalFilename)
                self.unsetCursor()
                self.progress.setLabelText('Complete...')
                qApp.processEvents()
                self.saveAction.setEnabled(True)
                self.progress.close()
                self.progress.deleteLater()
                self.complete()
                self.saveAction.setEnabled(True)
            self.unsetCursor()
            self.saveAction.setDisabled(True)
            return True
        self.unsetCursor()
        self.saveAction.setDisabled(True)
        return False

    def joinVideos(self, joinlist: list, filename: str) -> None:
        listfile = os.path.normpath(
            os.path.join(os.path.dirname(joinlist[0]), '.vidcutter.list'))
        fobj = open(listfile, 'w')
        for file in joinlist:
            fobj.write('file \'%s\'\n' % file.replace("'", "\\'"))
        fobj.close()
        self.videoService.join(listfile, filename)
        try:
            QFile.remove(listfile)
            for file in joinlist:
                if os.path.isfile(file):
                    QFile.remove(file)
        except:
            pass

    def showProgress(self,
                     steps: int,
                     label: str = 'Processing video...') -> None:
        self.progress = QProgressDialog(label,
                                        None,
                                        0,
                                        steps,
                                        self.parent,
                                        windowModality=Qt.ApplicationModal,
                                        windowIcon=self.parent.windowIcon(),
                                        minimumDuration=0,
                                        minimumWidth=500)
        self.progress.show()
        for i in range(steps):
            self.progress.setValue(i)
            qApp.processEvents()
            time.sleep(1)

    def complete(self) -> None:
        info = QFileInfo(self.finalFilename)
        mbox = QMessageBox(windowTitle='Success',
                           windowIcon=self.parent.windowIcon(),
                           minimumWidth=500,
                           iconPixmap=self.successIcon.pixmap(48, 49),
                           textFormat=Qt.RichText)
        mbox.setText(
            '''
<style>
    table.info { margin:8px; padding:4px 15px; }
    td.label { font-weight:bold; font-size:9pt; text-align:right; background-color:#444; color:#FFF; }
    td.value { background-color:#FFF !important; font-size:10pt; }
</style>
<p>Your video was successfully created.</p>
<p align="center">
    <table class="info" cellpadding="6" cellspacing="0">
        <tr>
            <td class="label"><b>Filename</b></td>
            <td class="value" nowrap>%s</td>
        </tr>
        <tr>
            <td class="label"><b>Size</b></td>
            <td class="value">%s</td>
        </tr>
        <tr>
            <td class="label"><b>Runtime</b></td>
            <td class="value">%s</td>
        </tr>
    </table>
</p>
<p>How would you like to proceed?</p>''' %
            (QDir.toNativeSeparators(
                self.finalFilename), self.sizeof_fmt(int(info.size())),
             self.deltaToQTime(self.totalRuntime).toString(self.timeformat)))
        play = mbox.addButton('Play', QMessageBox.AcceptRole)
        play.setIcon(self.completePlayIcon)
        play.clicked.connect(self.openResult)
        fileman = mbox.addButton('Open', QMessageBox.AcceptRole)
        fileman.setIcon(self.completeOpenIcon)
        fileman.clicked.connect(self.openFolder)
        end = mbox.addButton('Exit', QMessageBox.AcceptRole)
        end.setIcon(self.completeExitIcon)
        end.clicked.connect(self.close)
        new = mbox.addButton('Restart', QMessageBox.AcceptRole)
        new.setIcon(self.completeRestartIcon)
        new.clicked.connect(self.startNew)
        mbox.setDefaultButton(new)
        mbox.setEscapeButton(new)
        mbox.exec_()

    def sizeof_fmt(self, num: float, suffix: chr = 'B') -> str:
        for unit in ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']:
            if abs(num) < 1024.0:
                return "%3.1f%s%s" % (num, unit, suffix)
            num /= 1024.0
        return "%.1f%s%s" % (num, 'Y', suffix)

    @pyqtSlot()
    def openFolder(self) -> None:
        self.openResult(pathonly=True)

    @pyqtSlot(bool)
    def openResult(self, pathonly: bool = False) -> None:
        self.startNew()
        if len(self.finalFilename) and os.path.exists(self.finalFilename):
            target = self.finalFilename if not pathonly else os.path.dirname(
                self.finalFilename)
            QDesktopServices.openUrl(QUrl.fromLocalFile(target))

    @pyqtSlot()
    def startNew(self) -> None:
        self.unsetCursor()
        self.clearList()
        self.seekSlider.setValue(0)
        self.seekSlider.setRange(0, 0)
        self.mediaPlayer.setMedia(QMediaContent())
        self.initNoVideo()
        self.videoLayout.replaceWidget(self.videoplayerWidget,
                                       self.novideoWidget)
        self.initMediaControls(False)
        self.parent.setWindowTitle('%s' % qApp.applicationName())

    def wheelEvent(self, event: QWheelEvent) -> None:
        if self.mediaPlayer.isVideoAvailable(
        ) or self.mediaPlayer.isAudioAvailable():
            if event.angleDelta().y() > 0:
                newval = self.seekSlider.value() - 1000
            else:
                newval = self.seekSlider.value() + 1000
            self.seekSlider.setValue(newval)
            self.seekSlider.setSliderPosition(newval)
            self.mediaPlayer.setPosition(newval)
        event.accept()

    def keyPressEvent(self, event: QKeyEvent) -> None:
        if self.mediaPlayer.isVideoAvailable(
        ) or self.mediaPlayer.isAudioAvailable():
            addtime = 0
            if event.key() == Qt.Key_Left:
                addtime = -1000
            elif event.key() == Qt.Key_PageUp or event.key() == Qt.Key_Up:
                addtime = -10000
            elif event.key() == Qt.Key_Right:
                addtime = 1000
            elif event.key() == Qt.Key_PageDown or event.key() == Qt.Key_Down:
                addtime = 10000
            elif event.key() == Qt.Key_Enter:
                self.toggleFullscreen()
            elif event.key(
            ) == Qt.Key_Escape and self.videoWidget.isFullScreen():
                self.videoWidget.setFullScreen(False)
            if addtime != 0:
                newval = self.seekSlider.value() + addtime
                self.seekSlider.setValue(newval)
                self.seekSlider.setSliderPosition(newval)
                self.mediaPlayer.setPosition(newval)
        event.accept()

    def mousePressEvent(self, event: QMouseEvent) -> None:
        if event.button() == Qt.BackButton and self.cutStartAction.isEnabled():
            self.cutStart()
            event.accept()
        elif event.button(
        ) == Qt.ForwardButton and self.cutEndAction.isEnabled():
            self.cutEnd()
            event.accept()
        else:
            super(VidCutter, self).mousePressEvent(event)

    def eventFilter(self, obj: QObject, event: QEvent) -> bool:
        if event.type() == QEvent.MouseButtonRelease and isinstance(
                obj, VideoSlider):
            if obj.objectName() == 'VideoSlider' and (
                    self.mediaPlayer.isVideoAvailable()
                    or self.mediaPlayer.isAudioAvailable()):
                obj.setValue(
                    QStyle.sliderValueFromPosition(obj.minimum(),
                                                   obj.maximum(), event.x(),
                                                   obj.width()))
                self.mediaPlayer.setPosition(obj.sliderPosition())
        return QWidget.eventFilter(self, obj, event)

    @pyqtSlot(QMediaPlayer.Error)
    def handleError(self, error: QMediaPlayer.Error) -> None:
        self.unsetCursor()
        self.startNew()
        if error == QMediaPlayer.ResourceError:
            QMessageBox.critical(
                self.parent, 'Error',
                'Invalid media file detected at:<br/><br/><b>%s</b><br/><br/>%s'
                % (self.movieFilename, self.mediaPlayer.errorString()))
        else:
            QMessageBox.critical(self.parent, 'Error',
                                 self.mediaPlayer.errorString())

    def getAppPath(self) -> str:
        return ':'

    def closeEvent(self, event: QCloseEvent) -> None:
        self.parent.closeEvent(event)
Ejemplo n.º 5
0
class VidCutter(QWidget):
    def __init__(self, parent):
        super(VidCutter, self).__init__(parent)
        self.novideoWidget = QWidget(self, autoFillBackground=True)
        self.parent = parent
        self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface)
        self.videoWidget = VideoWidget(self)
        self.videoService = VideoService(self)

        QFontDatabase.addApplicationFont(
            MainWindow.get_path('fonts/DroidSansMono.ttf'))
        QFontDatabase.addApplicationFont(
            MainWindow.get_path('fonts/OpenSans.ttf'))

        fontSize = 12 if sys.platform == 'darwin' else 10
        appFont = QFont('Open Sans', fontSize, 300)
        qApp.setFont(appFont)

        self.clipTimes = []
        self.inCut = False
        self.movieFilename = ''
        self.movieLoaded = False
        self.timeformat = 'hh:mm:ss'
        self.finalFilename = ''
        self.totalRuntime = 0

        self.initIcons()
        self.initActions()

        self.toolbar = QToolBar(floatable=False,
                                movable=False,
                                iconSize=QSize(40, 36))
        self.toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        self.toolbar.setStyleSheet('''QToolBar { spacing:10px; }
            QToolBar QToolButton { border:1px solid transparent; min-width:95px; font-size:11pt; font-weight:400;
                border-radius:5px; padding:1px 2px; color:#444; }
            QToolBar QToolButton:hover { border:1px inset #6A4572; color:#6A4572; background-color:rgba(255, 255, 255, 0.85); }
            QToolBar QToolButton:pressed { border:1px inset #6A4572; color:#6A4572; background-color:rgba(255, 255, 255, 0.25); }
            QToolBar QToolButton:disabled { color:#999; }''')
        self.initToolbar()

        self.appMenu, self.cliplistMenu = QMenu(), QMenu()
        self.initMenus()

        self.seekSlider = VideoSlider(parent=self,
                                      sliderMoved=self.setPosition)

        self.initNoVideo()

        self.cliplist = QListWidget(
            sizePolicy=QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding),
            contextMenuPolicy=Qt.CustomContextMenu,
            uniformItemSizes=True,
            iconSize=QSize(100, 700),
            dragDropMode=QAbstractItemView.InternalMove,
            alternatingRowColors=True,
            customContextMenuRequested=self.itemMenu,
            dragEnabled=True)
        self.cliplist.setStyleSheet(
            'QListView { border-radius:0; border:none; border-left:1px solid #B9B9B9; '
            +
            'border-right:1px solid #B9B9B9; } QListView::item { padding:10px 0; }'
        )
        self.cliplist.setFixedWidth(185)
        self.cliplist.model().rowsMoved.connect(self.syncClipList)

        listHeader = QLabel(pixmap=QPixmap(
            MainWindow.get_path('images/clipindex.png'), 'PNG'),
                            alignment=Qt.AlignCenter)
        listHeader.setStyleSheet(
            '''padding:5px; padding-top:8px; border:1px solid #b9b9b9;
                                    background-color:qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #FFF,
                                    stop: 0.5 #EAEAEA, stop: 0.6 #EAEAEA stop:1 #FFF);'''
        )

        self.runtimeLabel = QLabel('<div align="right">00:00:00</div>',
                                   textFormat=Qt.RichText)
        self.runtimeLabel.setStyleSheet(
            '''font-family:Droid Sans Mono; font-size:10pt; color:#FFF;
                                           background:rgb(106, 69, 114) url(:images/runtime.png)
                                           no-repeat left center; padding:2px; padding-right:8px;
                                           border:1px solid #B9B9B9;''')

        self.clipindexLayout = QVBoxLayout(spacing=0)
        self.clipindexLayout.setContentsMargins(0, 0, 0, 0)
        self.clipindexLayout.addWidget(listHeader)
        self.clipindexLayout.addWidget(self.cliplist)
        self.clipindexLayout.addWidget(self.runtimeLabel)

        self.videoLayout = QHBoxLayout()
        self.videoLayout.setContentsMargins(0, 0, 0, 0)
        self.videoLayout.addWidget(self.novideoWidget)
        self.videoLayout.addLayout(self.clipindexLayout)

        self.timeCounter = QLabel('00:00:00 / 00:00:00',
                                  autoFillBackground=True,
                                  alignment=Qt.AlignCenter,
                                  sizePolicy=QSizePolicy(
                                      QSizePolicy.Expanding,
                                      QSizePolicy.Fixed))
        self.timeCounter.setStyleSheet(
            'color:#FFF; background:#000; font-family:Droid Sans Mono; font-size:10.5pt; padding:4px;'
        )

        videoplayerLayout = QVBoxLayout(spacing=0)
        videoplayerLayout.setContentsMargins(0, 0, 0, 0)
        videoplayerLayout.addWidget(self.videoWidget)
        videoplayerLayout.addWidget(self.timeCounter)

        self.videoplayerWidget = QWidget(self, visible=False)
        self.videoplayerWidget.setLayout(videoplayerLayout)

        self.muteButton = QPushButton(icon=self.unmuteIcon,
                                      flat=True,
                                      toolTip='Mute',
                                      statusTip='Toggle audio mute',
                                      iconSize=QSize(16, 16),
                                      cursor=Qt.PointingHandCursor,
                                      clicked=self.muteAudio)

        self.volumeSlider = QSlider(Qt.Horizontal,
                                    toolTip='Volume',
                                    statusTip='Adjust volume level',
                                    cursor=Qt.PointingHandCursor,
                                    value=50,
                                    minimum=0,
                                    maximum=100,
                                    sliderMoved=self.setVolume)

        self.menuButton = QPushButton(
            icon=self.menuIcon,
            flat=True,
            toolTip='Menu',
            statusTip='Media + application information',
            iconSize=QSize(24, 24),
            cursor=Qt.PointingHandCursor)
        self.menuButton.setMenu(self.appMenu)

        toolbarLayout = QHBoxLayout()
        toolbarLayout.addWidget(self.toolbar)
        toolbarLayout.setContentsMargins(2, 2, 2, 2)

        toolbarGroup = QGroupBox()
        toolbarGroup.setFlat(False)
        toolbarGroup.setCursor(Qt.PointingHandCursor)
        toolbarGroup.setLayout(toolbarLayout)

        toolbarGroup.setStyleSheet(
            '''QGroupBox { background-color:rgba(0, 0, 0, 0.1);
            border:1px inset #888; border-radius:5px; }''')

        controlsLayout = QHBoxLayout(spacing=0)
        controlsLayout.addStretch(1)
        controlsLayout.addWidget(toolbarGroup)
        controlsLayout.addStretch(1)
        controlsLayout.addWidget(self.muteButton)
        controlsLayout.addWidget(self.volumeSlider)
        controlsLayout.addSpacing(1)
        controlsLayout.addWidget(self.menuButton)

        layout = QVBoxLayout()
        layout.setContentsMargins(10, 10, 10, 4)
        layout.addLayout(self.videoLayout)
        layout.addWidget(self.seekSlider)
        layout.addSpacing(5)
        layout.addLayout(controlsLayout)
        layout.addSpacing(2)

        self.setLayout(layout)

        self.mediaPlayer.setVideoOutput(self.videoWidget)
        self.mediaPlayer.stateChanged.connect(self.mediaStateChanged)
        self.mediaPlayer.positionChanged.connect(self.positionChanged)
        self.mediaPlayer.durationChanged.connect(self.durationChanged)
        self.mediaPlayer.error.connect(self.handleError)

    def initNoVideo(self) -> None:
        novideoImage = QLabel(
            alignment=Qt.AlignCenter,
            autoFillBackground=False,
            pixmap=QPixmap(MainWindow.get_path('images/novideo.png'), 'PNG'),
            sizePolicy=QSizePolicy(QSizePolicy.Expanding,
                                   QSizePolicy.MinimumExpanding))
        novideoImage.setBackgroundRole(QPalette.Dark)
        novideoImage.setContentsMargins(0, 20, 0, 20)
        self.novideoLabel = QLabel(alignment=Qt.AlignCenter,
                                   autoFillBackground=True,
                                   sizePolicy=QSizePolicy(
                                       QSizePolicy.Expanding,
                                       QSizePolicy.Minimum))
        self.novideoLabel.setBackgroundRole(QPalette.Dark)
        self.novideoLabel.setContentsMargins(0, 20, 15, 60)
        novideoLayout = QVBoxLayout(spacing=0)
        novideoLayout.addWidget(novideoImage)
        novideoLayout.addWidget(self.novideoLabel, alignment=Qt.AlignTop)
        self.novideoMovie = QMovie(
            MainWindow.get_path('images/novideotext.gif'))
        self.novideoMovie.frameChanged.connect(self.setNoVideoText)
        self.novideoMovie.start()
        self.novideoWidget.setBackgroundRole(QPalette.Dark)
        self.novideoWidget.setLayout(novideoLayout)

    def initIcons(self) -> None:
        self.appIcon = QIcon(MainWindow.get_path('images/vidcutter.png'))
        self.openIcon = icon('fa.film',
                             color='#444',
                             color_active='#6A4572',
                             scale_factor=0.9)
        self.playIcon = icon('fa.play-circle-o',
                             color='#444',
                             color_active='#6A4572',
                             scale_factor=1.1)
        self.pauseIcon = icon('fa.pause-circle-o',
                              color='#444',
                              color_active='#6A4572',
                              scale_factor=1.1)
        self.cutStartIcon = icon('fa.scissors',
                                 scale_factor=1.15,
                                 color='#444',
                                 color_active='#6A4572')
        endicon_normal = icon('fa.scissors', scale_factor=1.15,
                              color='#444').pixmap(QSize(36, 36)).toImage()
        endicon_active = icon('fa.scissors',
                              scale_factor=1.15,
                              color='#6A4572').pixmap(QSize(36, 36)).toImage()
        self.cutEndIcon = QIcon()
        self.cutEndIcon.addPixmap(
            QPixmap.fromImage(
                endicon_normal.mirrored(horizontal=True, vertical=False)),
            QIcon.Normal, QIcon.Off)
        self.cutEndIcon.addPixmap(
            QPixmap.fromImage(
                endicon_active.mirrored(horizontal=True, vertical=False)),
            QIcon.Active, QIcon.Off)
        self.saveIcon = icon('fa.video-camera',
                             color='#6A4572',
                             color_active='#6A4572')
        self.muteIcon = QIcon(MainWindow.get_path('images/muted.png'))
        self.unmuteIcon = QIcon(MainWindow.get_path('images/unmuted.png'))
        self.upIcon = icon('ei.caret-up', color='#444')
        self.downIcon = icon('ei.caret-down', color='#444')
        self.removeIcon = icon('ei.remove', color='#B41D1D')
        self.removeAllIcon = icon('ei.trash', color='#B41D1D')
        self.successIcon = QIcon(MainWindow.get_path('images/success.png'))
        self.menuIcon = icon('fa.cog', color='#444', scale_factor=1.15)
        self.completePlayIcon = icon('fa.play', color='#444')
        self.completeOpenIcon = icon('fa.folder-open', color='#444')
        self.completeRestartIcon = icon('fa.retweet', color='#444')
        self.completeExitIcon = icon('fa.sign-out', color='#444')
        self.mediaInfoIcon = icon('fa.info-circle', color='#444')
        self.updateCheckIcon = icon('fa.cloud-download', color='#444')

    def initActions(self) -> None:
        self.openAction = QAction(self.openIcon,
                                  'Open',
                                  self,
                                  statusTip='Open media file',
                                  triggered=self.openMedia)
        self.playAction = QAction(self.playIcon,
                                  'Play',
                                  self,
                                  statusTip='Play media file',
                                  triggered=self.playMedia,
                                  enabled=False)
        self.cutStartAction = QAction(self.cutStartIcon,
                                      ' Start',
                                      self,
                                      toolTip='Start',
                                      statusTip='Set clip start marker',
                                      triggered=self.setCutStart,
                                      enabled=False)
        self.cutEndAction = QAction(self.cutEndIcon,
                                    ' End',
                                    self,
                                    toolTip='End',
                                    statusTip='Set clip end marker',
                                    triggered=self.setCutEnd,
                                    enabled=False)
        self.saveAction = QAction(self.saveIcon,
                                  'Save',
                                  self,
                                  statusTip='Save clips to a new video file',
                                  triggered=self.cutVideo,
                                  enabled=False)
        self.moveItemUpAction = QAction(
            self.upIcon,
            'Move up',
            self,
            statusTip='Move clip position up in list',
            triggered=self.moveItemUp,
            enabled=False)
        self.moveItemDownAction = QAction(
            self.downIcon,
            'Move down',
            self,
            statusTip='Move clip position down in list',
            triggered=self.moveItemDown,
            enabled=False)
        self.removeItemAction = QAction(
            self.removeIcon,
            'Remove clip',
            self,
            statusTip='Remove selected clip from list',
            triggered=self.removeItem,
            enabled=False)
        self.removeAllAction = QAction(self.removeAllIcon,
                                       'Clear list',
                                       self,
                                       statusTip='Clear all clips from list',
                                       triggered=self.clearList,
                                       enabled=False)
        self.mediaInfoAction = QAction(
            self.mediaInfoIcon,
            'Media information',
            self,
            statusTip='View current media file information',
            triggered=self.mediaInfo,
            enabled=False)
        self.updateCheckAction = QAction(
            self.updateCheckIcon,
            'Check for updates...',
            self,
            statusTip='Check for application updates',
            triggered=self.updateCheck)
        self.aboutQtAction = QAction('About Qt',
                                     self,
                                     statusTip='About Qt',
                                     triggered=qApp.aboutQt)
        self.aboutAction = QAction('About %s' % qApp.applicationName(),
                                   self,
                                   statusTip='Credits and licensing',
                                   triggered=self.aboutInfo)

    def initToolbar(self) -> None:
        self.toolbar.addAction(self.openAction)
        self.toolbar.addAction(self.playAction)
        self.toolbar.addAction(self.cutStartAction)
        self.toolbar.addAction(self.cutEndAction)
        self.toolbar.addAction(self.saveAction)

    def initMenus(self) -> None:
        self.appMenu.addAction(self.mediaInfoAction)
        self.appMenu.addAction(self.updateCheckAction)
        self.appMenu.addSeparator()
        self.appMenu.addAction(self.aboutQtAction)
        self.appMenu.addAction(self.aboutAction)

        self.cliplistMenu.addAction(self.moveItemUpAction)
        self.cliplistMenu.addAction(self.moveItemDownAction)
        self.cliplistMenu.addSeparator()
        self.cliplistMenu.addAction(self.removeItemAction)
        self.cliplistMenu.addAction(self.removeAllAction)

    @staticmethod
    def getSpacer() -> QWidget:
        spacer = QWidget()
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
        return spacer

    def setRunningTime(self, runtime: str) -> None:
        self.runtimeLabel.setText('<div align="right">%s</div>' % runtime)

    @pyqtSlot(int)
    def setNoVideoText(self) -> None:
        self.novideoLabel.setPixmap(self.novideoMovie.currentPixmap())

    def itemMenu(self, pos: QPoint) -> None:
        globalPos = self.cliplist.mapToGlobal(pos)
        self.moveItemUpAction.setEnabled(False)
        self.moveItemDownAction.setEnabled(False)
        self.removeItemAction.setEnabled(False)
        self.removeAllAction.setEnabled(False)
        index = self.cliplist.currentRow()
        if index != -1:
            if not self.inCut:
                if index > 0:
                    self.moveItemUpAction.setEnabled(True)
                if index < self.cliplist.count() - 1:
                    self.moveItemDownAction.setEnabled(True)
            if self.cliplist.count() > 0:
                self.removeItemAction.setEnabled(True)
        if self.cliplist.count() > 0:
            self.removeAllAction.setEnabled(True)
        self.cliplistMenu.exec_(globalPos)

    def moveItemUp(self) -> None:
        index = self.cliplist.currentRow()
        tmpItem = self.clipTimes[index]
        del self.clipTimes[index]
        self.clipTimes.insert(index - 1, tmpItem)
        self.renderTimes()

    def moveItemDown(self) -> None:
        index = self.cliplist.currentRow()
        tmpItem = self.clipTimes[index]
        del self.clipTimes[index]
        self.clipTimes.insert(index + 1, tmpItem)
        self.renderTimes()

    def removeItem(self) -> None:
        index = self.cliplist.currentRow()
        del self.clipTimes[index]
        if self.inCut and index == self.cliplist.count() - 1:
            self.inCut = False
            self.initMediaControls()
        self.renderTimes()

    def clearList(self) -> None:
        self.clipTimes.clear()
        self.cliplist.clear()
        self.inCut = False
        self.renderTimes()
        self.initMediaControls()

    def mediaInfo(self) -> None:
        if self.mediaPlayer.isMetaDataAvailable():
            content = '<table cellpadding="4">'
            for key in self.mediaPlayer.availableMetaData():
                val = self.mediaPlayer.metaData(key)
                if type(val) is QSize:
                    val = '%s x %s' % (val.width(), val.height())
                content += '<tr><td align="right"><b>%s:</b></td><td>%s</td></tr>\n' % (
                    key, val)
            content += '</table>'
            mbox = QMessageBox(windowTitle='Media Information',
                               windowIcon=self.parent.windowIcon(),
                               textFormat=Qt.RichText)
            mbox.setText('<b>%s</b>' % os.path.basename(
                self.mediaPlayer.currentMedia().canonicalUrl().toLocalFile()))
            mbox.setInformativeText(content)
            mbox.exec_()
        else:
            QMessageBox.critical(
                self.parent, 'MEDIA ERROR',
                '<h3>Could not probe media file.</h3>' +
                '<p>An error occurred while analyzing the media file for its metadata details.'
                +
                '<br/><br/><b>This DOES NOT mean there is a problem with the file and you should '
                + 'be able to continue using it.</b></p>')

    def aboutInfo(self) -> None:
        about_html = '''<style>
    a { color:#441d4e; text-decoration:none; font-weight:bold; }
    a:hover { text-decoration:underline; }
</style>
<div style="min-width:650px;">
<p style="font-size:26pt; font-weight:bold; color:#6A4572;">%s</p>
<p>
    <span style="font-size:13pt;"><b>Version: %s</b></span>
    <span style="font-size:10pt;position:relative;left:5px;">( %s )</span>
</p>
<p style="font-size:13px;">
    Copyright &copy; 2016 <a href="mailto:[email protected]">Pete Alexandrou</a>
    <br/>
    Website: <a href="%s">%s</a>
</p>
<p style="font-size:13px;">
    Thanks to the folks behind the <b>Qt</b>, <b>PyQt</b> and <b>FFmpeg</b>
    projects for all their hard and much appreciated work.
</p>
<p style="font-size:11px;">
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.
</p>
<p style="font-size:11px;">
    This software uses libraries from the <a href="https://www.ffmpeg.org">FFmpeg</a> project under the
    <a href="https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html">LGPLv2.1</a>
</p></div>''' % (qApp.applicationName(), qApp.applicationVersion(),
                 platform.architecture()[0], qApp.organizationDomain(),
                 qApp.organizationDomain())
        QMessageBox.about(self.parent, 'About %s' % qApp.applicationName(),
                          about_html)

    def openMedia(self) -> None:
        filename, _ = QFileDialog.getOpenFileName(self.parent,
                                                  caption='Select video',
                                                  directory=QDir.homePath())
        if filename != '':
            self.loadFile(filename)

    def loadFile(self, filename: str) -> None:
        self.movieFilename = filename
        if not os.path.exists(filename):
            return
        self.mediaPlayer.setMedia(QMediaContent(QUrl.fromLocalFile(filename)))
        self.initMediaControls(True)
        self.cliplist.clear()
        self.clipTimes = []
        self.parent.setWindowTitle(
            '%s - %s' % (qApp.applicationName(), os.path.basename(filename)))
        if not self.movieLoaded:
            self.videoLayout.replaceWidget(self.novideoWidget,
                                           self.videoplayerWidget)
            self.novideoMovie.stop()
            self.novideoMovie.deleteLater()
            self.novideoWidget.deleteLater()
            self.videoplayerWidget.show()
            self.videoWidget.show()
            self.movieLoaded = True
        if self.mediaPlayer.isVideoAvailable():
            self.mediaPlayer.setPosition(1)
        self.mediaPlayer.play()
        self.mediaPlayer.pause()

    def playMedia(self) -> None:
        if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            self.mediaPlayer.pause()
            self.playAction.setText('Play')
        else:
            self.mediaPlayer.play()
            self.playAction.setText('Pause')

    def initMediaControls(self, flag: bool = True) -> None:
        self.playAction.setEnabled(flag)
        self.saveAction.setEnabled(False)
        self.cutStartAction.setEnabled(flag)
        self.cutEndAction.setEnabled(False)
        self.mediaInfoAction.setEnabled(flag)
        if flag:
            self.seekSlider.setRestrictValue(0)

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

    def positionChanged(self, progress: int) -> None:
        self.seekSlider.setValue(progress)
        currentTime = self.deltaToQTime(progress)
        totalTime = self.deltaToQTime(self.mediaPlayer.duration())
        self.timeCounter.setText('%s / %s' % (currentTime.toString(
            self.timeformat), totalTime.toString(self.timeformat)))

    @pyqtSlot()
    def mediaStateChanged(self) -> None:
        if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            self.playAction.setIcon(self.pauseIcon)
        else:
            self.playAction.setIcon(self.playIcon)

    def durationChanged(self, duration: int) -> None:
        self.seekSlider.setRange(0, duration)

    def muteAudio(self) -> None:
        if self.mediaPlayer.isMuted():
            self.muteButton.setIcon(self.unmuteIcon)
            self.muteButton.setToolTip('Mute')
        else:
            self.muteButton.setIcon(self.muteIcon)
            self.muteButton.setToolTip('Unmute')
        self.mediaPlayer.setMuted(not self.mediaPlayer.isMuted())

    def setVolume(self, volume: int) -> None:
        self.mediaPlayer.setVolume(volume)

    def toggleFullscreen(self) -> None:
        self.videoWidget.setFullScreen(not self.videoWidget.isFullScreen())

    def setCutStart(self) -> None:
        self.clipTimes.append([
            self.deltaToQTime(self.mediaPlayer.position()), '',
            self.captureImage()
        ])
        self.cutStartAction.setDisabled(True)
        self.cutEndAction.setEnabled(True)
        self.seekSlider.setRestrictValue(self.seekSlider.value(), True)
        self.inCut = True
        self.renderTimes()

    def setCutEnd(self) -> None:
        item = self.clipTimes[len(self.clipTimes) - 1]
        selected = self.deltaToQTime(self.mediaPlayer.position())
        if selected.__lt__(item[0]):
            QMessageBox.critical(
                self.parent, 'Invalid END Time',
                'The clip end time must come AFTER it\'s start time. Please try again.'
            )
            return
        item[1] = selected
        self.cutStartAction.setEnabled(True)
        self.cutEndAction.setDisabled(True)
        self.seekSlider.setRestrictValue(0, False)
        self.inCut = False
        self.renderTimes()

    @pyqtSlot(QModelIndex, int, int, QModelIndex, int)
    def syncClipList(self, parent: QModelIndex, start: int, end: int,
                     destination: QModelIndex, row: int) -> None:
        if start < row:
            index = row - 1
        else:
            index = row
        clip = self.clipTimes.pop(start)
        self.clipTimes.insert(index, clip)

    def renderTimes(self) -> None:
        self.cliplist.clear()
        if len(self.clipTimes) > 4:
            self.cliplist.setFixedWidth(200)
        else:
            self.cliplist.setFixedWidth(185)
        self.totalRuntime = 0
        for item in self.clipTimes:
            endItem = ''
            if type(item[1]) is QTime:
                endItem = item[1].toString(self.timeformat)
                self.totalRuntime += item[0].msecsTo(item[1])
            listitem = QListWidgetItem()
            listitem.setTextAlignment(Qt.AlignVCenter)
            if type(item[2]) is QPixmap:
                listitem.setIcon(QIcon(item[2]))
            self.cliplist.addItem(listitem)
            marker = QLabel(
                '''<style>b { font-size:7pt; } p { margin:2px 5px; }</style>
                            <p><b>START</b><br/>%s<br/><b>END</b><br/>%s</p>'''
                % (item[0].toString(self.timeformat), endItem))
            marker.setStyleSheet('border:none;')
            self.cliplist.setItemWidget(listitem, marker)
            listitem.setFlags(Qt.ItemIsSelectable | Qt.ItemIsDragEnabled
                              | Qt.ItemIsEnabled)
        if len(self.clipTimes) and not self.inCut:
            self.saveAction.setEnabled(True)
        if self.inCut or len(self.clipTimes) == 0 or not type(
                self.clipTimes[0][1]) is QTime:
            self.saveAction.setEnabled(False)
        self.setRunningTime(
            self.deltaToQTime(self.totalRuntime).toString(self.timeformat))

    @staticmethod
    def deltaToQTime(millisecs: int) -> QTime:
        secs = millisecs / 1000
        return QTime((secs / 3600) % 60, (secs / 60) % 60, secs % 60,
                     (secs * 1000) % 1000)

    def captureImage(self) -> QPixmap:
        frametime = self.deltaToQTime(self.mediaPlayer.position()).toString(
            self.timeformat)
        inputfile = self.mediaPlayer.currentMedia().canonicalUrl().toLocalFile(
        )
        imagecap = self.videoService.capture(inputfile, frametime)
        if type(imagecap) is QPixmap:
            return imagecap

    def cutVideo(self) -> bool:
        clips = len(self.clipTimes)
        filename, filelist = '', []
        source = self.mediaPlayer.currentMedia().canonicalUrl().toLocalFile()
        _, sourceext = os.path.splitext(source)
        if clips > 0:
            self.finalFilename, _ = QFileDialog.getSaveFileName(
                self.parent, 'Save video', source,
                'Video files (*%s)' % sourceext)
            if self.finalFilename == '':
                return False
            qApp.setOverrideCursor(Qt.BusyCursor)
            self.saveAction.setDisabled(True)
            self.showProgress(clips)
            file, ext = os.path.splitext(self.finalFilename)
            index = 1
            self.progress.setLabelText('Cutting media files...')
            qApp.processEvents()
            for clip in self.clipTimes:
                duration = self.deltaToQTime(clip[0].msecsTo(
                    clip[1])).toString(self.timeformat)
                filename = '%s_%s%s' % (file, '{0:0>2}'.format(index), ext)
                filelist.append(filename)
                self.videoService.cut(source, filename,
                                      clip[0].toString(self.timeformat),
                                      duration)
                index += 1
            if len(filelist) > 1:
                self.joinVideos(filelist, self.finalFilename)
            else:
                QFile.remove(self.finalFilename)
                QFile.rename(filename, self.finalFilename)
            self.progress.setLabelText('Complete...')
            self.progress.setValue(100)
            qApp.processEvents()
            self.progress.close()
            self.progress.deleteLater()
            qApp.restoreOverrideCursor()
            self.complete()
            return True
        return False

    def joinVideos(self, joinlist: list, filename: str) -> None:
        listfile = os.path.normpath(
            os.path.join(os.path.dirname(joinlist[0]), '.vidcutter.list'))
        fobj = open(listfile, 'w')
        for file in joinlist:
            fobj.write('file \'%s\'\n' % file.replace("'", "\\'"))
        fobj.close()
        self.videoService.join(listfile, filename)
        QFile.remove(listfile)
        for file in joinlist:
            if os.path.isfile(file):
                QFile.remove(file)

    def updateCheck(self) -> None:
        self.updater = Updater()
        self.updater.updateAvailable.connect(self.updateHandler)
        self.updater.start()

    def updateHandler(self, updateExists: bool, version: str = None):
        if updateExists:
            if Updater.notify_update(self, version) == QMessageBox.AcceptRole:
                self.updater.install_update(self)
        else:
            Updater.notify_no_update(self)

    def showProgress(self,
                     steps: int,
                     label: str = 'Analyzing media...') -> None:
        self.progress = QProgressDialog(label,
                                        None,
                                        0,
                                        steps,
                                        self.parent,
                                        windowModality=Qt.ApplicationModal,
                                        windowIcon=self.parent.windowIcon(),
                                        minimumDuration=0,
                                        minimumWidth=500)
        self.progress.show()
        for i in range(steps):
            self.progress.setValue(i)
            qApp.processEvents()
            time.sleep(1)

    def complete(self) -> None:
        info = QFileInfo(self.finalFilename)
        mbox = QMessageBox(windowTitle='VIDEO PROCESSING COMPLETE',
                           minimumWidth=500,
                           textFormat=Qt.RichText)
        mbox.setText(
            '''
    <style>
        table.info { margin:6px; padding:4px 15px; }
        td.label { font-weight:bold; font-size:10.5pt; text-align:right; }
        td.value { font-size:10.5pt; }
    </style>
    <table class="info" cellpadding="4" cellspacing="0">
        <tr>
            <td class="label"><b>File:</b></td>
            <td class="value" nowrap>%s</td>
        </tr>
        <tr>
            <td class="label"><b>Size:</b></td>
            <td class="value">%s</td>
        </tr>
        <tr>
            <td class="label"><b>Length:</b></td>
            <td class="value">%s</td>
        </tr>
    </table><br/>''' %
            (QDir.toNativeSeparators(
                self.finalFilename), self.sizeof_fmt(int(info.size())),
             self.deltaToQTime(self.totalRuntime).toString(self.timeformat)))
        play = mbox.addButton('Play', QMessageBox.AcceptRole)
        play.setIcon(self.completePlayIcon)
        play.clicked.connect(self.openResult)
        fileman = mbox.addButton('Open', QMessageBox.AcceptRole)
        fileman.setIcon(self.completeOpenIcon)
        fileman.clicked.connect(self.openFolder)
        end = mbox.addButton('Exit', QMessageBox.AcceptRole)
        end.setIcon(self.completeExitIcon)
        end.clicked.connect(self.close)
        new = mbox.addButton('Restart', QMessageBox.AcceptRole)
        new.setIcon(self.completeRestartIcon)
        new.clicked.connect(self.parent.restart)
        mbox.setDefaultButton(new)
        mbox.setEscapeButton(new)
        mbox.adjustSize()
        mbox.exec_()

    def sizeof_fmt(self, num: float, suffix: chr = 'B') -> str:
        for unit in ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']:
            if abs(num) < 1024.0:
                return "%3.1f%s%s" % (num, unit, suffix)
            num /= 1024.0
        return "%.1f%s%s" % (num, 'Y', suffix)

    @pyqtSlot()
    def openFolder(self) -> None:
        self.openResult(pathonly=True)

    @pyqtSlot(bool)
    def openResult(self, pathonly: bool = False) -> None:
        self.parent.restart()
        if len(self.finalFilename) and os.path.exists(self.finalFilename):
            target = self.finalFilename if not pathonly else os.path.dirname(
                self.finalFilename)
            QDesktopServices.openUrl(QUrl.fromLocalFile(target))

    @pyqtSlot()
    def startNew(self) -> None:
        qApp.restoreOverrideCursor()
        self.clearList()
        self.seekSlider.setValue(0)
        self.seekSlider.setRange(0, 0)
        self.mediaPlayer.setMedia(QMediaContent())
        self.initNoVideo()
        self.videoLayout.replaceWidget(self.videoplayerWidget,
                                       self.novideoWidget)
        self.initMediaControls(False)
        self.parent.setWindowTitle('%s' % qApp.applicationName())

    def wheelEvent(self, event: QWheelEvent) -> None:
        if self.mediaPlayer.isVideoAvailable(
        ) or self.mediaPlayer.isAudioAvailable():
            if event.angleDelta().y() > 0:
                newval = self.seekSlider.value() - 1000
            else:
                newval = self.seekSlider.value() + 1000
            self.seekSlider.setValue(newval)
            self.seekSlider.setSliderPosition(newval)
            self.mediaPlayer.setPosition(newval)
        event.accept()

    def keyPressEvent(self, event: QKeyEvent) -> None:
        if self.mediaPlayer.isVideoAvailable(
        ) or self.mediaPlayer.isAudioAvailable():
            addtime = 0
            if event.key() == Qt.Key_Left:
                addtime = -1000
            elif event.key() == Qt.Key_PageUp or event.key() == Qt.Key_Up:
                addtime = -10000
            elif event.key() == Qt.Key_Right:
                addtime = 1000
            elif event.key() == Qt.Key_PageDown or event.key() == Qt.Key_Down:
                addtime = 10000
            elif event.key() == Qt.Key_Enter:
                self.toggleFullscreen()
            elif event.key(
            ) == Qt.Key_Escape and self.videoWidget.isFullScreen():
                self.videoWidget.setFullScreen(False)
            if addtime != 0:
                newval = self.seekSlider.value() + addtime
                self.seekSlider.setValue(newval)
                self.seekSlider.setSliderPosition(newval)
                self.mediaPlayer.setPosition(newval)
        event.accept()

    def mousePressEvent(self, event: QMouseEvent) -> None:
        if event.button() == Qt.BackButton and self.cutStartAction.isEnabled():
            self.setCutStart()
            event.accept()
        elif event.button(
        ) == Qt.ForwardButton and self.cutEndAction.isEnabled():
            self.setCutEnd()
            event.accept()
        else:
            super(VidCutter, self).mousePressEvent(event)

    @pyqtSlot(QMediaPlayer.Error)
    def handleError(self, error: QMediaPlayer.Error) -> None:
        qApp.restoreOverrideCursor()
        self.startNew()
        if error == QMediaPlayer.ResourceError:
            QMessageBox.critical(
                self.parent, 'INVALID MEDIA',
                'Invalid media file detected at:<br/><br/><b>%s</b><br/><br/>%s'
                % (self.movieFilename, self.mediaPlayer.errorString()))
        else:
            QMessageBox.critical(self.parent, 'ERROR NOTIFICATION',
                                 self.mediaPlayer.errorString())

    def closeEvent(self, event: QCloseEvent) -> None:
        self.parent.closeEvent(event)
Ejemplo n.º 6
0
class MediaPlayer(QMainWindow, Form):
    def __init__(self):
        Form.__init__(self)
        QMainWindow.__init__(self)
        self.setupUi(self)
        # Video Part
        self.player = QMediaPlayer(None, QMediaPlayer.VideoSurface)
        self.player.setVideoOutput(self.videowidget)
        self.setWindowTitle(" Media Player")
        self.setAcceptDrops(True)

        # Define some Events
        self.videowidget.mouseDoubleClickEvent = self.Doubleclick_mouse
        self.wheelEvent = self.Scroll_mouse
        self.mouseReleaseEvent = self.MainWindow_Event
        self.videowidget.mouseMoveEvent = self.MousePos
        self.resizeEvent = self.main_size_Change

        self.lineEdit_Bookmark.setVisible(False)
        self.lineEdit_Bookmark.returnPressed.connect(self.save_Bookmarks)

        # threads part
        self.tag_thread = None
        self.search_Thread = None
        self.SearchAnimation = None
        self.BookMarkAnimation = None

        # Create Tags
        self.allTag = {}
        self.tag_Path = None

        # create Volume Slide
        self.Slider_Volume = Slider(self)
        self.Slider_Volume.setOrientation(Qt.Horizontal)
        self.horizontalLayout_3.addWidget(self.Slider_Volume, 0)
        self.Slider_Volume.setMaximumWidth(100)
        self.Slider_Volume.setMaximumHeight(30)

        # create Play Slider
        self.Slider_Play = Slider(self)
        self.Slider_Play.setOrientation(Qt.Horizontal)
        self.Slider_Play.resize(644, 22)
        self.horizontalLayout_4.addWidget(self.label_Time)
        self.horizontalLayout_4.addWidget(self.Slider_Play)
        self.horizontalLayout_4.addWidget(self.label_Duration)

        # To Apply initial Theme
        self.Setting = Setting.SettingWindow(self)
        Theme_module.Theme_apply(self.Setting)

        #Create Help
        self.helpW = Help.helpWindow(self)

        # PushButttons
        self.pushButton_Start.setEnabled(False)
        self.pushButton_Start.clicked.connect(self.start)

        self.pushButton_volume.setEnabled(False)
        self.pushButton_volume.clicked.connect(self.volumeOnOff)

        self.pushButton_stop.setEnabled(False)
        self.pushButton_stop.clicked.connect(self.stop)
        self.pushButton_stop.setToolTip("Stop (Ctrl+Z)")

        self.pushButton_next.setEnabled(False)
        self.pushButton_next.clicked.connect(self.next)
        self.pushButton_next.setToolTip("Next (PgUp)")

        self.pushButton_previous.setEnabled(False)
        self.pushButton_previous.clicked.connect(self.previous)
        self.pushButton_previous.setToolTip("Previous (PgDn)")

        self.pushButton_open.clicked.connect(self.Load_video)
        self.pushButton_open.setToolTip("Open (Ctrl+O)")

        self.pushButton_Search.clicked.connect(self.sch_icon_Event)
        self.pushButton_Search.setToolTip("Search (Ctrl+S)")

        self.pushButton_BookMark.setVisible(False)
        self.pushButton_BookMark.clicked.connect(self.add_BookMarks)
        self.pushButton_BookMark.setToolTip("Add BookMark (Ctrl+B)")

        self.pushButton_Setting.clicked.connect(self.Settingshow)
        self.pushButton_Setting.setToolTip("Setting")

        self.pushButton_Playlist.clicked.connect(self.Play_list)
        self.pushButton_Playlist.setToolTip("Play list")
        self.PlaylistW = Playlist.PlaylistWindow(self)

        # Create Tags of file Dockwidget
        self.pushButton_Tag_of_file.clicked.connect(self.Show_Tags_of_file)
        self.pushButton_Tag_of_file.setToolTip("Tags of file")
        self.DockWidget_Tags_of_file = QDockWidget("Tags of file", self)
        self.DockWidget_Tags_of_file.setVisible(False)
        self.DockWidget_Tags_of_file.setMinimumWidth(150)
        self.ComboBox_Tags_of_file = QComboBox(self)
        self.ListWidget_Tags_of_file = QListWidget()
        widget = QWidget()  # Create Widget for Tags of file DockWidget
        layout = QVBoxLayout()  # Create Layout for Tags of file DockWidget
        # Add Listwiget and ComboBox to layout
        layout.addWidget(self.ComboBox_Tags_of_file)
        layout.addWidget(self.ListWidget_Tags_of_file)
        widget.setLayout(layout)  # Set layout on the widget
        # set Widget on Tags of file DockWidget
        self.DockWidget_Tags_of_file.setWidget(widget)
        self.addDockWidget(Qt.RightDockWidgetArea,
                           self.DockWidget_Tags_of_file)
        self.ComboBox_Tags_of_file.activated.connect(self.ListWidget_Tag)
        self.ListWidget_Tags_of_file.itemActivated.connect(self.GoToTagtime)

        # Slider Play
        self.Slider_Play.setRange(0, 0)
        self.player.positionChanged.connect(self.Position_changed)
        self.player.durationChanged.connect(self.Duration_changed)
        self.Slider_Play.setUP_Slider.connect(self.Set_Position)
        self.Slider_Play.moveMent_Position.connect(self.slider_play_pos)

        # Label Time
        self.Slider_play_label = QLabel("Time", parent=self)
        self.Slider_play_label.resize(50, 20)
        self.Slider_play_label.setAlignment(Qt.AlignCenter)
        self.Slider_play_label.setVisible(False)

        # Slider Volume
        self.Slider_Volume.setRange(0, 0)
        self.Slider_Volume.setUP_Slider.connect(self.Set_volume)
        self.Slider_Volume.moveMent_Position.connect(self.slider_volume_pos)

        # Label Volume
        self.Slider_Volume_label = QLabel("Volume", parent=self)
        self.Slider_Volume_label.resize(30, 20)
        self.Slider_Volume_label.setAlignment(Qt.AlignCenter)
        self.Slider_Volume_label.setVisible(False)

        # Create listWidget for search part
        self.sch_listWidget = QListWidget(self)
        self.sch_listWidget.setVisible(False)
        self.search_lineEdit.setVisible(False)
        # Search signals
        self.sch_listWidget.itemDoubleClicked.connect(
            self.item_searchlist_Event)
        self.search_lineEdit.textChanged.connect(self.search_Tag)

        # Define Action for Tool Bar
        self.actionOpen.triggered.connect(self.Load_video)
        self.actionChange_Password.triggered.connect(self.menuBarAccout)
        self.actionDelete_Account.triggered.connect(self.menuBarAccout)

        self.actionHelp.triggered.connect(self.Help)
        self.actionLogOut.triggered.connect(self.Logout)
        self.actionExit.triggered.connect(lambda: self.close())

        self.actionFullScreen.triggered.connect(self.fullscreen)
        self.actionSpeed.triggered.connect(self.menuBarSpeed)
        self.actionThme.triggered.connect(self.menuBarTheme)

        self.actionCreate_Tag.triggered.connect(self.menuBarCreateTag)
        self.actionOpen_Tags.triggered.connect(self.openTags)
        self.actionEdit_Tag.triggered.connect(self.menuBarEditTag)
        self.actionClose_Tag.triggered.connect(self.menuBarCloseTag)

        # Shortcut
        QShortcut(QKeySequence('Ctrl+B'),
                  self).activated.connect(self.add_BookMarks)
        QShortcut(QKeySequence('Ctrl+Z'), self).activated.connect(self.stop)
        QShortcut(QKeySequence('Ctrl+M'),
                  self).activated.connect(self.volumeOnOff)
        QShortcut(QKeySequence('Ctrl+S'),
                  self).activated.connect(self.sch_icon_Event)
        QShortcut(QKeySequence('Ctrl+O'),
                  self).activated.connect(self.Load_video)

        # For FullScreen part
        self.firstTime_fullscreen = True

        # close event to stop running thread
        self.closeEvent = self.Close

    # KeyPress Event
    def keyPressEvent(self, event):
        if event.key() == Qt.Key_PageDown and self.pushButton_next.isEnabled():
            self.next()
        if event.key() == Qt.Key_PageUp and self.pushButton_previous.isEnabled(
        ):
            self.previous()
        if event.key() == Qt.Key_F5:
            self.fullscreen()
        if event.key() == Qt.Key_6:
            self.Move_Slider_play(self.width() * 4500 / self.player.duration())
        if event.key() == Qt.Key_4:
            self.Move_Slider_play(
                (-1 * self.width() * 4500 / self.player.duration()))
        if event.key() == Qt.Key_8:
            self.Move_Slider_volume(+1)
        if event.key() == Qt.Key_2:
            self.Move_Slider_volume(-1)

    def menuBarTheme(self):
        # Open Theme Tab of Setting Window
        self.Setting.Account_changing = False
        self.Settingshow()
        self.Setting.Tab.setCurrentIndex(0)

    def Help(self):
        self.helpW.show()

    def menuBarSpeed(self):
        # Open Speed Tab of Setting Window
        self.Setting.Account_changing = False
        self.Settingshow()
        self.Setting.Tab.setCurrentIndex(1)

    def menuBarEditTag(self):
        # Open Tag Tab of Setting Window
        self.Setting.Account_changing = False
        self.Settingshow()
        self.Setting.Tab.setCurrentIndex(2)

    def menuBarAccout(self):
        # Open Account Tab of Setting Window
        self.Setting.Account_changing = False
        self.Settingshow()
        self.Setting.Tab.setCurrentIndex(3)

    def menuBarCreateTag(self):
        # create new Tag file
        if self.tag_Path:
            self.confirmCloseTag = confrimWin(
                self,
                Title="Create Tag",
                Text="Are you sure to close current tag and create new one")
            self.actionCreate_Tag.setEnabled(False)
            self.confirmCloseTag.show()
        else:
            dialog = QFileDialog(self, 'File tag', directory=os.getcwd())
            _path = dialog.getSaveFileName(filter="*.csv")[0]
            try:
                if _path:
                    self.tag_Path = _path
                    open(self.tag_Path, "w")
            except:
                pass

    def menuBarCloseTag(self):
        # close current tags
        if self.tag_Path:
            self.confirmCloseTag = confrimWin(
                self,
                Title="Close Tag",
                Text="Are you sure to close current tag")
            self.confirmCloseTag.show()
            self.actionClose_Tag.setEnabled(False)
        else:
            self.confirmCloseTag = confrimWin(self,
                                              Title="Warning",
                                              Text="There is no tag to close")
            self.confirmCloseTag.show()
            self.actionClose_Tag.setEnabled(False)

    def fullscreen(self):
        self.DockWidget_Tags_of_file.setVisible(False)
        self.Slider_play_label.setVisible(False)
        self.Slider_Volume_label.setVisible(False)

        Full_screen.fullscreen(self)

    # Define some Events
    def MousePos(self, position):
        if self.isFullScreen():
            Full_screen.MousePosition(self, position)
        self.Slider_play_label.setVisible(False)
        self.Slider_Volume_label.setVisible(False)

    def Doubleclick_mouse(self, position):
        if self.pushButton_Start.isEnabled():
            self.start()

    def Scroll_mouse(self, event):
        if self.player.isAudioAvailable() or self.player.isVideoAvailable():
            self.Set_volume(
                int(self.player.volume() + event.angleDelta().y() / 120))

    def Move_Slider_play(self, move):
        if self.player.isAudioAvailable() or self.player.isVideoAvailable():
            Now = self.player.position() * self.Slider_Play.width(
            ) / self.player.duration()
            self.Set_Position(Now + move)

    def Move_Slider_volume(self, move):
        if self.player.isAudioAvailable() or self.player.isVideoAvailable():
            self.Set_volume(self.player.volume() + move)

    def Settingshow(self):
        # To show Setting window
        self.Setting.Tab.setCurrentIndex(0)
        self.Setting.lineEdit_CurrentPass.clear()
        self.Setting.lineEdit_NewPass.clear()
        self.Setting.lineEdit_ReNewpass.clear()
        self.Setting.label_finish.setVisible(False)
        self.Setting.label_NotMatch.setVisible(False)
        self.Setting.label_PassLong.setVisible(False)
        self.Setting.label_OldPass.setVisible(False)
        self.Setting.label_Wait.setVisible(False)
        self.Setting.label_Error.setVisible(False)
        self.Setting.Account_changing = False
        self.Setting.show()

    def start(self):
        # Start and Pause the player
        if self.player.state() == QMediaPlayer.PlayingState:  # Stop
            self.player.pause()
            self.pushButton_Start.setEnabled(True)
            self.pushButton_Start.setIcon(QIcon('./Icons/play.png'))
            self.pushButton_Start.setToolTip("Play")

        else:  # Start
            self.player.play()
            self.pushButton_Start.setEnabled(True)
            self.pushButton_Start.setIcon(QIcon('./Icons/pause.png'))
            self.pushButton_Start.setToolTip("Pause")
        self.pushButton_Start.setFocus(True)

    def stop(self):
        # Stop player
        if self.player.isAudioAvailable() or self.player.isVideoAvailable():
            self.player.stop()
            self.pushButton_next.setEnabled(False)
            self.pushButton_previous.setEnabled(False)
            self.pushButton_volume.setEnabled(False)
            self.pushButton_Start.setEnabled(False)
            self.pushButton_stop.setEnabled(False)
            self.pushButton_BookMark.setVisible(False)
            self.label_Duration.setText("00:00:00")
            self.label_Time.setText("00:00:00")
            self.Slider_Volume.setEnabled(False)

    def next(self):
        # Play next file from playlist
        # If the playing file is in the playlist
        if self.windowTitle()[16:] in self.PlaylistW.Files.keys():
            self.PlaylistW.listWidget_Playlist.setCurrentRow(
                list(self.PlaylistW.Files.keys()).index(self.windowTitle()
                                                        [16:]) + 1)
        else:  # If the playing file is not in the playlist
            self.PlaylistW.listWidget_Playlist.setCurrentRow(
                self.PlaylistW.listWidget_Playlist.currentRow() + 1)

        self.PlaylistW.spliter = len(
            str(self.PlaylistW.listWidget_Playlist.currentRow() + 1)) + 3
        self.player.setMedia(
            QMediaContent(
                QUrl.fromLocalFile(self.PlaylistW.Files[
                    self.PlaylistW.listWidget_Playlist.currentItem().text()
                    [self.PlaylistW.spliter:]])))
        # Change title
        self.setWindowTitle(
            f" Media Player - {self.PlaylistW.listWidget_Playlist.currentItem().text()[self.PlaylistW.spliter:]}"
        )
        currentText = self.PlaylistW.listWidget_Playlist.currentItem().text(
        )[self.PlaylistW.spliter:]
        index = self.ComboBox_Tags_of_file.findText(currentText)
        self.ComboBox_Tags_of_file.setCurrentIndex(index)
        self.set_TagonListwidget(".".join(currentText.split(".")[:-1]))
        self.start()
        # To handle Next button
        if self.PlaylistW.listWidget_Playlist.currentRow(
        ) == self.PlaylistW.listWidget_Playlist.count() - 1:
            self.pushButton_next.setEnabled(False)

        self.pushButton_previous.setEnabled(True)

    def previous(self):
        # Play previous file from playlist
        # If the playing file is in the playlist
        if self.windowTitle()[16:] in self.PlaylistW.Files.keys():
            self.PlaylistW.listWidget_Playlist.setCurrentRow(
                list(self.PlaylistW.Files.keys()).index(self.windowTitle()
                                                        [16:]) - 1)
        else:  # If the playing file is not in the playlist
            self.PlaylistW.listWidget_Playlist.setCurrentRow(
                self.PlaylistW.listWidget_Playlist.currentRow())

        self.PlaylistW.spliter = len(
            str(self.PlaylistW.listWidget_Playlist.currentRow() + 1)) + 3

        self.player.setMedia(
            QMediaContent(
                QUrl.fromLocalFile(self.PlaylistW.Files[
                    self.PlaylistW.listWidget_Playlist.currentItem().text()
                    [self.PlaylistW.spliter:]])))
        self.setWindowTitle(
            f" Media Player - {self.PlaylistW.listWidget_Playlist.currentItem().text()[self.PlaylistW.spliter:]}"
        )
        currentText = self.PlaylistW.listWidget_Playlist.currentItem().text(
        )[self.PlaylistW.spliter:]
        index = self.ComboBox_Tags_of_file.findText(currentText)
        self.ComboBox_Tags_of_file.setCurrentIndex(index)
        self.set_TagonListwidget(".".join(currentText.split(".")[:-1]))
        self.start()

        # To handle previous button
        if not self.PlaylistW.listWidget_Playlist.currentRow():
            self.pushButton_previous.setEnabled(False)
        self.pushButton_next.setEnabled(True)

    def Load_video(self, filepath=None):
        # Load Files of directory
        # just mp4 ,mkv , mp3
        if not filepath:
            try:
                # To read initial Directory from csvfile
                with open("./PlayListPart/Filepath.csv") as file:
                    initial_FilePath = file.read()
                    file_path, _ = QFileDialog.getOpenFileName(
                        self,
                        "Open video",
                        directory=initial_FilePath,
                        filter='*.mp4 *.mkv *.mp3')
            except:
                initial_FilePath = os.getcwd()
                file_path, _ = QFileDialog.getOpenFileName(
                    self,
                    "Open video",
                    directory=initial_FilePath,
                    filter='*.mp4 *.mkv *.mp3')
        else:
            initial_FilePath = None
            file_path = filepath
        if file_path:
            # To write initial Directory in csvfile
            if file_path.replace(file_path.split("/")[-1],
                                 "") != initial_FilePath:
                with open("./PlayListPart/Filepath.csv", 'w') as file:
                    file.write(file_path.replace(file_path.split("/")[-1], ""))
            self.player.setMedia(QMediaContent(QUrl.fromLocalFile(file_path)))
            self.start()
            self.pushButton_volume.setEnabled(True)
            self.pushButton_volume.setIcon(QIcon('./Icons/unmute.png'))
            self.pushButton_volume.setToolTip("Mute (Ctrl+M)")
            if not self.isFullScreen():
                self.pushButton_BookMark.setVisible(True)
            self.pushButton_stop.setEnabled(True)
            # Handle Volume Slider
            self.Slider_Volume.setEnabled(True)
            self.Slider_Volume.setRange(0, self.player.volume())
            self.Slider_Volume.setValue(60)
            self.player.setVolume(60)

            # Create Playlist
            self.Setting.comboBox_Tag.clear()
            self.ComboBox_Tags_of_file.clear()
            self.PlaylistW.Create_Playlist(file_path)
            self.set_TagonListwidget(".".join(
                self.windowTitle()[16:].split(".")[:-1]))

    def Position_changed(self, position):
        if position > self.player.duration():
            position = self.player.duration()
        if self.player.isAudioAvailable() or self.player.isVideoAvailable():
            hour = position // (1000 * 3600)
            minute = (position % (1000 * 3600)) // (60 * 1000)
            second = ((position % (1000 * 3600)) % (60 * 1000)) // 1000
            # Show Time
            self.label_Time.setText(
                f'{str(hour).zfill(2)}:{str(minute).zfill(2)}:{str(second).zfill(2)}'
            )
        # Automatic Next
        if self.player.duration() and position == self.player.duration():
            if self.pushButton_next.isEnabled():
                self.next()
            else:
                self.stop()
        self.Slider_Play.setValue(position)

    def Duration_changed(self, duration):
        # To set file duration
        self.Slider_Play.setRange(0, duration)
        # convert millsec to xx:xx:xx format
        hour = duration // (1000 * 3600)
        minute = (duration % (1000 * 3600)) // (60 * 1000)
        second = ((duration % (1000 * 3600)) % (60 * 1000)) // 1000
        self.label_Duration.setText(
            f'{str(hour).zfill(2)}:{str(minute).zfill(2)}:{str(second).zfill(2)}'
        )

        self.Slider_Volume.setEnabled(True)

    def Set_Position(self, position):
        if self.Slider_Play.width() - 1 <= position:
            position = self.Slider_Play.width() - 1
            self.player.pause()
            self.pushButton_Start.setEnabled(True)
            self.pushButton_Start.setIcon(QIcon('./Icons/play.png'))
            self.pushButton_Start.setToolTip("Paly")

        # if not (position < 0 and position > self.Slider_Play.width()):
        position = int(self.player.duration() *
                       (position / self.Slider_Play.width()))
        self.Slider_Play.setValue(position)
        self.player.setPosition(position)

    def slider_play_pos(self, position):
        if self.player.isAudioAvailable() or self.player.isVideoAvailable():
            # Convert Width of Slider to time
            Time = int(self.player.duration() * position /
                       self.Slider_Play.width())
            # convert millsec to xx:xx:xx format
            hour = Time // (1000 * 3600)
            minute = (Time % (1000 * 3600)) // (60 * 1000)
            second = ((Time % (1000 * 3600)) % (60 * 1000)) // 1000
            self.Slider_play_label.setText(
                f'{str(hour).zfill(2)}:{str(minute).zfill(2)}:{str(second).zfill(2)}'
            )
            if self.isFullScreen():
                self.Slider_play_label.move(position + self.Slider_Play.x(),
                                            self.height() - 65)
            else:
                self.Slider_play_label.move(position + self.Slider_Play.x(),
                                            self.height() - 80)
            self.Slider_play_label.setVisible(True)

    def slider_volume_pos(self, position):
        if self.player.isAudioAvailable() or self.player.isVideoAvailable():
            # Convert Width of Slider to volume
            volume = int(100 * position / self.Slider_Volume.width())
            self.Slider_Volume_label.setText(f'{volume}%')
            if self.isFullScreen():
                self.Slider_Volume_label.move(
                    position + self.Slider_Volume.x() - 5,
                    self.height() - 37)
            else:
                self.Slider_Volume_label.move(
                    position + self.Slider_Volume.x() - 5,
                    self.height() - 48)
            self.Slider_Volume_label.setVisible(True)

    def Set_volume(self, volume):
        if self.player.isAudioAvailable() or self.player.isVideoAvailable():
            # Volume must be between 0 , 100
            if 100 <= volume:
                volume = 100
            elif volume <= 0:
                volume = 0

            if not volume:
                self.player.setMuted(True)
                self.pushButton_volume.setIcon(QIcon('./Icons/mute.png'))
                self.pushButton_volume.setToolTip("UnMute (Ctrl+M)")

            else:
                self.player.setMuted(False)
                self.pushButton_volume.setIcon(QIcon('./Icons/unmute.png'))
                self.pushButton_volume.setToolTip("Mute (Ctrl+M)")

            self.Slider_Volume.setValue(volume)
            self.player.setVolume(volume)

    def volumeOnOff(self):
        """Mute and unmute"""
        if self.player.isAudioAvailable() or self.player.isVideoAvailable():
            if self.player.isMuted():
                self.player.setMuted(False)
                self.pushButton_volume.setIcon(QIcon('./Icons/unmute.png'))
                self.pushButton_volume.setToolTip("Mute (Ctrl+M)")
            else:
                self.player.setMuted(True)
                self.pushButton_volume.setIcon(QIcon('./Icons/mute.png'))
                self.pushButton_volume.setToolTip("UnMute (Ctrl+M)")

    # save bookmarks and updatae tag list widget
    def save_Bookmarks(self):
        if not self.tag_Path:
            error_addBookmark = confrimWin(
                self,
                Title="Warning",
                Text="First select or create a tag file and try again")
            error_addBookmark.show()
            return False
        try:
            # use add bookmark function to add bookmarks in tag part
            add_Bookmark(
                self.lineEdit_Bookmark.text() + "#" +
                tc.millis_to_format(self.player.position()),
                ".".join(self.windowTitle()[16:].split(".")[:-1]),
                self.tag_Path)
            # there isn't any tags for movie we want to add bookmark it
            if not ".".join(
                    self.windowTitle()[16:].split(".")[:-1]) in self.allTag:
                self.allTag.update(
                    {".".join(self.windowTitle()[16:].split(".")[:-1]): {}})
            self.allTag[".".join(
                self.windowTitle()[16:].split(".")[:-1])].update({
                    self.lineEdit_Bookmark.text():
                    tc.millis_to_format(self.player.position())
                })
            self.set_TagonListwidget(
                self.windowTitle()[16:].split(".")[0])  # update tag listwidget
            # update combo box of edit and Main window
            self.pushButton_Start.setFocus(True)
            index = self.ComboBox_Tags_of_file.findText(
                self.windowTitle()[16:])
            self.ComboBox_Tags_of_file.setCurrentIndex(index)
            index = self.Setting.comboBox_Tag.findText(self.windowTitle()[16:])
            self.Setting.comboBox_Tag.setCurrentIndex(index)
            # ****

        except:
            pass

        self.lineEdit_Bookmark.clear()
        self.lineEdit_Bookmark.setVisible(False)

    def sch_icon_Event(self):
        # Show search lineEdit to search
        self.search_lineEdit.setFixedWidth(0)

        # Start Animation of search lineEdit
        self.SearchAnimation = Search_Animation(self)
        self.SearchAnimation.update_Animation.connect(
            self.Update_Search_Animation)
        self.pushButton_Search.setEnabled(False)
        self.SearchAnimation.start()

        self.search_lineEdit.setVisible(True)
        self.search_lineEdit.setFocus(True)

    def Update_Search_Animation(self, size):
        if size == -1:  # Animation is finished
            self.pushButton_Search.setEnabled(True)
        else:  # Animation is running
            self.search_lineEdit.setFixedWidth(size)

    def add_BookMarks(self):
        # Show Bookmark lineEdit to search

        if self.player.isVideoAvailable() or self.player.isAudioAvailable():
            self.lineEdit_Bookmark.setFixedWidth(0)

            # Start Animation of search lineEdit
            self.BookMarkAnimation = BookMark_Animation(self)
            self.BookMarkAnimation.update_Animation.connect(
                self.Update_BookMark_Animation)

            self.pushButton_BookMark.setEnabled(False)
            self.BookMarkAnimation.start()

            self.lineEdit_Bookmark.setVisible(True)
            self.lineEdit_Bookmark.setFocus(True)

    def Update_BookMark_Animation(self, size):
        if size == -1:  # Animation is finished
            self.pushButton_BookMark.setEnabled(True)
        else:  # Animation is running
            self.lineEdit_Bookmark.setFixedWidth(size)

    def MainWindow_Event(self, type):
        # Click on MainWindow
        self.search_lineEdit.setText("")
        self.search_lineEdit.setVisible(False)

        self.lineEdit_Bookmark.setText("")
        self.lineEdit_Bookmark.setVisible(False)

        self.sch_listWidget.setVisible(False)
        self.sch_listWidget.clear()

    # handle main size change to set correct size to
    # search and bookmark line edits and searchlistwidgetd
    def main_size_Change(self, val):
        self.lineEdit_Bookmark.setFixedWidth(int(self.size().width() / 4))
        self.search_lineEdit.setFixedWidth(int(self.size().width() / 4))

        self.sch_listWidget.resize(  # Handle size of search listwidget
            int(self.size().width() / 4),
            int((200 / 600) * self.size().height()))
        self.sch_listWidget.move(  # Move search listwidget
            self.size().width() - self.pushButton_Search.geometry().width() -
            self.search_lineEdit.geometry().width() - 15,
            self.search_lineEdit.geometry().height() +
            self.search_lineEdit.pos().y() + self.menubar.geometry().height())

    # Item clicked event in search tag part
    def item_searchlist_Event(self, item):
        session, tag = re.split(" -> ", item.text())
        # get all session to compare that is selected tag in playlist or user select tag wrong
        all_sessions = list(self.PlaylistW.Files.keys())
        # there is bug here if mp3 and mp4 has same name
        all_sessions = [
            ".".join(item.split(".")[:-1]) for item in all_sessions
        ]
        if session in all_sessions:
            if session != ".".join(self.windowTitle()[16:].split(".")[:-1]):
                # Show confirm window to get accept user for change video
                self.confirmWin = confrimWin(
                    self,
                    session=session,
                    tag_Text=tag,
                    Text=
                    f"Are you sure to change video to {session} from search")
                self.confirmWin.show()
            else:
                try:
                    # concert time format to second for using in change position
                    time_second = tc.to_second(self.allTag[session][tag])
                    self.change_Position(time_second)
                except:  # handle unexcepted error!
                    pass
        else:
            Warning_user_wrongTags = confrimWin(
                self, Title="Warning", Text="You have opened wrong tag files")
            Warning_user_wrongTags.show()

    # Create search listwidget and running thread to starting search

    def search_Tag(self, val):
        # Create QListWidget
        self.sch_listWidget.resize(int((200 / 800) * self.size().width()),
                                   int((200 / 600) * self.size().height()))
        self.sch_listWidget.move(
            self.search_lineEdit.x(),
            self.search_lineEdit.geometry().height() +
            self.search_lineEdit.pos().y() + self.menubar.geometry().height())

        self.sch_listWidget.setVisible(True)
        if val == "":
            self.sch_listWidget.clear()
        # start search thread
        else:
            self.search_Thread = search_thread(self, self.allTag, val)
            self.search_Thread.update_schTag.connect(self.update_searchTags)
            self.search_Thread.start()

    # Update searchtags function to update search widget by searching instantly
    def update_searchTags(self, tagsDict):
        self.sch_listWidget.clear()  # clear search list widget

        for session, Tags in tagsDict.items(
        ):  # writing data on search listwidget
            for text in Tags:
                self.sch_listWidget.addItem(session + " -> " + text)

    def Logout(self):
        os.remove("LoginPart/User.csv")
        self.close()
        self.player.stop()
        self.Setting.close()
        self.PlaylistW.close()
        self.helpW.close()
        self.DockWidget_Tags_of_file.close()
        subprocess.call(['python', 'MediaPlayer.py'])  # Start again

    def Play_list(self):
        # Open playlist
        # To show current Row for every time it is opened

        try:
            if self.PlaylistW.listWidget_Playlist.count():
                self.PlaylistW.listWidget_Playlist.setCurrentRow(
                    list(self.PlaylistW.Files.keys()).index(
                        self.windowTitle()[16:]))
        except Exception as e:
            pass

        self.PlaylistW.show()
        # setPosition of playlist when it is opened
        self.PlaylistW.move(
            QtGui.QCursor().pos().x(),
            QtGui.QCursor().pos().y() - self.PlaylistW.size().height() - 25)

    def Show_Tags_of_file(self):
        self.sch_listWidget.setVisible(False)
        # To show Tags of file in DockWidget
        self.DockWidget_Tags_of_file.setVisible(
            not self.DockWidget_Tags_of_file.isVisible())
        index = self.ComboBox_Tags_of_file.findText(self.windowTitle()[16:])
        self.ComboBox_Tags_of_file.setCurrentIndex(index)
        # To Change features of the Dockwidget according Fullscreen
        if self.isFullScreen():
            self.DockWidget_Tags_of_file.setFeatures(
                QDockWidget.DockWidgetClosable)
            Full_screen.Set_visible(self, False)

        else:  # To Change features of the Dockwidget according Normalscreen
            self.DockWidget_Tags_of_file.setFeatures(
                QDockWidget.DockWidgetFloatable
                | QDockWidget.DockWidgetMovable)

    def ListWidget_Tag(self, index):
        """Tag combo box item clicked function"""
        videoName = ".".join(
            list(self.PlaylistW.Files.keys())[index].split(".")[:-1])
        self.set_TagonListwidget(videoName, Setting_Tags=False)

    def openTags(self):
        """OpenTag in csv, pptx, docx format and start tag thread for reading data"""
        try:
            # To read initial Tag Directory from csvfile
            # if there is FilePath_Tag.csv
            with open("./tagPart/Filepath_Tag.csv") as file:
                initial_FilePath_Tag = file.read()
                _tag_Path, _ = QFileDialog.getOpenFileName(
                    self,
                    "Open Tag",
                    directory=initial_FilePath_Tag,
                    filter='*.csv *.docx *.pptx')

        except Exception as e:
            initial_FilePath_Tag = os.path.join(os.getcwd(), 'Tags')
            _tag_Path, _ = QFileDialog.getOpenFileName(
                self,
                "Open Tag",
                directory=initial_FilePath_Tag,
                filter='*.csv *.docx *.pptx')

        # if tagpath is correct we starting tag_thread to read tags from path
        if _tag_Path:
            # save filepath in csv file
            self.tag_Path = _tag_Path
            try:
                with open("./tagPart/Filepath_Tag.csv", 'w') as file:
                    file.write(
                        self.tag_Path.replace(
                            self.tag_Path.split("/")[-1], ""))
            except Exception as e:
                pass
            Tagname = self.tag_Path.split("/")[-1]
            fileFormat = Tagname.split(".")[-1]
            self.tag_thread = read_Tag(self, self.tag_Path, fileFormat)
            self.tag_thread.Tag_Ready.connect(self.getTag)
            self.tag_thread.start()

    # Tag is ready to use
    # if tags ready we shoud save them in self.allTag variable to use them properly
    def getTag(self, tags):
        self.allTag = tags
        index = self.ComboBox_Tags_of_file.findText(self.windowTitle()[16:])
        self.ComboBox_Tags_of_file.setCurrentIndex(index)
        self.set_TagonListwidget(".".join(
            self.windowTitle()[16:].split(".")[:-1]))

    # set tags on tag listwidget using vedio name
    # update tags

    def set_TagonListwidget(self,
                            videoName,
                            Setting_Tags=True,
                            Media_Tags=True):
        if Media_Tags:
            self.ListWidget_Tags_of_file.clear()
        if Setting_Tags:
            self.Setting.Edit_tag_Listwidget.clear()
        try:
            if videoName in self.allTag:
                sessionTag = self.allTag[videoName]
                # sorted tags by time
                sessionTag = {
                    text: time
                    for text, time in sorted(
                        sessionTag.items(),
                        key=lambda item: tc.to_second(item[1]))
                }
                for text in sessionTag:
                    # setting part tags by qtreewidget
                    if Setting_Tags:
                        item = QTreeWidgetItem(
                            self.Setting.Edit_tag_Listwidget,
                            [text, sessionTag[text]])
                    # media part tags by qlistwidget
                    if Media_Tags:
                        self.ListWidget_Tags_of_file.addItem(
                            f'{self.ListWidget_Tags_of_file.count()+1} . {text}'
                        )

        except:
            pass

    # item clicked event to go to time correlate clicked tag in video

    # change time when clicking on tags in search part and main listwidget if tags

    def GoToTagtime(self, item):
        spliter = len(str(self.ListWidget_Tags_of_file.currentRow() + 1)) + 3
        tag_Text = item.text()[spliter:]
        # change time if clicked on tags that belong to playing session
        if self.windowTitle()[16:] == self.ComboBox_Tags_of_file.currentText():
            session = ".".join(self.windowTitle()[16:].split(".")[:-1])
            try:
                # convert time to seconds using tc mudole(write by own)
                time_second = tc.to_second(self.allTag[session][tag_Text])
                # using change position function to handle sliders and time
                self.change_Position(time_second)
            except:
                pass
        else:
            session = ".".join(
                self.ComboBox_Tags_of_file.currentText().split(".")[:-1])
            self.confirmWin = confrimWin(
                self,
                session=session,
                tag_Text=tag_Text,
                Text=f"Are you sure to change video to {session}")
            self.confirmWin.show()

    def change_Position(self, time_second):
        # change slider position using item time
        self.Slider_Play.setValue(int(time_second) * 1000)
        # change video position using item time
        self.player.setPosition(int(time_second) * 1000)

    # change video function to change video when clicked on
    # tags that there is not in current movie's tags

    def change_Video(self, session):
        # there is bug here if user select mp3 file that has same name with mp4 similar file
        for key in self.PlaylistW.Files:
            if session + ".mp4" == key:
                return self._change_Video(key)
            elif session + ".mp3" == key:
                return self._change_Video(key)
            elif session + ".mkv" == key:
                return self._change_Video(key)
        return False

    def _change_Video(self, key):
        self.player.setMedia(
            QMediaContent(QUrl.fromLocalFile(
                self.PlaylistW.Files[key])))  # set video
        self.start()
        self.setWindowTitle(f" Media Player - {key}")  # change title
        # update combo box of session in MediaPlayer
        index = self.ComboBox_Tags_of_file.findText(key)
        self.ComboBox_Tags_of_file.setCurrentIndex(index)  # update combo box
        # update tags on setting and MediaPlayer window
        self.set_TagonListwidget(".".join(key.split(".")[:-1]))
        return True

    # close Event to stop runnig thread and preventing error
    def Close(self, val):
        if self.tag_thread:
            self.tag_thread.stop()
        if self.search_Thread:
            self.search_Thread.stop()
        if self.SearchAnimation:
            self.SearchAnimation.stop()
        if self.BookMarkAnimation:
            self.BookMarkAnimation.stop()

    # dragEnterEvent to hanle drag and drop option

    def dragEnterEvent(self, event):
        # pass
        if event.mimeData().hasUrls:
            event.accept()
        else:
            event.ignore()

    # dragMoveEvent to hanle drag and drop option
    def dragMoveEvent(self, event):
        if event.mimeData().hasUrls:
            event.accept()
        else:
            event.ignore()

    # dropEvent to hanle drag and drop option
    def dropEvent(self, event):
        if event.mimeData().hasUrls:
            event.setDropAction(Qt.CopyAction)
            # get droppen file path
            file_path = event.mimeData().urls()[0].toLocalFile()
            file_format = (file_path.split("/")[-1]).split(".")[-1]
            desired_format = ['mp4', 'mkv', 'mp3']  # desired path to open
            if file_format in desired_format:  # check file format
                self.Load_video(filepath=file_path)