vlcframe.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. import time
  2. from collections import deque
  3. import vlc
  4. from PyQt5 import QtWidgets, QtGui, QtCore
  5. from PyQt5.QtCore import pyqtSignal
  6. from PyQt5.QtGui import QIcon
  7. from ui.qt.widgets.vlcframe_ui import Ui_VlcFrame
  8. class VlcFrame(QtWidgets.QFrame):
  9. DEFAULT_VOLUME = 50
  10. trackStarted = pyqtSignal(object)
  11. trackEnded = pyqtSignal(object)
  12. playlistStarted = pyqtSignal(object)
  13. playlistEnded = pyqtSignal(object)
  14. def __init__(self, parent=None):
  15. super().__init__(parent)
  16. self._media = None
  17. self._is_paused = True
  18. self.is_muted = False
  19. self.volume = self.DEFAULT_VOLUME
  20. self.playlist = None
  21. self.tracks = deque()
  22. self.current_index = None
  23. # Create a basic vlc instance
  24. self._instance = vlc.Instance()
  25. # Create an empty vlc media player
  26. self._mediaplayer = self._instance.media_player_new()
  27. self.createWidgets()
  28. def createWidgets(self):
  29. self.ui = Ui_VlcFrame()
  30. self.ui.setupUi(self)
  31. self.ui.progressionSlider.sliderMoved.connect(self.set_position)
  32. self.ui.progressionSlider.sliderPressed.connect(self.set_position)
  33. self.ui.progressionSlider.positionClicked.connect(self.set_position)
  34. self.ui.btnPlayPause.clicked.connect(self.play_pause)
  35. self.ui.volumeSlider.setValue(self.volume)
  36. self._mediaplayer.audio_set_volume(self.volume)
  37. self.ui.volumeSlider.valueChanged.connect(self.set_volume)
  38. self.ui.volumeSlider.positionClicked.connect(self.set_volume)
  39. self.ui.btnMute.clicked.connect(self.toggle_muted)
  40. self.ui.btnPrevious.clicked.connect(self.go_to_previous_track)
  41. self.ui.btnNext.clicked.connect(self.go_to_next_track)
  42. self.timer = QtCore.QTimer(self)
  43. self.timer.setInterval(100)
  44. self.timer.timeout.connect(self.update_ui)
  45. self.setEnabled(False)
  46. def load_playlist(self, playlist, tracks, start_at=0):
  47. self.tracks = deque(tracks)
  48. self.current_index = start_at
  49. self.playlist = playlist
  50. def load_track(self, track):
  51. self.tracks = deque([track])
  52. self.current_index = 0
  53. self.playlist = None
  54. @property
  55. def currentTrack(self):
  56. if self.current_index is None:
  57. return None
  58. return self.tracks[self.current_index]
  59. def play(self):
  60. track = self.currentTrack
  61. self._media = self._instance.media_new(track.path)
  62. # Put the media in the media player
  63. self._mediaplayer.set_media(self._media)
  64. # Parse the metadata of the file
  65. self._media.parse()
  66. self.play_pause()
  67. self.setEnabled(True)
  68. if self.playlist:
  69. self.playlistStarted.emit(self.playlist)
  70. self.trackStarted.emit(self.currentTrack)
  71. if self.is_muted:
  72. self._mediaplayer.audio_set_volume(0)
  73. self.refresh_ui()
  74. def refresh_ui(self):
  75. self.ui.btnPrevious.setEnabled(
  76. len(self.tracks) > 0 and self.current_index > 0
  77. )
  78. self.ui.btnNext.setEnabled(
  79. len(self.tracks) > 0 and self.current_index < (len(self.tracks) - 1)
  80. )
  81. # Set the title of the track
  82. self.ui.lblTrack.setText(f"{self.currentTrack.artist or '(artiste inconnu)'} - {self.currentTrack.title}")
  83. self.ui.lblDuration.setText(self.currentTrack.literal_duration())
  84. def play_pause(self):
  85. """Toggle play/pause status
  86. """
  87. if self._mediaplayer.is_playing():
  88. self._mediaplayer.pause()
  89. self.ui.btnPlayPause.setIcon(QIcon(":/img/rsc/play.png"))
  90. self._is_paused = True
  91. self.timer.stop()
  92. else:
  93. if self._mediaplayer.play() == -1:
  94. return
  95. self._mediaplayer.play()
  96. self.ui.btnPlayPause.setIcon(QIcon(":/img/rsc/pause.png"))
  97. self.timer.start()
  98. self._is_paused = False
  99. def stop(self):
  100. """Stop player
  101. """
  102. self._mediaplayer.stop()
  103. self.ui.btnPlayPause.setIcon(QIcon(":/img/rsc/play.png"))
  104. self.ui.progressionSlider.setSliderPosition(0)
  105. if self.playlist:
  106. self.playlistEnded.emit(self.playlist)
  107. self.trackEnded.emit(self.currentTrack)
  108. if len(self.tracks) > 0 and self.current_index is not None and self.current_index < (len(self.tracks) - 1):
  109. self.current_index += 1
  110. self.play()
  111. else:
  112. self.current_index = None
  113. def go_to_previous_track(self):
  114. if not len(self.tracks) > 0 or self.current_index == 0:
  115. return
  116. self.current_index -= 1
  117. self.play()
  118. def go_to_next_track(self):
  119. if not len(self.tracks) > 0 or self.current_index == (len(self.tracks) - 1):
  120. return
  121. self.current_index += 1
  122. self.play()
  123. def update_ui(self):
  124. """Updates the user interface"""
  125. # Set the slider's position to its corresponding media position
  126. # Note that the setValue function only takes values of type int,
  127. # so we must first convert the corresponding media position.
  128. media_pos = int(self._mediaplayer.get_position() * 1000)
  129. self.ui.progressionSlider.setValue(media_pos)
  130. if self.currentTrack:
  131. time_pos = media_pos * self.currentTrack.duration // 1000
  132. self.ui.lblPosition.setText(time.strftime("%M:%S", time.gmtime(time_pos)))
  133. # No need to call this function if nothing is played
  134. if not self._mediaplayer.is_playing():
  135. self.timer.stop()
  136. # After the video finished, the play button stills shows "Pause",
  137. # which is not the desired behavior of a media player.
  138. # This fixes that "bug".
  139. if not self._is_paused:
  140. self.stop()
  141. def set_volume(self, volume):
  142. """Set the volume
  143. """
  144. # if muted: unmute
  145. if self.is_muted:
  146. self.toggle_muted()
  147. self._mediaplayer.audio_set_volume(volume)
  148. self.volume = volume
  149. self.ui.volumeSlider.setValue(volume)
  150. def set_position(self, position=None):
  151. """Set the movie position according to the position slider.
  152. """
  153. # The vlc MediaPlayer needs a float value between 0 and 1, Qt uses
  154. # integer variables, so you need a factor; the higher the factor, the
  155. # more precise are the results (1000 should suffice).
  156. # if paused: resume
  157. if self._is_paused:
  158. self.play_pause()
  159. # Set the media position to where the slider was dragged
  160. self.timer.stop()
  161. pos = self.ui.progressionSlider.value()
  162. self._mediaplayer.set_position(pos / 1000.0)
  163. self.timer.start()
  164. def toggle_muted(self):
  165. self.set_muted(not self.is_muted)
  166. def set_muted(self, mute):
  167. if mute:
  168. self._mediaplayer.audio_set_volume(0)
  169. self.ui.btnMute.setIcon(QIcon(":/img/rsc/mute.png"))
  170. self.is_muted = True
  171. else:
  172. self._mediaplayer.audio_set_volume(self.volume)
  173. self.ui.btnMute.setIcon(QIcon(":/img/rsc/volume.png"))
  174. self.is_muted = False
  175. def current(self):
  176. return self.currentTrack