import time from collections import deque from core.vlc_ import vlc from PyQt5 import QtWidgets, QtGui, QtCore from PyQt5.QtCore import pyqtSignal from PyQt5.QtGui import QIcon from ui.qt.widgets.vlcframe_ui import Ui_VlcFrame class VlcFrame(QtWidgets.QFrame): DEFAULT_VOLUME = 50 trackStarted = pyqtSignal(object) trackEnded = pyqtSignal(object) playlistStarted = pyqtSignal(object) playlistEnded = pyqtSignal(object) def __init__(self, parent=None): super().__init__(parent) self._media = None self._is_paused = True self.is_muted = False self.volume = self.DEFAULT_VOLUME self.playlist = None self.tracks = deque() self.current_index = None # Create a basic vlc instance self._instance = vlc.Instance() # Create an empty vlc media player self._mediaplayer = self._instance.media_player_new() self.createWidgets() def createWidgets(self): self.ui = Ui_VlcFrame() self.ui.setupUi(self) self.ui.progressionSlider.sliderMoved.connect(self.set_position) self.ui.progressionSlider.sliderPressed.connect(self.set_position) self.ui.progressionSlider.positionClicked.connect(self.set_position) self.ui.btnPlayPause.clicked.connect(self.play_pause) self.ui.volumeSlider.setValue(self.volume) self._mediaplayer.audio_set_volume(self.volume) self.ui.volumeSlider.valueChanged.connect(self.set_volume) self.ui.volumeSlider.positionClicked.connect(self.set_volume) self.ui.btnMute.clicked.connect(self.toggle_muted) self.ui.btnPrevious.clicked.connect(self.go_to_previous_track) self.ui.btnNext.clicked.connect(self.go_to_next_track) self.timer = QtCore.QTimer(self) self.timer.setInterval(100) self.timer.timeout.connect(self.update_ui) self.setEnabled(False) def load_playlist(self, playlist, tracks, start_at=0): self.tracks = deque(tracks) self.current_index = start_at self.playlist = playlist def load_track(self, track): self.tracks = deque([track]) self.current_index = 0 self.playlist = None @property def currentTrack(self): if self.current_index is None: return None return self.tracks[self.current_index] def play(self): track = self.currentTrack self._media = self._instance.media_new(track.path) # Put the media in the media player self._mediaplayer.set_media(self._media) # Parse the metadata of the file self._media.parse() self.play_pause() self.setEnabled(True) if self.playlist: self.playlistStarted.emit(self.playlist) self.trackStarted.emit(self.currentTrack) if self.is_muted: self._mediaplayer.audio_set_volume(0) self.refresh_ui() def refresh_ui(self): self.ui.btnPrevious.setEnabled( len(self.tracks) > 0 and self.current_index > 0 ) self.ui.btnNext.setEnabled( len(self.tracks) > 0 and self.current_index < (len(self.tracks) - 1) ) # Set the title of the track self.ui.lblTrack.setText(f"{self.currentTrack.artist or '(artiste inconnu)'} - {self.currentTrack.title}") self.ui.lblDuration.setText(self.currentTrack.literal_duration()) def play_pause(self): """Toggle play/pause status """ if self._mediaplayer.is_playing(): self._mediaplayer.pause() self.ui.btnPlayPause.setIcon(QIcon(":/img/rsc/play.png")) self._is_paused = True self.timer.stop() else: if self._mediaplayer.play() == -1: return self._mediaplayer.play() self.ui.btnPlayPause.setIcon(QIcon(":/img/rsc/pause.png")) self.timer.start() self._is_paused = False def stop(self): """Stop player """ self._mediaplayer.stop() self.ui.btnPlayPause.setIcon(QIcon(":/img/rsc/play.png")) self.ui.progressionSlider.setSliderPosition(0) if self.playlist: self.playlistEnded.emit(self.playlist) self.trackEnded.emit(self.currentTrack) if len(self.tracks) > 0 and self.current_index is not None and self.current_index < (len(self.tracks) - 1): self.current_index += 1 self.play() else: self.current_index = None def go_to_previous_track(self): if not len(self.tracks) > 0 or self.current_index == 0: return self.current_index -= 1 self.play() def go_to_next_track(self): if not len(self.tracks) > 0 or self.current_index == (len(self.tracks) - 1): return self.current_index += 1 self.play() def update_ui(self): """Updates the user interface""" # Set the slider's position to its corresponding media position # Note that the setValue function only takes values of type int, # so we must first convert the corresponding media position. media_pos = int(self._mediaplayer.get_position() * 1000) self.ui.progressionSlider.setValue(media_pos) if self.currentTrack: time_pos = media_pos * self.currentTrack.duration // 1000 self.ui.lblPosition.setText(time.strftime("%M:%S", time.gmtime(time_pos))) # No need to call this function if nothing is played if not self._mediaplayer.is_playing(): self.timer.stop() # After the video finished, the play button stills shows "Pause", # which is not the desired behavior of a media player. # This fixes that "bug". if not self._is_paused: self.stop() def set_volume(self, volume): """Set the volume """ # if muted: unmute if self.is_muted: self.toggle_muted() self._mediaplayer.audio_set_volume(volume) self.volume = volume self.ui.volumeSlider.setValue(volume) def set_position(self, position=None): """Set the movie position according to the position slider. """ # The vlc MediaPlayer needs a float value between 0 and 1, Qt uses # integer variables, so you need a factor; the higher the factor, the # more precise are the results (1000 should suffice). # if paused: resume if self._is_paused: self.play_pause() # Set the media position to where the slider was dragged self.timer.stop() pos = self.ui.progressionSlider.value() self._mediaplayer.set_position(pos / 1000.0) self.timer.start() def toggle_muted(self): self.set_muted(not self.is_muted) def set_muted(self, mute): if mute: self._mediaplayer.audio_set_volume(0) self.ui.btnMute.setIcon(QIcon(":/img/rsc/mute.png")) self.is_muted = True else: self._mediaplayer.audio_set_volume(self.volume) self.ui.btnMute.setIcon(QIcon(":/img/rsc/volume.png")) self.is_muted = False def current(self): return self.currentTrack