class Player(QWidget): audio_path = "audio" lyrics_path = "lyrics" timings_path = os.path.join("lyrics", "timing") settings_path = "settings.json" fullScreenChanged = pyqtSignal(bool) def __init__(self, parent=None): super(Player, self).__init__(parent) self.setWindowTitle("SongScreen") self.setFocusPolicy(Qt.StrongFocus) self.colorDialog = None self.trackInfo = "" self.statusInfo = "" self.duration = 0 self.player = QMediaPlayer() self.playlist = QMediaPlaylist() self.player.setPlaylist(self.playlist) self.player.durationChanged.connect(self.durationChanged) self.player.positionChanged.connect(self.positionChanged) self.player.metaDataChanged.connect(self.metaDataChanged) # self.playlist.currentIndexChanged.connect(self.playlistPositionChanged) self.player.mediaStatusChanged.connect(self.statusChanged) self.player.bufferStatusChanged.connect(self.bufferingProgress) self.player.videoAvailableChanged.connect(self.videoAvailableChanged) self.player.error.connect(self.displayErrorMessage) # self.videoWidget = VideoWidget() # self.player.setVideoOutput(self.videoWidget) self.slider = MediaProgressWidget() # QSlider(Qt.Horizontal) self.markers = [] self.songtext_widget = SongTextWidget() self.songtext_widget.show() # self.playlistModel = PlaylistModel() # self.playlistModel.setPlaylist(self.playlist) # # self.playlistView = QListView() # self.playlistView.setModel(self.playlistModel) # self.playlistView.setCurrentIndex( # self.playlistModel.index(self.playlist.currentIndex(), 0)) # # self.playlistView.activated.connect(self.jump) self.slider.setRange(0, self.player.duration() / 1000) self.labelDuration = QLabel() self.slider.sliderMoved.connect(self.seek) # openButton = QPushButton("Open", clicked=self.open) controls = PlayerControlsWidget() controls.setState(self.player.state()) controls.setVolume(self.player.volume()) # controls.setMuted(controls.isMuted()) controls.play.connect(self.player.play) controls.pause.connect(self.player.pause) controls.stop.connect(self.stop_clicked) # controls.stop.connect(self.videoWidget.update) # controls.next.connect(self.playlist.next) # controls.previous.connect(self.previousClicked) controls.changeVolume.connect(self.player.setVolume) # controls.changeMuting.connect(self.player.setMuted) # controls.changeRate.connect(self.player.setPlaybackRate) self.player.stateChanged.connect(controls.setState) self.player.stateChanged.connect(self.setState) self.player.volumeChanged.connect(controls.setVolume) # self.player.mutedChanged.connect(controls.setMuted) # self.fullScreenButton = QPushButton("FullScreen") # self.fullScreenButton.setCheckable(True) # # self.colorButton = QPushButton("Color Options...") # self.colorButton.setEnabled(False) # self.colorButton.clicked.connect(self.showColorDialog) displayLayout = QHBoxLayout() # displayLayout.addWidget(self.videoWidget, 2) # displayLayout.addWidget(self.songtext_widget) # displayLayout.addWidget(self.playlistView) self.song_select_widget = SongSelectWidget() self.song_select_widget.song_selected.connect(self.load_song) self.screen_select_widget = ScreenSelectWidget() self.screen_select_widget.screen_selected.connect(self.display_lyrics_on_screen) self.screen_select_widget.active_screen = QApplication.desktop().screenNumber(self.songtext_widget) self.settings_button = QPushButton() self.settings_button.setText(self.tr("Settings...")) self.settings_button.clicked.connect(self.show_settings) sidebarLayout = QVBoxLayout() sidebarLayout.setContentsMargins(10, 1, 0, 1); sidebarLayout.addWidget(self.settings_button) sidebarLayout.addStretch(1); sidebarLayout.addWidget(self.screen_select_widget) displayLayout.addWidget(self.song_select_widget) displayLayout.addLayout(sidebarLayout) controlLayout = QHBoxLayout() controlLayout.setContentsMargins(0, 0, 0, 0) # controlLayout.addWidget(openButton) # controlLayout.addStretch(1) controlLayout.addWidget(controls) controlLayout.addStretch(1) controlLayout.addWidget(self.labelDuration) # controlLayout.addWidget(self.fullScreenButton) # controlLayout.addWidget(self.colorButton) layout = QVBoxLayout() layout.addLayout(displayLayout) hLayout = QHBoxLayout() hLayout.addWidget(self.slider) # hLayout.addWidget(self.labelDuration) layout.addLayout(hLayout) layout.addLayout(controlLayout) self.setLayout(layout) if not self.player.isAvailable(): QMessageBox.warning(self, "Service not available", "The QMediaPlayer object does not have a valid service.\n" "Please check the media service plugins are installed.") controls.setEnabled(False) self.playlistView.setEnabled(False) # openButton.setEnabled(False) self.colorButton.setEnabled(False) self.fullScreenButton.setEnabled(False) self.metaDataChanged() self._loading_audio = False self._finished_song = False self._lyrics_fading = False self._song_number = -1 self.settings = { 'font_size': 40, 'line_increment': 2, 'lyrics_language': '', } self._load_settings() self.settings_widget = SettingsWidget(self.settings, self.lyrics_path) self.settings_widget.font_size_changed.connect(self.songtext_widget.set_font_size) self.settings_widget.line_increment_changed.connect(self.songtext_widget.set_line_increment) self.settings_widget.language_changed.connect(self._language_changed) self.song_select_widget.reset(self.available_song_numbers) @property def lyrics_language_path(self): path = QStandardPaths.locate(QStandardPaths.AppDataLocation, self.lyrics_path, QStandardPaths.LocateDirectory) return os.path.join(path, self.settings['lyrics_language']) @property def available_song_numbers(self): audios = set( [int(os.path.splitext(filename)[0]) for filename in os.listdir(self.audio_path) if filename[0] != '.']) try: lyrics = set( [int(os.path.splitext(filename)[0]) for filename in os.listdir(self.lyrics_language_path) if filename[0] != '.'] ) except (ValueError, FileNotFoundError): lyrics = set() return sorted(list(audios.intersection(lyrics))) def show_settings(self): self.settings_widget.hide() self.settings_widget.show() def display_lyrics_on_screen(self, screen_number): desktop = QApplication.desktop() if screen_number >= desktop.screenCount(): screen_number = desktop.screenNumber(self) rect = desktop.availableGeometry(screen_number) for _ in range(3): if screen_number != desktop.screenNumber(self): self.songtext_widget.setWindowFlags(Qt.FramelessWindowHint) self.songtext_widget.hide() self.songtext_widget.move(rect.x(), rect.y()) self.songtext_widget.resize(rect.width(), rect.height()) self.songtext_widget.showFullScreen() else: self.songtext_widget.setWindowFlags(Qt.WindowTitleHint) self.songtext_widget.hide() self.songtext_widget.move(rect.x(), rect.y()) self.songtext_widget.resize(self.songtext_widget.minimumSize()) self.songtext_widget.show() self.screen_select_widget.active_screen = screen_number self.activateWindow() def load_song(self, song_number): if self._song_number == song_number: self.seek(0) else: if self._song_number > 0: self._save_timings() self._song_number = song_number self.slider.dirty = False self._load_audio() self._load_lyrics() # self.player.play() def _load_audio(self): filename = os.path.join(self.audio_path, "{:03}.mp3".format(self._song_number)) self.playlist.clear() fileInfo = QFileInfo(filename) if fileInfo.exists(): url = QUrl.fromLocalFile(fileInfo.absoluteFilePath()) if fileInfo.suffix().lower() == 'm3u': self.playlist.load(url) else: self.playlist.addMedia(QMediaContent(url)) self._loading_audio = True self.player.play() def _load_lyrics(self): with open(os.path.join(self.lyrics_language_path, "{}.json".format(self._song_number)), 'r') as f: song_markers = json.load(f) self.markers = [] for m in song_markers['markers']: marker = MediaMarker(self.slider, m['name']) marker.text = m['text'] marker.progress = 0.0 self.markers.append(marker) self.songtext_widget.title = "{} {}".format(self._song_number, song_markers['title']) self.songtext_widget.markers = self.markers self.songtext_widget.fade_in() try: with open(os.path.join(self.timings_path, "{}.json".format(self._song_number)), 'r') as f: timings = json.load(f) for m, t in zip(self.markers, timings): m.progress = t except FileNotFoundError: pass self.slider.markers = self.markers def _language_changed(self, _): available_song_numbers = self.available_song_numbers self.song_select_widget.reset(available_song_numbers) if self._song_number in available_song_numbers: self._load_lyrics() # def open(self): # fileNames, _ = QFileDialog.getOpenFileNames(self, "Open Files") # self.addToPlaylist(fileNames) # # def addToPlaylist(self, fileNames): # for name in fileNames: # fileInfo = QFileInfo(name) # if fileInfo.exists(): # url = QUrl.fromLocalFile(fileInfo.absoluteFilePath()) # if fileInfo.suffix().lower() == 'm3u': # self.playlist.load(url) # else: # self.playlist.addMedia(QMediaContent(url)) # else: # url = QUrl(name) # if url.isValid(): # self.playlist.addMedia(QMediaContent(url)) def durationChanged(self, duration): duration /= 1000 self.duration = duration self.slider.setMaximum(duration) if self._loading_audio: self._loading_audio = False line_total = 0 for marker in self.markers: line_total += marker.linecount - 1 silence_ratio = 5.0 / self.duration offset = 1.8 / line_total linecount = 0 for marker in self.markers: if marker.progress == 0.0: marker.progress = offset + (1 - offset) * (1 - silence_ratio) * linecount / line_total linecount += marker.linecount - 1 self.player.pause() @property def _should_fade_out(self): return self.player.position() / 1000 >= self.duration - 5 def positionChanged(self, progress): progress /= 1000 if not self.slider.isSliderDown(): self.slider.setValue(progress) self.updateDurationInfo(progress) if self.duration > 0: # if self.player.state() == QMediaPlayer.PlayingState: self.songtext_widget.progress = progress / self.duration if self._should_fade_out: self._fade_out_lyrics() def _fade_out_lyrics(self): if not self._lyrics_fading: self._lyrics_fading = True self.songtext_widget.fade_out() def metaDataChanged(self): if self.player.isMetaDataAvailable(): self.setTrackInfo("%s - %s" % ( self.player.metaData(QMediaMetaData.AlbumArtist), self.player.metaData(QMediaMetaData.Title))) def previousClicked(self): # Go to the previous track if we are within the first 5 seconds of # playback. Otherwise, seek to the beginning. if self.player.position() <= 5000: self.playlist.previous() else: self.player.setPosition(0) def jump(self, index): if index.isValid(): self.playlist.setCurrentIndex(index.row()) self.player.play() def seek(self, seconds): self.player.setPosition(seconds * 1000) def setState(self, status): if status == QMediaPlayer.StoppedState: self._finished_song = True elif status == QMediaPlayer.PlayingState: if self._finished_song or (self._lyrics_fading and not self._should_fade_out): self._finished_song = False self._lyrics_fading = False self.songtext_widget.fade_in() def stop_clicked(self): self.player.stop() self._fade_out_lyrics() def statusChanged(self, status): self.handleCursor(status) if status == QMediaPlayer.LoadingMedia: self.setStatusInfo("Loading...") elif status == QMediaPlayer.StalledMedia: self.setStatusInfo("Media Stalled") elif status == QMediaPlayer.EndOfMedia: QApplication.alert(self) elif status == QMediaPlayer.InvalidMedia: self.displayErrorMessage() else: self.setStatusInfo("") def handleCursor(self, status): if status in (QMediaPlayer.LoadingMedia, QMediaPlayer.BufferingMedia, QMediaPlayer.StalledMedia): self.setCursor(Qt.BusyCursor) else: self.unsetCursor() def bufferingProgress(self, progress): self.setStatusInfo("Buffering %d%" % progress) def videoAvailableChanged(self, available): if available: self.fullScreenButton.clicked.connect( self.videoWidget.setFullScreen) self.videoWidget.fullScreenChanged.connect( self.fullScreenButton.setChecked) if self.fullScreenButton.isChecked(): self.videoWidget.setFullScreen(True) else: self.fullScreenButton.clicked.disconnect( self.videoWidget.setFullScreen) self.videoWidget.fullScreenChanged.disconnect( self.fullScreenButton.setChecked) self.videoWidget.setFullScreen(False) self.colorButton.setEnabled(available) def setTrackInfo(self, info): self.trackInfo = info # if self.statusInfo != "": # self.setWindowTitle("%s | %s" % (self.trackInfo, self.statusInfo)) # else: # self.setWindowTitle(self.trackInfo) def setStatusInfo(self, info): self.statusInfo = info # if self.statusInfo != "": # self.setWindowTitle("%s | %s" % (self.trackInfo, self.statusInfo)) # else: # self.setWindowTitle(self.trackInfo) def displayErrorMessage(self): self.setStatusInfo(self.player.errorString()) def updateDurationInfo(self, currentInfo): duration = self.duration if currentInfo or duration: currentTime = QTime((currentInfo / 3600) % 60, (currentInfo / 60) % 60, currentInfo % 60, (currentInfo * 1000) % 1000) totalTime = QTime((duration / 3600) % 60, (duration / 60) % 60, duration % 60, (duration * 1000) % 1000); format = 'hh:mm:ss' if duration > 3600 else 'mm:ss' tStr = currentTime.toString(format) + " / " + totalTime.toString(format) else: tStr = "" self.labelDuration.setText(tStr) def showColorDialog(self): if self.colorDialog is None: brightnessSlider = QSlider(Qt.Horizontal) brightnessSlider.setRange(-100, 100) brightnessSlider.setValue(self.videoWidget.brightness()) brightnessSlider.sliderMoved.connect( self.videoWidget.setBrightness) self.videoWidget.brightnessChanged.connect( brightnessSlider.setValue) contrastSlider = QSlider(Qt.Horizontal) contrastSlider.setRange(-100, 100) contrastSlider.setValue(self.videoWidget.contrast()) contrastSlider.sliderMoved.connect(self.videoWidget.setContrast) self.videoWidget.contrastChanged.connect(contrastSlider.setValue) hueSlider = QSlider(Qt.Horizontal) hueSlider.setRange(-100, 100) hueSlider.setValue(self.videoWidget.hue()) hueSlider.sliderMoved.connect(self.videoWidget.setHue) self.videoWidget.hueChanged.connect(hueSlider.setValue) saturationSlider = QSlider(Qt.Horizontal) saturationSlider.setRange(-100, 100) saturationSlider.setValue(self.videoWidget.saturation()) saturationSlider.sliderMoved.connect( self.videoWidget.setSaturation) self.videoWidget.saturationChanged.connect( saturationSlider.setValue) layout = QFormLayout() layout.addRow("Brightness", brightnessSlider) layout.addRow("Contrast", contrastSlider) layout.addRow("Hue", hueSlider) layout.addRow("Saturation", saturationSlider) button = QPushButton("Close") layout.addRow(button) self.colorDialog = QDialog(self) self.colorDialog.setWindowTitle("Color Options") self.colorDialog.setLayout(layout) button.clicked.connect(self.colorDialog.close) self.colorDialog.show() def closeEvent(self, close_event): self._save_timings() self._save_settings() self.songtext_widget.close() self.settings_widget.close() def keyPressEvent(self, key_event): if key_event.key() == Qt.Key_Space: key_event.accept() if self.player.state() == QMediaPlayer.PlayingState: self.player.pause() elif self.player.state() in [QMediaPlayer.PausedState, QMediaPlayer.StoppedState]: self.player.play() elif key_event.key() == Qt.Key_M: key_event.accept() self.slider.set_closest_marker_to_current_progress() def _save_timings(self): if self.slider.dirty: with open(os.path.join(self.timings_path, "{}.json".format(self._song_number)), 'w') as f: json.dump([marker.progress for marker in self.markers], f, indent=2) def _save_settings(self): # TODO : refactor and use QSettings directly # with open(self.settings_path, 'w') as f: self.settings.update({ 'lyrics_screen': QApplication.desktop().screenNumber(self.songtext_widget), 'control_window_position': self.pos(), }) # json.dump(self.settings, f, indent=2) settings = QSettings("Maccesch", "SongScreen") for key, value in self.settings.items(): settings.setValue(key, value) def _load_settings(self): # try: # with open(self.settings_path, 'r') as f: # settings = json.load(f) settings = QSettings("Maccesch", "SongScreen") if settings.contains('lyrics_screen'): self.display_lyrics_on_screen(settings.value('lyrics_screen')) if settings.contains('control_window_position'): self.move(settings.value('control_window_position')) for key in settings.allKeys(): self.settings[key] = settings.value(key) # self.settings.update(settings) self.songtext_widget.set_font_size(self.settings['font_size']) self.songtext_widget.set_line_increment(self.settings['line_increment']) # except (FileNotFoundError, ValueError): # pass if not os.path.exists(self.lyrics_language_path) or not self.settings['lyrics_language']: languages = list( filter(lambda p: os.path.isdir(os.path.join(self.lyrics_path, p)) and p != "timings", os.listdir(self.lyrics_path)) ) self.settings['lyrics_language'] = languages[0] if languages else ""
class Player(QWidget): fullScreenChanged = pyqtSignal(bool) def __init__(self, playlist, parent=None): super(Player, self).__init__(parent) self.colorDialog = None self.trackInfo = "" self.statusInfo = "" self.duration = 0 self.player = QMediaPlayer() self.playlist = QMediaPlaylist() self.player.setPlaylist(self.playlist) self.player.durationChanged.connect(self.durationChanged) self.player.positionChanged.connect(self.positionChanged) self.player.metaDataChanged.connect(self.metaDataChanged) self.playlist.currentIndexChanged.connect(self.playlistPositionChanged) self.player.mediaStatusChanged.connect(self.statusChanged) self.player.bufferStatusChanged.connect(self.bufferingProgress) self.player.videoAvailableChanged.connect(self.videoAvailableChanged) self.player.error.connect(self.displayErrorMessage) self.videoWidget = VideoWidget() self.player.setVideoOutput(self.videoWidget) self.playlistModel = PlaylistModel() self.playlistModel.setPlaylist(self.playlist) self.playlistView = QListView() self.playlistView.setModel(self.playlistModel) self.playlistView.setCurrentIndex( self.playlistModel.index(self.playlist.currentIndex(), 0)) self.playlistView.activated.connect(self.jump) self.slider = QSlider(Qt.Horizontal) self.slider.setRange(0, self.player.duration() / 1000) self.labelDuration = QLabel() self.slider.sliderMoved.connect(self.seek) # self.labelHistogram = QLabel() # self.labelHistogram.setText("Histogram:") # self.histogram = HistogramWidget() # histogramLayout = QHBoxLayout() # histogramLayout.addWidget(self.labelHistogram) # histogramLayout.addWidget(self.histogram, 1) self.probe = QVideoProbe() # self.probe.videoFrameProbed.connect(self.histogram.processFrame) self.probe.setSource(self.player) openButton = QPushButton("打开", clicked=self.open) controls = PlayerControls() controls.setState(self.player.state()) controls.setVolume(self.player.volume()) controls.setMuted(controls.isMuted()) controls.play.connect(self.player.play) controls.pause.connect(self.player.pause) controls.stop.connect(self.player.stop) controls.next.connect(self.playlist.next) controls.previous.connect(self.previousClicked) controls.changeVolume.connect(self.player.setVolume) controls.changeMuting.connect(self.player.setMuted) controls.changeRate.connect(self.player.setPlaybackRate) controls.stop.connect(self.videoWidget.update) self.player.stateChanged.connect(controls.setState) self.player.volumeChanged.connect(controls.setVolume) self.player.mutedChanged.connect(controls.setMuted) self.fullScreenButton = QPushButton("全屏") self.fullScreenButton.setCheckable(True) self.colorButton = QPushButton("颜色选项") self.colorButton.setEnabled(False) self.colorButton.clicked.connect(self.showColorDialog) displayLayout = QHBoxLayout() displayLayout.addWidget(self.videoWidget, 2) displayLayout.addWidget(self.playlistView) controlLayout = QHBoxLayout() controlLayout.setContentsMargins(0, 0, 0, 0) controlLayout.addWidget(openButton) controlLayout.addStretch(1) controlLayout.addWidget(controls) controlLayout.addStretch(1) controlLayout.addWidget(self.fullScreenButton) controlLayout.addWidget(self.colorButton) layout = QVBoxLayout() layout.addLayout(displayLayout) hLayout = QHBoxLayout() hLayout.addWidget(self.slider) hLayout.addWidget(self.labelDuration) layout.addLayout(hLayout) layout.addLayout(controlLayout) # layout.addLayout(histogramLayout) self.setLayout(layout) if not self.player.isAvailable(): QMessageBox.warning( self, "Service not available", "The QMediaPlayer object does not have a valid service.\n" "Please check the media service plugins are installed.") controls.setEnabled(False) self.playlistView.setEnabled(False) openButton.setEnabled(False) self.colorButton.setEnabled(False) self.fullScreenButton.setEnabled(False) self.metaDataChanged() self.addToPlaylist(playlist) def open(self): fileNames, _ = QFileDialog.getOpenFileNames(self, "Open Files") self.addToPlaylist(fileNames) def addToPlaylist(self, fileNames): for name in fileNames: fileInfo = QFileInfo(name) if fileInfo.exists(): url = QUrl.fromLocalFile(fileInfo.absoluteFilePath()) if fileInfo.suffix().lower() == 'm3u': self.playlist.load(url) else: self.playlist.addMedia(QMediaContent(url)) else: url = QUrl(name) if url.isValid(): self.playlist.addMedia(QMediaContent(url)) def durationChanged(self, duration): duration /= 1000 self.duration = duration self.slider.setMaximum(duration) def positionChanged(self, progress): progress /= 1000 if not self.slider.isSliderDown(): self.slider.setValue(progress) self.updateDurationInfo(progress) def metaDataChanged(self): if self.player.isMetaDataAvailable(): self.setTrackInfo( "%s - %s" % (self.player.metaData(QMediaMetaData.AlbumArtist), self.player.metaData(QMediaMetaData.Title))) def previousClicked(self): # Go to the previous track if we are within the first 5 seconds of # playback. Otherwise, seek to the beginning. if self.player.position() <= 5000: self.playlist.previous() else: self.player.setPosition(0) def jump(self, index): if index.isValid(): self.playlist.setCurrentIndex(index.row()) self.player.play() def playlistPositionChanged(self, position): self.playlistView.setCurrentIndex(self.playlistModel.index( position, 0)) def seek(self, seconds): self.player.setPosition(seconds * 1000) def statusChanged(self, status): self.handleCursor(status) if status == QMediaPlayer.LoadingMedia: self.setStatusInfo("Loading...") elif status == QMediaPlayer.StalledMedia: self.setStatusInfo("Media Stalled") elif status == QMediaPlayer.EndOfMedia: QApplication.alert(self) elif status == QMediaPlayer.InvalidMedia: self.displayErrorMessage() else: self.setStatusInfo("") def handleCursor(self, status): if status in (QMediaPlayer.LoadingMedia, QMediaPlayer.BufferingMedia, QMediaPlayer.StalledMedia): self.setCursor(Qt.BusyCursor) else: self.unsetCursor() def bufferingProgress(self, progress): self.setStatusInfo("Buffering %d%" % progress) def videoAvailableChanged(self, available): if available: self.fullScreenButton.clicked.connect( self.videoWidget.setFullScreen) self.videoWidget.fullScreenChanged.connect( self.fullScreenButton.setChecked) if self.fullScreenButton.isChecked(): self.videoWidget.setFullScreen(True) else: self.fullScreenButton.clicked.disconnect( self.videoWidget.setFullScreen) self.videoWidget.fullScreenChanged.disconnect( self.fullScreenButton.setChecked) self.videoWidget.setFullScreen(False) self.colorButton.setEnabled(available) def setTrackInfo(self, info): self.trackInfo = info if self.statusInfo != "": self.setWindowTitle("%s | %s" % (self.trackInfo, self.statusInfo)) else: self.setWindowTitle(self.trackInfo) def setStatusInfo(self, info): self.statusInfo = info if self.statusInfo != "": self.setWindowTitle("%s | %s" % (self.trackInfo, self.statusInfo)) else: self.setWindowTitle(self.trackInfo) def displayErrorMessage(self): self.setStatusInfo(self.player.errorString()) def updateDurationInfo(self, currentInfo): duration = self.duration if currentInfo or duration: currentTime = QTime((currentInfo / 3600) % 60, (currentInfo / 60) % 60, currentInfo % 60, (currentInfo * 1000) % 1000) totalTime = QTime((duration / 3600) % 60, (duration / 60) % 60, duration % 60, (duration * 1000) % 1000) format = 'hh:mm:ss' if duration > 3600 else 'mm:ss' tStr = currentTime.toString(format) + " / " + totalTime.toString( format) else: tStr = "" self.labelDuration.setText(tStr) def showColorDialog(self): if self.colorDialog is None: brightnessSlider = QSlider(Qt.Horizontal) brightnessSlider.setRange(-100, 100) brightnessSlider.setValue(self.videoWidget.brightness()) brightnessSlider.sliderMoved.connect( self.videoWidget.setBrightness) self.videoWidget.brightnessChanged.connect( brightnessSlider.setValue) contrastSlider = QSlider(Qt.Horizontal) contrastSlider.setRange(-100, 100) contrastSlider.setValue(self.videoWidget.contrast()) contrastSlider.sliderMoved.connect(self.videoWidget.setContrast) self.videoWidget.contrastChanged.connect(contrastSlider.setValue) hueSlider = QSlider(Qt.Horizontal) hueSlider.setRange(-100, 100) hueSlider.setValue(self.videoWidget.hue()) hueSlider.sliderMoved.connect(self.videoWidget.setHue) self.videoWidget.hueChanged.connect(hueSlider.setValue) saturationSlider = QSlider(Qt.Horizontal) saturationSlider.setRange(-100, 100) saturationSlider.setValue(self.videoWidget.saturation()) saturationSlider.sliderMoved.connect( self.videoWidget.setSaturation) self.videoWidget.saturationChanged.connect( saturationSlider.setValue) layout = QFormLayout() layout.addRow("亮度", brightnessSlider) layout.addRow("对比度", contrastSlider) layout.addRow("色调", hueSlider) layout.addRow("饱和度", saturationSlider) button = QPushButton("关闭") layout.addRow(button) self.colorDialog = QDialog(self) self.colorDialog.setWindowTitle("颜色选项") self.colorDialog.setLayout(layout) button.clicked.connect(self.colorDialog.close) self.colorDialog.show()
class Player(QWidget): fullScreenChanged = pyqtSignal(bool) def __init__(self, playlist, parent=None): super(Player, self).__init__(parent) self.colorDialog = None self.trackInfo = "" self.statusInfo = "" self.duration = 0 self.player = QMediaPlayer() self.playlist = QMediaPlaylist() self.player.setPlaylist(self.playlist) self.player.durationChanged.connect(self.durationChanged) self.player.positionChanged.connect(self.positionChanged) self.player.metaDataChanged.connect(self.metaDataChanged) self.playlist.currentIndexChanged.connect(self.playlistPositionChanged) self.player.mediaStatusChanged.connect(self.statusChanged) self.player.bufferStatusChanged.connect(self.bufferingProgress) self.player.videoAvailableChanged.connect(self.videoAvailableChanged) self.player.error.connect(self.displayErrorMessage) self.videoWidget = VideoWidget() self.player.setVideoOutput(self.videoWidget) self.playlistModel = PlaylistModel() self.playlistModel.setPlaylist(self.playlist) self.playlistView = QListView() self.playlistView.setModel(self.playlistModel) self.playlistView.setCurrentIndex( self.playlistModel.index(self.playlist.currentIndex(), 0)) self.playlistView.activated.connect(self.jump) self.script_box = QPlainTextEdit() self.segmentList = QTreeWidget() self.segmentList.setSortingEnabled(True) #self.segmentList.setColumnCount(5) self.segmentList.setColumnCount(4) #self.segmentList.setHeaderLabels(['Product','Start','Label','Tool','Behavior']) self.segmentList.setHeaderLabels(['Start segment', 'End segment', 'Label', 'Event']) ''' self.productTextInput = QLineEdit() self.startTextInput = QLineEdit() self.labelTextInput = QLineEdit() self.toolTextInput = QLineEdit() self.behaviorTextInput = QLineEdit() ''' self.startTextInput = QLineEdit() self.endTextInput = QLineEdit() self.labelTextInput = QLineEdit() self.contentTextInput = QLineEdit() self.addBtn = QPushButton("Add") self.addBtn.clicked.connect(self.addSegment) self.saveBtn = QPushButton("Save") self.saveBtn.clicked.connect(self.saveSegments) self.slider = QSlider(Qt.Horizontal) self.slider.setRange(0, self.player.duration() / 1000) self.labelDuration = QLabel() self.slider.sliderMoved.connect(self.seek) self.labelHistogram = QLabel() self.labelHistogram.setText("Histogram:") self.histogram = HistogramWidget() histogramLayout = QHBoxLayout() histogramLayout.addWidget(self.labelHistogram) histogramLayout.addWidget(self.histogram, 1) self.probe = QVideoProbe() self.probe.videoFrameProbed.connect(self.histogram.processFrame) self.probe.setSource(self.player) openButton = QPushButton("Open", clicked=self.open) if os.path.isdir(VIDEO_DIR): self.open_folder(VIDEO_DIR) controls = PlayerControls() controls.setState(self.player.state()) controls.setVolume(self.player.volume()) controls.setMuted(controls.isMuted()) controls.play.connect(self.player.play) controls.pause.connect(self.player.pause) controls.stop.connect(self.player.stop) controls.next.connect(self.playlist.next) controls.previous.connect(self.previousClicked) controls.changeVolume.connect(self.player.setVolume) controls.changeMuting.connect(self.player.setMuted) controls.changeRate.connect(self.player.setPlaybackRate) controls.stop.connect(self.videoWidget.update) self.player.stateChanged.connect(controls.setState) self.player.volumeChanged.connect(controls.setVolume) self.player.mutedChanged.connect(controls.setMuted) #self.segmentButton = QPushButton("Segment") #self.segmentButton.clicked.connect(self.createNewSegment) self.startSegmentButton = QPushButton("Start Segment") self.startSegmentButton.clicked.connect(self.createNewStartSegment) # self.segmentButton.setCheckable(True) self.endSegmentButton = QPushButton("End Segment") self.endSegmentButton.clicked.connect(self.createNewEndSegment) #self.fullScreenButton = QPushButton("FullScreen") #self.fullScreenButton.setCheckable(True) self.colorButton = QPushButton("Color Options...") self.colorButton.setEnabled(False) self.colorButton.clicked.connect(self.showColorDialog) displayLayout = QHBoxLayout() # videoLayout = QVBoxLayout() # videoLayout.addWidget(self.videoWidget) # videoLayout.addWidget(self.script_box) displayLayout.addWidget(self.videoWidget, 3) editLayout = QVBoxLayout() editLayout.addWidget(self.playlistView, 2) #editLayout.addWidget(self.script_box, 4) editLayout.addWidget(self.segmentList, 3) segmentInputLayout = QHBoxLayout() ''' segmentInputLayout.addWidget(self.productTextInput) segmentInputLayout.addWidget(self.startTextInput) segmentInputLayout.addWidget(self.labelTextInput) segmentInputLayout.addWidget(self.toolTextInput) segmentInputLayout.addWidget(self.behaviorTextInput) ''' segmentInputLayout.addWidget(self.startTextInput) segmentInputLayout.addWidget(self.endTextInput) segmentInputLayout.addWidget(self.labelTextInput) segmentInputLayout.addWidget(self.contentTextInput) editLayout.addLayout(segmentInputLayout,1) displayLayout.addLayout(editLayout, 2) controlLayout = QHBoxLayout() controlLayout.setContentsMargins(0, 0, 0, 0) controlLayout.addWidget(openButton) controlLayout.addStretch(1) controlLayout.addWidget(controls) controlLayout.addStretch(1) #controlLayout.addWidget(self.segmentButton) controlLayout.addWidget(self.startSegmentButton) controlLayout.addWidget(self.endSegmentButton) controlLayout.addWidget(self.addBtn) controlLayout.addWidget(self.saveBtn) #controlLayout.addWidget(self.fullScreenButton) # controlLayout.addWidget(self.colorButton) layout = QVBoxLayout() layout.addLayout(displayLayout, 2) hLayout = QHBoxLayout() hLayout.addWidget(self.slider) hLayout.addWidget(self.labelDuration) layout.addLayout(hLayout) layout.addLayout(controlLayout) # layout.addLayout(histogramLayout) self.setLayout(layout) if not self.player.isAvailable(): QMessageBox.warning(self, "Service not available", "The QMediaPlayer object does not have a valid service.\n" "Please check the media service plugins are installed.") controls.setEnabled(False) self.playlistView.setEnabled(False) openButton.setEnabled(False) self.colorButton.setEnabled(False) #self.fullScreenButton.setEnabled(False) self.metaDataChanged() self.addToPlaylist(playlist) def open(self): fileNames, _ = QFileDialog.getOpenFileNames(self, "Open Files") self.addToPlaylist(fileNames) def open_folder(self, folder_path): fileNames = [folder_path+x for x in os.listdir(folder_path) if x.endswith('.mp4')] self.addToPlaylist(fileNames) def addToPlaylist(self, fileNames): for name in fileNames: fileInfo = QFileInfo(name) if fileInfo.exists(): url = QUrl.fromLocalFile(fileInfo.absoluteFilePath()) if fileInfo.suffix().lower() == 'm3u': self.playlist.load(url) else: self.playlist.addMedia(QMediaContent(url)) else: url = QUrl(name) if url.isValid(): self.playlist.addMedia(QMediaContent(url)) def addSegment(self): item = TreeWidgetItem(self.segmentList) ''' item.setText(0, self.productTextInput.text()) item.setText(1, self.startTextInput.text()) item.setText(2, self.labelTextInput.text()) item.setText(3, self.toolTextInput.text()) item.setText(4, self.behaviorTextInput.text()) ''' item.setText(0, self.startTextInput.text()) item.setText(1, self.endTextInput.text()) item.setText(2, self.labelTextInput.text()) item.setText(3, self.contentTextInput.text()) item.setFlags(item.flags() | Qt.ItemIsEditable) self.segmentList.addTopLevelItem(item) self.segmentList.sortByColumn(0, Qt.AscendingOrder) self.clear_input_boxes() self.player.play() def saveSegments(self): itemCnt = self.segmentList.topLevelItemCount() colCnt = self.segmentList.columnCount() save_dict = {'segments':[]} for i in range(itemCnt): item = self.segmentList.topLevelItem(i) temp_data = [] for j in range(colCnt): temp_data.append(item.text(j)) #temp_dict = {'product': temp_data[0], 'start': temp_data[1], 'label': temp_data[2], 'tool': temp_data[3], 'behavior': temp_data[4]} if len(temp_data[0]) > 0 and len(temp_data[1]) > 0 and (':' in temp_data[0]) and (':' in temp_data[1]): start_interval_seconds = 0 j = 0 while j < len(temp_data[0].split(':')): start_interval_seconds += (int(temp_data[0].split(':')[- 1 - j]) * (60 ** j)) j += 1 end_interval_seconds = 0 j = 0 while j < len(temp_data[1].split(':')): end_interval_seconds += (int(temp_data[1].split(':')[- 1 - j]) * (60 ** j)) j += 1 else: start_interval_seconds = '' end_interval_seconds = '' temp_dict = {'start_segment': start_interval_seconds, 'end_segment': end_interval_seconds, 'label': temp_data[2], 'event': temp_data[3]} save_dict['segments'].append(temp_dict) import json file_name = self.playlist.currentMedia().canonicalUrl().fileName() with open(SEGMENT_DIR+file_name.replace('.mp4','.json'),'w') as file: json.dump(save_dict, file) def durationChanged(self, duration): duration /= 1000 self.duration = duration self.slider.setMaximum(duration) def positionChanged(self, progress): progress /= 1000 if not self.slider.isSliderDown(): self.slider.setValue(progress) self.updateDurationInfo(progress) def metaDataChanged(self): if self.player.isMetaDataAvailable(): self.setTrackInfo("%s - %s" % ( self.player.metaData(QMediaMetaData.AlbumArtist), self.player.metaData(QMediaMetaData.Title))) def previousClicked(self): # Go to the previous track if we are within the first 5 seconds of # playback. Otherwise, seek to the beginning. if self.player.position() <= 5000: self.playlist.previous() else: self.player.setPosition(0) def clear_input_boxes(self): ''' self.productTextInput.clear() self.startTextInput.clear() self.labelTextInput.clear() self.toolTextInput.clear() self.behaviorTextInput.clear() ''' self.startTextInput.clear() self.endTextInput.clear() self.labelTextInput.clear() self.contentTextInput.clear() def jump(self, index): if index.isValid(): self.playlist.setCurrentIndex(index.row()) self.player.play() file_name = self.playlist.currentMedia().canonicalUrl().fileName() ''' script_file_name = file_name.replace('.mp4','.txt') if os.path.isfile(SCRIPT_DIR+script_file_name): text=open(SCRIPT_DIR+script_file_name).read() self.script_box.setPlainText(text) ''' segment_file_path = SEGMENT_DIR + file_name.replace('.mp4','.json') json_dict = self.open_json(segment_file_path) self.clear_input_boxes() self.segmentList.clear() for segment in json_dict["segments"]: item = TreeWidgetItem(self.segmentList) ''' item.setText(0, segment['product']) item.setText(1, str(segment['start'])) item.setText(2, segment['label']) item.setText(3, segment['tool']) item.setText(4, segment['behavior']) ''' item.setText(0, segment['start_segment']) item.setText(1, segment['end_segment']) item.setText(2, segment['label']) item.setText(3, segment['content']) item.setFlags(item.flags() | Qt.ItemIsEditable) self.segmentList.addTopLevelItem(item) # print([str(x.text()) for x in self.segmentList.currentItem()]) def open_json(self, file_path): import json try: with open(file_path, 'r') as file: json_dict = json.loads(file.read()) except: json_dict = {"segments":[]} # json_dict = {"segments":[{"product":"Sorry","start":"File not found.","label":"","tool":"","behavior":""}]} return json_dict def playlistPositionChanged(self, position): self.playlistView.setCurrentIndex( self.playlistModel.index(position, 0)) def seek(self, seconds): self.player.setPosition(seconds * 1000) def statusChanged(self, status): self.handleCursor(status) if status == QMediaPlayer.LoadingMedia: self.setStatusInfo("Loading...") elif status == QMediaPlayer.StalledMedia: self.setStatusInfo("Media Stalled") elif status == QMediaPlayer.EndOfMedia: QApplication.alert(self) elif status == QMediaPlayer.InvalidMedia: self.displayErrorMessage() else: self.setStatusInfo("") def handleCursor(self, status): if status in (QMediaPlayer.LoadingMedia, QMediaPlayer.BufferingMedia, QMediaPlayer.StalledMedia): self.setCursor(Qt.BusyCursor) else: self.unsetCursor() def bufferingProgress(self, progress): self.setStatusInfo("Buffering %d%" % progress) def videoAvailableChanged(self, available): ''' if available: self.fullScreenButton.clicked.connect( self.videoWidget.setFullScreen) self.videoWidget.fullScreenChanged.connect( self.fullScreenButton.setChecked) if self.fullScreenButton.isChecked(): self.videoWidget.setFullScreen(True) else: self.fullScreenButton.clicked.disconnect( self.videoWidget.setFullScreen) self.videoWidget.fullScreenChanged.disconnect( self.fullScreenButton.setChecked) self.videoWidget.setFullScreen(False) ''' self.colorButton.setEnabled(available) def setTrackInfo(self, info): self.trackInfo = info if self.statusInfo != "": self.setWindowTitle("%s | %s" % (self.trackInfo, self.statusInfo)) else: self.setWindowTitle(self.trackInfo) def setStatusInfo(self, info): self.statusInfo = info if self.statusInfo != "": self.setWindowTitle("%s | %s" % (self.trackInfo, self.statusInfo)) else: self.setWindowTitle(self.trackInfo) def displayErrorMessage(self): self.setStatusInfo(self.player.errorString()) def updateDurationInfo(self, currentInfo): duration = self.duration if currentInfo or duration: currentTime = QTime((currentInfo/3600)%60, (currentInfo/60)%60, currentInfo%60, (currentInfo*1000)%1000) totalTime = QTime((duration/3600)%60, (duration/60)%60, duration%60, (duration*1000)%1000); format = 'hh:mm:ss' if duration > 3600 else 'mm:ss' tStr = currentTime.toString(format) + " / " + totalTime.toString(format) else: tStr = "" self.labelDuration.setText(tStr) ''' def createNewSegment(self): self.startTextInput.setText(str(int(self.player.position()/1000))) ''' def createNewStartSegment(self): seconds = int(self.player.position()/1000) self.startTextInput.setText("{:02d}".format(math.floor(seconds / 3600)) + ':' + "{:02d}".format( math.floor((seconds / 60)) - math.floor(seconds / 3600) * 60) + ':' + "{:02d}".format(seconds % 60)) def createNewEndSegment(self): seconds = int(self.player.position() / 1000) self.endTextInput.setText("{:02d}".format(math.floor(seconds / 3600)) + ':' + "{:02d}".format( math.floor((seconds / 60)) - math.floor(seconds / 3600) * 60) + ':' + "{:02d}".format(seconds % 60)) self.player.pause() def showColorDialog(self): if self.colorDialog is None: brightnessSlider = QSlider(Qt.Horizontal) brightnessSlider.setRange(-100, 100) brightnessSlider.setValue(self.videoWidget.brightness()) brightnessSlider.sliderMoved.connect( self.videoWidget.setBrightness) self.videoWidget.brightnessChanged.connect( brightnessSlider.setValue) contrastSlider = QSlider(Qt.Horizontal) contrastSlider.setRange(-100, 100) contrastSlider.setValue(self.videoWidget.contrast()) contrastSlider.sliderMoved.connect(self.videoWidget.setContrast) self.videoWidget.contrastChanged.connect(contrastSlider.setValue) hueSlider = QSlider(Qt.Horizontal) hueSlider.setRange(-100, 100) hueSlider.setValue(self.videoWidget.hue()) hueSlider.sliderMoved.connect(self.videoWidget.setHue) self.videoWidget.hueChanged.connect(hueSlider.setValue) saturationSlider = QSlider(Qt.Horizontal) saturationSlider.setRange(-100, 100) saturationSlider.setValue(self.videoWidget.saturation()) saturationSlider.sliderMoved.connect( self.videoWidget.setSaturation) self.videoWidget.saturationChanged.connect( saturationSlider.setValue) layout = QFormLayout() layout.addRow("Brightness", brightnessSlider) layout.addRow("Contrast", contrastSlider) layout.addRow("Hue", hueSlider) layout.addRow("Saturation", saturationSlider) button = QPushButton("Close") layout.addRow(button) self.colorDialog = QDialog(self) self.colorDialog.setWindowTitle("Color Options") self.colorDialog.setLayout(layout) button.clicked.connect(self.colorDialog.close) self.colorDialog.show()
class Player(Qt.QWidget): """docstring for Player""" fullScreenChanged = Qt.pyqtSignal(bool) def __init__(self, playlist, parent=None): # create player super(Player, self).__init__(parent) self.trackInfo = '' self.statusInfo = '' self.duration = 0 # create player object self.player = QMediaPlayer() self.playlist = QMediaPlaylist() self.name = 'Current playlist' self.player.setPlaylist(self.playlist) self.player.durationChanged.connect(self.durationChanged) self.player.positionChanged.connect(self.positionChanged) self.player.metaDataChanged.connect(self.metaDataChanged) self.playlist.currentIndexChanged.connect(self.playlistPositionChanged) self.player.mediaStatusChanged.connect(self.statusChanged) self.player.bufferStatusChanged.connect(self.bufferingProgress) # self.player.videoAvailableChanged.connect(self.videoAvailableChanged) self.player.error.connect(self.displayErrorMessage) # connect with VideoWidget # self.videoWidget = VideoWidget() # self.player.setVideoOutput(self.videoWidget) # connect with PlaylistModel self.playlistModel = PlaylistModel() self.playlistModel.setPlaylist(self.playlist) self.playlistView = Qt.QListView() self.playlistView.setModel(self.playlistModel) self.playlistView.setCurrentIndex(self.playlistModel.index( self.playlist.currentIndex(), 0)) # change to next song self.playlistView.activated.connect(self.jump) self.slider = Qt.QSlider(QtCore.Qt.Horizontal) self.slider.setRange(0, self.player.duration() / 1000) self.labelDuration = Qt.QLabel() self.slider.sliderMoved.connect(self.seek) # create histogram self.labelHistogram = Qt.QLabel() self.labelHistogram.setText('Histogram: ') self.histogram = HistogramWidget() histogramLayout = Qt.QHBoxLayout() histogramLayout.addWidget(self.labelHistogram) histogramLayout.addWidget(self.histogram, 1) # create videoProbe self.videoProbe = Qt.QVideoProbe() self.videoProbe.videoFrameProbed.connect(self.histogram.processFrame) self.videoProbe.setSource(self.player) # add control controls = Controllers() controls.setState(self.player.state()) controls.setVolume(self.player.volume()) controls.setMuted(controls.isMuted()) # connect player's controls with Controllers controls.play.connect(self.player.play) controls.pause.connect(self.player.pause) controls.stop.connect(self.player.stop) controls.next.connect(self.playlist.next) controls.previous.connect(self.previousAction) controls.changeVolume.connect(self.player.setVolume) controls.changeMuting.connect(self.player.setMuted) # setPlaybackRate is from QMediaPlayer controls.changeSpeed.connect(self.player.setPlaybackRate) # controls.stop.connect(self.videoWidget.update) self.player.stateChanged.connect(controls.setState) self.player.volumeChanged.connect(controls.setVolume) self.player.mutedChanged.connect(controls.setMuted) # create fullScreenButton # self.fullScreenButton = Qt.QPushButton('FullScreen') # self.fullScreenButton.setCheckable(True) # displayLayout displayLayout = Qt.QHBoxLayout() # displayLayout.addWidget(self.videoWidget, 2) displayLayout.addWidget(self.playlistView) # controlLayout controlLayout = Qt.QHBoxLayout() controlLayout.setContentsMargins(0, 0, 0, 0) # connect controlLayout with controls controlLayout.addWidget(controls) controlLayout.addStretch(1) # connect controlLayout with fullScreenButton # controlLayout.addWidget(self.fullScreenButton) # visualize player layout = Qt.QVBoxLayout() layout.addLayout(displayLayout) # layout for sliding song playing hLayout = Qt.QHBoxLayout() hLayout.addWidget(self.slider) hLayout.addWidget(self.labelDuration) layout.addLayout(hLayout) layout.addLayout(controlLayout) layout.addLayout(histogramLayout) # set icon self.setWindowIcon(Qt.QIcon('favicon.ico')) # create menus toolBar = Qt.QToolBar() # create basic actions self.createActions() # create simple button to repeat song self.repeatButton = Qt.QToolButton() self.repeatButton.setDefaultAction(self.repeatAct) # create playOnceButton self.playOnceButton = Qt.QToolButton() self.playOnceButton.setDefaultAction(self.playOnceAct) self.playOnceButton.setEnabled(False) # create shuffleButton self.shuffleButton = Qt.QToolButton() self.shuffleButton.setDefaultAction(self.shuffleAct) # create sequentialButton self.sequentialButton = Qt.QToolButton() self.sequentialButton.setDefaultAction(self.sequentialAct) # create fileButton for fileMenu fileButton = Qt.QToolButton() fileButton.setText('File') fileButton.setPopupMode(Qt.QToolButton.MenuButtonPopup) fileButton.setMenu(self.popFileMenu()) # create editButton for editMenu closeButton = Qt.QToolButton() closeButton.setText('Edit') closeButton.setDefaultAction(self.fileCloseAct) # display in toolBar these buttons toolBar.addWidget(self.repeatButton) toolBar.addWidget(self.playOnceButton) toolBar.addWidget(self.shuffleButton) toolBar.addWidget(self.sequentialButton) toolBar.addWidget(fileButton) toolBar.addWidget(closeButton) # add toolBar to layout of the player layout.addWidget(toolBar) layout.addWidget(Qt.QGroupBox()) self.setWindowTitle("Python Music Player") self.setLayout(layout) if not self.player.isAvailable(): Qt.QMessageBox(self, 'Unavailable service') # self.displayErrorMessage() controls.setEnabled(False) self.playlistView.setEnabled(False) self.fullScreenButton.setEnabled(False) self.metaDataChanged() self.addToPlaylist(playlist) # create fileMenu def popFileMenu(self): aMenu = Qt.QMenu(self) aMenu.addAction(self.fileOpenAct) aMenu.addAction(self.fileCloseAct) return aMenu def createActions(self): self.repeatAct = Qt.QAction('Repeat', self, triggered=self.repeatSong) self.playOnceAct = Qt.QAction( 'Play once', self, triggered=self.playOnceSong) self.shuffleAct = Qt.QAction( 'Shuffle', self, triggered=self.playlist.shuffle) self.sequentialAct = Qt.QAction( 'Sequential', self, triggered=self.playSequential) self.fileOpenAct = Qt.QAction('Open', self, triggered=self.open) self.fileOpenAct.setShortcut('Ctrl+O') self.fileCloseAct = Qt.QAction('Close', self, triggered=self.close) self.fileCloseAct.setShortcut('Ctrl+Q') def repeatSong(self): self.playlist.setPlaybackMode(QMediaPlaylist.CurrentItemInLoop) self.repeatButton.setEnabled(False) self.playOnceButton.setEnabled(True) def playOnceSong(self): self.playlist.setPlaybackMode(QMediaPlaylist.Sequential) self.playOnceButton.setEnabled(False) self.repeatButton.setEnabled(True) # unproperly used def playSequential(self): self.playlist.setPlaybackMode(QMediaPlaylist.Sequential) # get and display song duration def durationChanged(self, duration): duration /= 1000 self.duration = duration self.slider.setMaximum(duration) # change slider position def positionChanged(self, progress): progress /= 1000 if not self.slider.isSliderDown(): self.slider.setValue(progress) self.updateDurationInfo(progress) def updateDurationInfo(self, currentInfo): duration = self.duration if currentInfo or duration: currentTime = QtCore.QTime( (currentInfo / 3600) % 60, # hours (currentInfo / 60) % 60, # minutes currentInfo % 60, # seconds (currentInfo * 1000) % 1000) # miliseconds totalTime = QtCore.QTime( (duration / 3600) % 60, # hours (duration / 60) % 60, # minutes duration % 60, # seconds (duration * 1000) % 1000) # miliseconds formating = 'hh:mm:ss' if duration > 3600 else 'mm:ss' toString = (currentTime.toString(formating) + ' / ' + totalTime.toString(formating)) else: toString = '' self.labelDuration.setText(toString) def metaDataChanged(self): if self.player.isMetaDataAvailable(): self.setTrackInfo('{0} - {1}'.format( self.player.metaData(Qt.QMediaMetaData.AlbumArtist), self.player.metaData(Qt.QMediaMetaData.Title))) def setTrackInfo(self, info): self.trackInfo = info if self.statusInfo: self.setWindowTitle('{0} | {1}'.format( self.trackInfo, self.statusInfo)) else: self.setWindowTitle(self.trackInfo) def playlistPositionChanged(self, position): self.playlistView.setCurrentIndex( self.playlistModel.index(position, 0)) def statusChanged(self, status): self.handleCursor(status) if status == QMediaPlayer.LoadingMedia: self.setStatusInfo('Loading...') elif status == QMediaPlayer.StalledMedia: self.setStatusInfo('Media Stalled') elif status == QMediaPlayer.EndOfMedia: Qt.QApplication.alert(self) elif status == QMediaPlayer.InvalidMedia: self.displayErrorMessage() else: self.setStatusInfo('') def handleCursor(self, status): if status in [QMediaPlayer.LoadingMedia, QMediaPlayer.BufferingMedia, QMediaPlayer.StalledMedia]: self.setCursor(QtCore.Qt.BusyCursor) else: self.unsetCursor() def setStatusInfo(self, info): self.statusInfo = info if self.statusInfo: self.setWindowTitle('{0} | {1}'.format( self.trackInfo, self.statusInfo)) else: self.setWindowTitle(self.trackInfo) def bufferingProgress(self, progress): self.setStatusInfo('Buffering {0}'.format(progress)) def displayErrorMessage(self): self.statusInfo(self.player.errorString()) def jump(self, index): if index.isValid(): self.playlist.setCurrentIndex(index.row()) self.player.play() def seek(self, seconds): self.player.setPosition(seconds * 1000) def previousAction(self): self.playlist.previous() def close(self): choice = Qt.QMessageBox.question( self, 'Close', 'Close the app?', Qt.QMessageBox.Yes | Qt.QMessageBox.No) if choice == Qt.QMessageBox.Yes: sys.exit() def open(self): names, _ = Qt.QFileDialog.getOpenFileNames(self, 'Open Files') # ['/home/milka/Documents/MusicPlayer/song.mp3'] self.addToPlaylist(names) def addToPlaylist(self, names): for name in names: fileInfo = Qt.QFileInfo(name) if fileInfo.exists(): url = QtCore.QUrl.fromLocalFile(fileInfo.absoluteFilePath()) # save_to_db song url create_song( url.path(), self.duration, playlist_name=self.name) if fileInfo.suffix().lower() == 'm3u': self.playlist.load(url) else: self.playlist.addMedia(Qt.QMediaContent(url)) else: url = QtCore.QUrl(name) if url.isValid(): self.playlist.addMedia(Qt.QMediaContent(url))
class AudioPlayerPage(QWidget): about_play_audio = pyqtSignal(str) def __init__(self): super().__init__() self.audio_list_widget = QListWidget() self.audio_list_widget.installEventFilter(self) self.audio_list_widget.itemDoubleClicked.connect(self.play) # TODO: playlist объединить с audio_list_widget (см примеры работы с QMediaPlayer) self.playlist = QMediaPlaylist() self.playlist.currentIndexChanged.connect( lambda row: self.audio_list_widget.setCurrentRow(row)) # TODO: обрабатывать сигналы плеера: http://doc.qt.io/qt-5/qmediaplayer.html#signals self.player = QMediaPlayer() self.player.setPlaylist(self.playlist) self.player.currentMediaChanged.connect( lambda media: self.about_play_audio.emit(self.audio_list_widget. currentItem().text())) if not self.player.isAvailable(): # TODO: перевод text = "The QMediaPlayer object does not have a valid service.\n" \ "Please check the media service plugins are installed." log.warning(text) QMessageBox.warning(self, "Service not available", text) quit() self.controls = PlayerControls(self.player) self.controls.set_state(self.player.state()) self.controls.set_volume(self.player.volume()) self.controls.set_muted(self.controls.is_muted()) self.controls.play_signal.connect(self.play) self.controls.pause_signal.connect(self.player.pause) self.controls.stop_signal.connect(self.player.stop) self.controls.next_signal.connect(self.playlist.next) self.controls.previous_signal.connect(self.playlist.previous) self.controls.change_volume_signal.connect(self.player.setVolume) self.controls.change_muting_signal.connect(self.player.setMuted) self.progress = QProgressBar() self.progress.hide() layout = QVBoxLayout() layout.addWidget(self.controls) layout.addWidget(self.audio_list_widget) layout.addWidget(self.progress) self.setLayout(layout) self.thread = LoadAudioListThread() self.thread.about_add_audio.connect(self._add_audio) self.thread.about_progress.connect(self.progress.setValue) self.thread.about_range_progress.connect(self.progress.setRange) self.thread.started.connect(self._start) self.thread.finished.connect(self._finished) def _add_audio(self, title, url): item = QListWidgetItem(title) item.setData(Qt.UserRole, url) self.audio_list_widget.addItem(item) self.playlist.addMedia(QMediaContent(QUrl(url))) # При добавлении первой аудизаписи, вызываем воспроизведение if self.audio_list_widget.count() == 1: self.audio_list_widget.setCurrentRow(0) self.playlist.setCurrentIndex(0) self.play() def _start(self): self.audio_list_widget.clear() self.playlist.clear() self.progress.show() def _finished(self): self.progress.hide() def fill(self, vk): self.thread.vk = vk # Если поток запущен, останавливаем его, иначе -- запускаем if self.thread.isRunning(): self.thread.exit() else: self.thread.start() def play(self): if self.playlist.currentIndex() != self.audio_list_widget.currentRow(): self.playlist.setCurrentIndex(self.audio_list_widget.currentRow()) self.player.play() def eventFilter(self, obj, event): # Воспроизведение видео при клике на кнопки Enter/Return в плейлисте if obj == self.audio_list_widget and event.type( ) == QKeyEvent.KeyPress: if self.audio_list_widget.hasFocus() and event.key( ) == Qt.Key_Return or event.key() == Qt.Key_Enter: item = self.audio_list_widget.currentItem() if item is not None: self.play() return super().eventFilter(obj, event)
class Player(QWidget): fullScreenChanged = pyqtSignal(bool) def __init__(self, playlist, parent=None, add_button = None): super(Player, self).__init__(parent) self.add_button = add_button self.colorDialog = None self.trackInfo = "" self.statusInfo = "" self.duration = 0 self.player = QMediaPlayer() self.playlist = QMediaPlaylist() self.player.setPlaylist(self.playlist) self.player.durationChanged.connect(self.durationChanged) self.player.positionChanged.connect(self.positionChanged) self.player.metaDataChanged.connect(self.metaDataChanged) self.playlist.currentIndexChanged.connect(self.playlistPositionChanged) self.player.mediaStatusChanged.connect(self.statusChanged) self.player.bufferStatusChanged.connect(self.bufferingProgress) self.player.error.connect(self.displayErrorMessage) self.videoWidget = VideoWidget() self.player.setVideoOutput(self.videoWidget) self.playlistModel = PlaylistModel() self.playlistModel.setPlaylist(self.playlist) self.playlistView = QListView() self.playlistView.setModel(self.playlistModel) self.playlistView.setCurrentIndex( self.playlistModel.index(self.playlist.currentIndex(), 0)) self.playlistView.activated.connect(self.jump) self.slider = QSlider(Qt.Horizontal) self.slider.setRange(0, self.player.duration() / 1000) self.labelDuration = QLabel() self.slider.sliderMoved.connect(self.seek) openButton = QPushButton("Open Audio/Video File", clicked=self.open) controls = PlayerControls() controls.setState(self.player.state()) controls.setVolume(self.player.volume()) controls.setMuted(controls.isMuted()) controls.play.connect(self.player.play) controls.pause.connect(self.player.pause) controls.stop.connect(self.player.stop) controls.next.connect(self.playlist.next) controls.previous.connect(self.previousClicked) controls.changeVolume.connect(self.player.setVolume) controls.changeMuting.connect(self.player.setMuted) controls.changeRate.connect(self.player.setPlaybackRate) controls.stop.connect(self.videoWidget.update) self.player.stateChanged.connect(controls.setState) self.player.volumeChanged.connect(controls.setVolume) self.player.mutedChanged.connect(controls.setMuted) displayLayout = QHBoxLayout() displayLayout.addWidget(self.videoWidget, 2) displayLayout.addWidget(self.playlistView) controlLayout = QHBoxLayout() controlLayout.setContentsMargins(0, 0, 0, 0) controlLayout.addWidget(openButton) # button to add decoder if add_button: add_decoder_btn = QPushButton("Decode Keystrokes of Selected Media") add_decoder_btn.clicked.connect(add_button) controlLayout.addWidget(add_decoder_btn) # controlLayout.addStretch(1) controlLayout.addWidget(controls) layout = QVBoxLayout() layout.addLayout(displayLayout) hLayout = QHBoxLayout() hLayout.addWidget(self.slider) hLayout.addWidget(self.labelDuration) layout.addLayout(hLayout) layout.addLayout(controlLayout) self.setLayout(layout) if not self.player.isAvailable(): QMessageBox.warning(self, "Service not available", "The QMediaPlayer object does not have a valid service.\n" "Please check the media service plugins are installed.") controls.setEnabled(False) self.playlistView.setEnabled(False) openButton.setEnabled(False) self.metaDataChanged() self.addToPlaylist(playlist) def get_current_file(self): inds = self.playlistView.selectedIndexes() if len(inds) == 1: index = inds[0] location = self.playlistModel.m_playlist.media(index.row()).canonicalUrl() return location.path() def open(self): fileNames, _ = QFileDialog.getOpenFileNames(self, "Open Files") self.addToPlaylist(fileNames) def addToPlaylist(self, fileNames): for name in fileNames: fileInfo = QFileInfo(name) if fileInfo.exists(): url = QUrl.fromLocalFile(fileInfo.absoluteFilePath()) if fileInfo.suffix().lower() == 'm3u': self.playlist.load(url) else: self.playlist.addMedia(QMediaContent(url)) else: url = QUrl(name) if url.isValid(): self.playlist.addMedia(QMediaContent(url)) def durationChanged(self, duration): duration /= 1000 self.duration = duration self.slider.setMaximum(duration) def positionChanged(self, progress): progress /= 1000 if not self.slider.isSliderDown(): self.slider.setValue(progress) self.updateDurationInfo(progress) def metaDataChanged(self): if self.player.isMetaDataAvailable(): self.setTrackInfo("%s - %s" % ( self.player.metaData(QMediaMetaData.AlbumArtist), self.player.metaData(QMediaMetaData.Title))) def previousClicked(self): # Go to the previous track if we are within the first 5 seconds of # playback. Otherwise, seek to the beginning. if self.player.position() <= 5000: self.playlist.previous() else: self.player.setPosition(0) def jump(self, index): if index.isValid(): self.playlist.setCurrentIndex(index.row()) self.player.play() def playlistPositionChanged(self, position): self.playlistView.setCurrentIndex( self.playlistModel.index(position, 0)) def seek(self, seconds): self.player.setPosition(seconds * 1000) def statusChanged(self, status): self.handleCursor(status) if status == QMediaPlayer.LoadingMedia: self.setStatusInfo("Loading...") elif status == QMediaPlayer.StalledMedia: self.setStatusInfo("Media Stalled") elif status == QMediaPlayer.EndOfMedia: QApplication.alert(self) elif status == QMediaPlayer.InvalidMedia: self.displayErrorMessage() else: self.setStatusInfo("") def handleCursor(self, status): if status in (QMediaPlayer.LoadingMedia, QMediaPlayer.BufferingMedia, QMediaPlayer.StalledMedia): self.setCursor(Qt.BusyCursor) else: self.unsetCursor() def bufferingProgress(self, progress): self.setStatusInfo("Buffering %d%" % progress) def setTrackInfo(self, info): self.trackInfo = info if self.statusInfo != "": self.setWindowTitle("%s | %s" % (self.trackInfo, self.statusInfo)) else: self.setWindowTitle(self.trackInfo) def setStatusInfo(self, info): self.statusInfo = info if self.statusInfo != "": self.setWindowTitle("%s | %s" % (self.trackInfo, self.statusInfo)) else: self.setWindowTitle(self.trackInfo) def displayErrorMessage(self): self.setStatusInfo(self.player.errorString()) def updateDurationInfo(self, currentInfo): duration = self.duration if currentInfo or duration: currentTime = QTime((currentInfo/3600)%60, (currentInfo/60)%60, currentInfo%60, (currentInfo*1000)%1000) totalTime = QTime((duration/3600)%60, (duration/60)%60, duration%60, (duration*1000)%1000); format = 'hh:mm:ss' if duration > 3600 else 'mm:ss' tStr = currentTime.toString(format) + " / " + totalTime.toString(format) else: tStr = "" self.labelDuration.setText(tStr)
class AudioPlayer(QObject): songPositionChanged = pyqtSignal(int) songDurationChanged = pyqtSignal(int) stateChanged = pyqtSignal(int) playlistChanged = pyqtSignal(QMediaPlaylist, int) currentSongChanged = pyqtSignal(str, str, bytes) currentSelectionChanged = pyqtSignal(UUID, int) customPlaylistCreated = pyqtSignal(UUID, str) libraryPlaylistCreated = pyqtSignal(UUID) addedToLibraryPlaylist = pyqtSignal(UUID, list) addedToCustomPlaylist = pyqtSignal(UUID, list) updatedLibraryPlaylist = pyqtSignal(UUID, list) playlistRemoved = pyqtSignal(UUID) def __init__(self, volumeLevel=40, playbackMode=QMediaPlaylist.Sequential, parent=None): super(AudioPlayer, self).__init__(parent) self.__player = QMediaPlayer() self.__player.setVolume(volumeLevel) self.__player.currentMediaChanged.connect(self._onMediaChanged) self.__player.stateChanged.connect( lambda state: self.stateChanged.emit(int(state))) self.__player.positionChanged.connect( lambda x: self.songPositionChanged.emit(x)) self.__player.durationChanged.connect( lambda x: self.songDurationChanged.emit(x)) self.__playlistManager = PlaylistManger() self.__playlistManager.customPlaylistCreated.connect( lambda uuid, name: self.customPlaylistCreated.emit(uuid, name)) self.__playlistManager.libraryPlaylistCreated.connect( lambda p: self.libraryPlaylistCreated.emit(p)) self.__playlistManager.currentPlaylistChanged.connect( self._onChangedPlaylist) self.__playlistManager.currentPlaylistChanged.connect( lambda p, i: self.playlistChanged.emit(p, i)) self.__playlistManager.playlistRemoved.connect( lambda uuid: self.playlistRemoved.emit(uuid)) self.__playlistManager.addedToLibraryPlaylist.connect( lambda uuid, l: self.addedToLibraryPlaylist.emit(uuid, l)) self.__playlistManager.addedToCustomPlaylist.connect( lambda uuid, l: self.addedToCustomPlaylist.emit(uuid, l)) self.__playlistManager.updatedLibraryPlaylist.connect( lambda uuid, l: self.updatedLibraryPlaylist.emit(uuid, l)) def createCustomPlaylist(self, name=None, urls=None): self.__playlistManager.createCustomPlaylist(name, urls) def createLibraryPlaylist(self, urls=None): self.__playlistManager.createLibraryPlaylist(urls) def addToLibraryPlaylist(self, url=None): self.__playlistManager.addToLibraryPlaylist(url) def updateLibraryPlaylist(self, url=None): self.__playlistManager.updateLibraryPlaylist(url) def renamePlaylist(self, uuid, newName): self.__playlistManager.renamePlaylist(uuid, newName) def addSongsToCustomPlaylist(self, uuid, urls=[]): self.__playlistManager.addSongsToCustomPlaylist(uuid, urls) def removeSong(self, uuid, row): self.__playlistManager.removeSong(uuid, row) def setPlaylist(self, uuid, index=0): if (self.__player.playlist() and self.__playlistManager.isCurrentPlaylist(uuid)): if index == self.__player.playlist().currentIndex(): if self.__player.state() == QMediaPlayer.PlayingState: self.__player.pause() else: self.__player.play() else: self.__player.playlist().setCurrentIndex(index) else: self.__playlistManager.setPlaylist(uuid, index) def hasLibraryPlaylist(self): return self.__playlistManager.hasLibraryPlaylist() def removePlaylist(self, uuid): self.__playlistManager.removePlaylist(uuid) def getCurrentPlaylist(self): self.__playlistManager.getCurrentPlaylist() def getCurrentQMediaPlaylist(self): self.__player.playlist() def isPlayerAvailable(self): return self.__player.isAvailable() def getDuration(self): return self.__player.duration() def getPlayer(self): return self.__player def getState(self): return self.__player.state() def play(self): if self.__player.playlist(): self.__player.play() def pause(self): if self.__player.playlist(): self.__player.pause() def stop(self): if self.__player.playlist(): self.__player.stop() def previousEnhanced(self, sameSongMillis): if self.__player.position() <= sameSongMillis: self.previous() else: self.__player.setPosition(0) def previous(self): if self.__player.playlist(): self.__player.playlist().previous() def next(self): if self.__player.playlist(): self.__player.playlist().next() def setVolume(self, value): self.__player.setVolume(value) def setPosition(self, milliseconds): self.__player.setPosition(milliseconds) def playlistCurrentIndex(self): if self.__player.playlist(): return self.__player.playlist().currentIndex() def setCurrentPlaylistIndex(self, index): if self.__player.playlist(): self.__player.playlist().setCurrentIndex(index) def _onChangedPlaylist(self, playlist, index, playIt=False): self.__player.setPlaylist(playlist) if playlist: playlist.setCurrentIndex(index) if playIt: self.play() def _onMediaChanged(self, media): if media.isNull() and self.__player.playlist(): self.__player.playlist().setCurrentIndex(0) media = self.__player.playlist().media(0) if media.isNull(): return title, artist, cover = self.__playlistManager.getBasicSongInfo(media) self.currentSongChanged.emit(title, artist, cover) uuid = self.__playlistManager.getCurrentPlaylistUuid() index = self.__playlistManager.getCurrentSongIndex() self.currentSelectionChanged.emit(uuid, index) # def saveSettings(self): # # settings = QSettings(QSettings.IniFormat, QSettings.UserScope, # # QCoreApplication.organizationName(), # # QCoreApplication.applicationName()) # # settings.beginGroup("music_player") # # # if self.__playlistManager.getLibraryPlaylist(): # # # libraryDirectories = self.__playlistManager.getLibraryPlaylist().getDirectories() # # # settings.beginWriteArray('library_playlist', # # # len(libraryDirectories)) # # # for index, value in enumerate(libraryDirectories): # # # settings.setArrayIndex(index) # # # settings.setValue("url", value) # # # settings.endArray() # # customPlaylists = self.__playlistManager.getCustomPlaylists() # # settings.beginWriteArray('custom_playlists', # # len(customPlaylists)) # # for index, value in enumerate(customPlaylists): # # settings.setArrayIndex(index) # # playlistName = settings.value('name', value.getName()) # # playlistUrls = value.getAddedSongUrls() # # settings.beginWriteArray(playlistName, # # len(playlistUrls)) # # for i, v in enumerate(playlistUrls): # # settings.setArrayIndex(i) # # settings.setValue("url", v) # # settings.endArray() # # settings.endArray() # # settings.endGroup() # # if self.__playlistManager.getLibraryPlaylist(): # # libraryDirectories = self.__playlistManager.getLibraryPlaylist().getDirectories() # # settings.beginWriteArray('library_playlist', # # len(libraryDirectories)) # # for index, value in enumerate(libraryDirectories): # # settings.setArrayIndex(index) # # settings.setValue("url", value) # # settings.endArray() # customPlaylists = self.__playlistManager.getCustomPlaylists() # for playlist in customPlaylists: # playlistName = playlist.getName() # for url in playlist: # pass # def restoreSettings(self): # settings = QSettings(QSettings.IniFormat, QSettings.UserScope, # QCoreApplication.organizationName(), # QCoreApplication.applicationName()) # settings.beginGroup("music_player") # # size = settings.beginReadArray('library_playlist') # # libraryDirectories = [] # # for i in range(size): # # settings.setArrayIndex(i) # # libraryDirectories.append(settings.value("url")) # # settings.endArray() # customPlaylists = {} # size = settings.beginReadArray('custom_playlists') # for i in range(size): # urls = [] # settings.setArrayIndex(i) # playlistName = settings.value('name') # print(playlistName) # size2 = settings.beginReadArray(playlistName) # for j in range(size2): # settings.setArrayIndex(j) # url = settings.value("url") # urls.append(url) # settings.endArray() # customPlaylists[playlistName] = urls # settings.endArray() # settings.endGroup() # print('-------') # print(customPlaylists) # settings = QSettings(QCoreApplication.organizationName(), # QCoreApplication.applicationName()) # settings.beginGroup("music_player") # size = settings.beginReadArray('library_playlist') # if not size == 0: # for i in range(0, size): # settings.setArrayIndex(i) # url = settings.value("url") # settings.endArray() # if url: # from audio.playlist_models import DirectoryPlaylist # playlist = DirectoryPlaylist() # playlist.add_directory(url) # self.addAndSetPlaylist(playlist, 2) # settings.endGroup() pass
class PlayManager(QWidget): NO_PLAYING_SONG = "没有正在播放的歌曲" def __init__(self, layout, parent=None): super().__init__(parent) self.player = QMediaPlayer(self) self.player.setAudioRole(QAudio.MusicRole) self.player.durationChanged.connect(self.duration_changed) self.player.positionChanged.connect(self.position_changed) self.player.mediaStatusChanged.connect(self.status_changed) self.player.bufferStatusChanged.connect(self.buffering_progress) self.player.stateChanged.connect(self.state_changed) self.player.error[QMediaPlayer.Error].connect( self.display_error_message) self.play_control = PlayControl(layout, parent) self.play_control.set_state(self.player.state()) self.play_control.play.connect(self.player.play) self.play_control.pause.connect(self.player.pause) self.play_control.stop.connect(self.player.stop) self.player.stateChanged.connect(self.play_control.set_state) self.song_label = QLabel() self.song_label.setText(self.NO_PLAYING_SONG) self.song_label.setWordWrap(True) song_grid = QGridLayout(parent) song_grid.addWidget(self.song_label, 0, 0) self.duration_label = QLabel() self.duration_label.setText("00:00 / 00:00") self.slider = QSlider(Qt.Horizontal, parent) self.slider.setRange(0, self.player.duration() / 1000) self.slider.sliderMoved.connect(self.seek) progress_layout = QHBoxLayout(parent) progress_layout.insertWidget(0, self.duration_label) progress_layout.insertWidget(0, self.slider) song_grid.addLayout(progress_layout, 1, 0) self.status_label = QLabel() song_grid.addWidget(self.status_label, 2, 0) layout.addLayout(song_grid) if not self.is_player_available(): QMessageBox.warning(self, "警告", "QMediaPlayer 对象无可用的服务") return self.song = None def is_player_available(self): return self.player.isAvailable() def add_to_play(self, spider, song): self.song = song spider.get_song_url(song) url = song.url if url is None or len(url) == 0: return req = QNetworkRequest(QUrl(url)) req.setHeader(QNetworkRequest.UserAgentHeader, HttpRequestConfig.CHROME_USER_AGENT) self.player.setMedia(QMediaContent(req)) def duration_changed(self, duration): self.duration = int(duration / 1000) self.slider.setMaximum(self.duration) t = "00:00 / %s" % (self.song.get_time()) self.duration_label.setText(t) def position_changed(self, progress): if not self.slider.isSliderDown(): self.slider.setValue(int(progress / 1000)) self.update_duration_info(int(progress / 1000)) @pyqtSlot(int) def seek(self, seconds): self.player.setPosition(seconds * 1000) def state_changed(self, state): if QMediaPlayer.PlayingState == state \ or QMediaPlayer.PausedState == state: self.song_label.setText("%s - %s" % (self.song.get_singers(), self.song.name)) else: self.song_label.setText(self.NO_PLAYING_SONG) def status_changed(self, status): if QMediaPlayer.UnknownMediaStatus == status \ or QMediaPlayer.NoMedia == status \ or QMediaPlayer.LoadedMedia == status \ or QMediaPlayer.BufferedMedia == status: self.set_status_info("") elif QMediaPlayer.LoadingMedia == status: self.set_status_info("加载中...") elif QMediaPlayer.BufferingMedia == status: self.set_status_info("缓存中...") elif QMediaPlayer.StalledMedia == status: self.set_status_info("等待中...") elif QMediaPlayer.EndOfMedia == status: QApplication.alert(self) elif QMediaPlayer.InvalidMedia == status: self.display_error_message() @pyqtSlot(int) def buffering_progress(self, progress): if self.player.mediaStatus() == QMediaPlayer.StalledMedia: self.set_status_info("等待中 %d%%" % (progress, )) else: self.set_status_info("缓存中 %d%%" % (progress, )) def set_status_info(self, info): self.status_label.setText(info) def display_error_message(self): self.set_status_info(self.player.errorString()) def update_duration_info(self, current_info): s = time.strftime("%M:%S", time.gmtime(current_info)) t = "%s / %s" % (s, self.song.get_time()) self.duration_label.setText(t)
class Player(QWidget): fullScreenChanged = pyqtSignal(bool) def __init__(self, playlist, parent=None): super(Player, self).__init__(parent) self.setStyleSheet(stylesheet(self)) self.colorDialog = None self.trackInfo = "" self.statusInfo = "" self.duration = 0 self.url = "" self. settings = QSettings("QAudioPlayer", "QAudioPlayer") self.player = QMediaPlayer() self.playlist = QMediaPlaylist() self.player.setPlaylist(self.playlist) self.player.durationChanged.connect(self.durationChanged) self.player.positionChanged.connect(self.positionChanged) self.player.metaDataChanged.connect(self.metaDataChanged) self.playlist.currentIndexChanged.connect(self.playlistPositionChanged) self.player.mediaStatusChanged.connect(self.statusChanged) self.player.bufferStatusChanged.connect(self.bufferingProgress) self.player.error.connect(self.displayErrorMessage) self.playlistModel = PlaylistModel() self.playlistModel.setPlaylist(self.playlist) self.playlistView = QListView() # self.playlistView.setSpacing(1) self.playlistView.setStyleSheet(stylesheet(self)) self.playlistView.setModel(self.playlistModel) self.playlistView.setCurrentIndex( self.playlistModel.index(self.playlist.currentIndex(), 0)) self.playlistView.activated.connect(self.jump) self.slider = QSlider(Qt.Horizontal) self.slider.setRange(0, self.player.duration() / 1000) self.slider.setStyleSheet(stylesheet(self)) self.labelDuration = QLabel() self.slider.sliderMoved.connect(self.seek) openButton = QPushButton("Open", clicked=self.open) openButton.setIcon(openButton.style().standardIcon(QStyle.SP_DialogOpenButton)) clearButton = QPushButton("", clicked=self.clearList) clearButton.setFixedWidth(36) clearButton.setIcon(QIcon.fromTheme("edit-delete")) clearButton.setToolTip("clear List") controls = PlayerControls() controls.setState(self.player.state()) controls.setVolume(self.player.volume()) controls.setMuted(controls.isMuted()) controls.play.connect(self.player.play) controls.pause.connect(self.player.pause) controls.stop.connect(self.player.stop) controls.next.connect(self.playlist.next) controls.previous.connect(self.previousClicked) controls.changeVolume.connect(self.player.setVolume) controls.changeMuting.connect(self.player.setMuted) controls.changeRate.connect(self.player.setPlaybackRate) self.player.stateChanged.connect(controls.setState) self.player.volumeChanged.connect(controls.setVolume) self.player.mutedChanged.connect(controls.setMuted) displayLayout = QHBoxLayout() displayLayout.addWidget(self.playlistView) controlLayout = QHBoxLayout() controlLayout.setContentsMargins(0, 0, 0, 0) controlLayout.addWidget(openButton) controlLayout.addWidget(clearButton) # controlLayout.addStretch(1) controlLayout.addWidget(controls) # controlLayout.addStretch(1) layout = QVBoxLayout() layout.addLayout(displayLayout) hLayout = QHBoxLayout() hLayout.addWidget(self.slider) hLayout.addWidget(self.labelDuration) layout.addLayout(hLayout) layout.addLayout(controlLayout) self.statusBar = QStatusBar() vlayout = QVBoxLayout() vlayout.addWidget(self.statusBar) layout.addLayout(vlayout) self.statusBar.showMessage("Welcome") self.setWindowTitle("QAudioPlayer") self.setMinimumSize(300, 200) # self.setBackgroundRole(QPalette.Window) self.setContentsMargins(0,0,0,0) self.setLayout(layout) self.readSettings() if not self.player.isAvailable(): QMessageBox.warning(self, "Service not available", "The QMediaPlayer object does not have a valid service.\n" "Please check the media service plugins are installed.") controls.setEnabled(False) self.playlistView.setEnabled(False) openButton.setEnabled(False) self.colorButton.setEnabled(False) self.fullScreenButton.setEnabled(False) self.metaDataChanged() self.addToPlaylist(playlist) def readSettings(self): if self.settings.contains("url"): self.url = self.settings.value("url") self.addToPlaylist(self.url) def writeSettings(self): self.settings.setValue("url", self.url) def closeEvent(self, event): print("writing settings") self.writeSettings() print("goodbye ...") event.accept() def open(self): fileNames, _ = QFileDialog.getOpenFileNames(self, "Open Files", "/home", "Audio Files *.mp3 *.m4a *.ogg *.wav *.m3u") if fileNames: self.url = fileNames self.addToPlaylist(fileNames) print("added Files to playlist") def openOnStart(self, name): fileInfo = QFileInfo(name) if fileInfo.exists(): url = QUrl.fromLocalFile(fileInfo.absoluteFilePath()) if fileInfo.suffix().lower() == 'm3u': self.playlist.load(url) else: self.playlist.addMedia(QMediaContent(url)) else: url = QUrl(name) if url.isValid(): self.playlist.addMedia(QMediaContent(url)) print("added Files to playlist") def clearList(self): self.playlist.clear() def addToPlaylist(self, fileNames): for name in fileNames: fileInfo = QFileInfo(name) if fileInfo.exists(): url = QUrl.fromLocalFile(fileInfo.absoluteFilePath()) if fileInfo.suffix().lower() == 'm3u': self.playlist.load(url) else: self.playlist.addMedia(QMediaContent(url)) else: url = QUrl(name) if url.isValid(): self.playlist.addMedia(QMediaContent(url)) def durationChanged(self, duration): duration /= 1000 self.duration = duration self.slider.setMaximum(duration) def positionChanged(self, progress): progress /= 1000 if not self.slider.isSliderDown(): self.slider.setValue(progress) self.updateDurationInfo(progress) def metaDataChanged(self): if self.player.isMetaDataAvailable(): self.setTrackInfo("%s - %s" % ( self.player.metaData(QMediaMetaData.AlbumArtist), self.player.metaData(QMediaMetaData.Title))) def previousClicked(self): # Go to the previous track if we are within the first 5 seconds of # playback. Otherwise, seek to the beginning. if self.player.position() <= 5000: self.playlist.previous() else: self.player.setPosition(0) def jump(self, index): if index.isValid(): self.playlist.setCurrentIndex(index.row()) self.player.play() def playlistPositionChanged(self, position): self.playlistView.setCurrentIndex( self.playlistModel.index(position, 0)) def seek(self, seconds): self.player.setPosition(seconds * 1000) def statusChanged(self, status): self.handleCursor(status) if status == QMediaPlayer.LoadingMedia: self.setStatusInfo("Loading...") elif status == QMediaPlayer.StalledMedia: self.setStatusInfo("Media Stalled") elif status == QMediaPlayer.EndOfMedia: QApplication.alert(self) elif status == QMediaPlayer.InvalidMedia: self.displayErrorMessage() else: self.setStatusInfo("") def handleCursor(self, status): if status in (QMediaPlayer.LoadingMedia, QMediaPlayer.BufferingMedia, QMediaPlayer.StalledMedia): self.setCursor(Qt.BusyCursor) else: self.unsetCursor() def bufferingProgress(self, progress): self.setStatusInfo("Buffering %d%" % progress) def setTrackInfo(self, info): self.trackInfo = info if self.statusInfo != "": self.statusBar.showMessage("%s | %s" % (self.trackInfo, self.statusInfo)) else: self.statusBar.showMessage(self.trackInfo) def setStatusInfo(self, info): self.statusInfo = info if self.statusInfo != "": self.statusBar.showMessage("%s | %s" % (self.trackInfo, self.statusInfo)) else: self.statusBar.showMessage(self.trackInfo) def displayErrorMessage(self): self.setStatusInfo(self.player.errorString()) def updateDurationInfo(self, currentInfo): duration = self.duration if currentInfo or duration: currentTime = QTime((currentInfo/3600)%60, (currentInfo/60)%60, currentInfo%60, (currentInfo*1000)%1000) totalTime = QTime((duration/3600)%60, (duration/60)%60, duration%60, (duration*1000)%1000); format = 'hh:mm:ss' if duration > 3600 else 'mm:ss' tStr = currentTime.toString(format) + " / " + totalTime.toString(format) else: tStr = "" self.labelDuration.setText(tStr)
class Player(QWidget): fullScreenChanged = pyqtSignal(bool) def __init__(self, playlist, parent=None): super(Player, self).__init__(parent) self.colorDialog = None self.trackInfo = "" self.statusInfo = "" self.duration = 0 self.player = QMediaPlayer() self.playlist = QMediaPlaylist() self.player.setPlaylist(self.playlist) self.player.durationChanged.connect(self.durationChanged) self.player.positionChanged.connect(self.positionChanged) self.player.metaDataChanged.connect(self.metaDataChanged) self.playlist.currentIndexChanged.connect(self.playlistPositionChanged) self.player.mediaStatusChanged.connect(self.statusChanged) self.player.bufferStatusChanged.connect(self.bufferingProgress) self.player.videoAvailableChanged.connect(self.videoAvailableChanged) self.player.error.connect(self.displayErrorMessage) self.videoWidget = VideoWidget() self.player.setVideoOutput(self.videoWidget) self.playlistModel = PlaylistModel() self.playlistModel.setPlaylist(self.playlist) self.playlistView = QListView() self.playlistView.setModel(self.playlistModel) self.playlistView.setCurrentIndex( self.playlistModel.index(self.playlist.currentIndex(), 0)) self.playlistView.activated.connect(self.jump) self.slider = QSlider(Qt.Horizontal) self.slider.setRange(0, self.player.duration() / 1000) self.labelDuration = QLabel() self.slider.sliderMoved.connect(self.seek) self.labelHistogram = QLabel() self.labelHistogram.setText("Histogram:") self.histogram = HistogramWidget() histogramLayout = QHBoxLayout() histogramLayout.addWidget(self.labelHistogram) histogramLayout.addWidget(self.histogram, 1) self.probe = QVideoProbe() self.probe.videoFrameProbed.connect(self.histogram.processFrame) self.probe.setSource(self.player) openButton = QPushButton("Open", clicked=self.open) controls = PlayerControls() controls.setState(self.player.state()) controls.setVolume(self.player.volume()) controls.setMuted(controls.isMuted()) controls.play.connect(self.player.play) controls.pause.connect(self.player.pause) controls.stop.connect(self.player.stop) controls.next.connect(self.playlist.next) controls.previous.connect(self.previousClicked) controls.changeVolume.connect(self.player.setVolume) controls.changeMuting.connect(self.player.setMuted) controls.changeRate.connect(self.player.setPlaybackRate) controls.stop.connect(self.videoWidget.update) self.player.stateChanged.connect(controls.setState) self.player.volumeChanged.connect(controls.setVolume) self.player.mutedChanged.connect(controls.setMuted) self.fullScreenButton = QPushButton("FullScreen") self.fullScreenButton.setCheckable(True) self.colorButton = QPushButton("Color Options...") self.colorButton.setEnabled(False) self.colorButton.clicked.connect(self.showColorDialog) displayLayout = QHBoxLayout() displayLayout.addWidget(self.videoWidget, 2) displayLayout.addWidget(self.playlistView) controlLayout = QHBoxLayout() controlLayout.setContentsMargins(0, 0, 0, 0) controlLayout.addWidget(openButton) controlLayout.addStretch(1) controlLayout.addWidget(controls) controlLayout.addStretch(1) controlLayout.addWidget(self.fullScreenButton) controlLayout.addWidget(self.colorButton) layout = QVBoxLayout() layout.addLayout(displayLayout) hLayout = QHBoxLayout() hLayout.addWidget(self.slider) hLayout.addWidget(self.labelDuration) layout.addLayout(hLayout) layout.addLayout(controlLayout) layout.addLayout(histogramLayout) self.setLayout(layout) if not self.player.isAvailable(): QMessageBox.warning(self, "Service not available", "The QMediaPlayer object does not have a valid service.\n" "Please check the media service plugins are installed.") controls.setEnabled(False) self.playlistView.setEnabled(False) openButton.setEnabled(False) self.colorButton.setEnabled(False) self.fullScreenButton.setEnabled(False) self.metaDataChanged() self.addToPlaylist(playlist) def open(self): fileNames, _ = QFileDialog.getOpenFileNames(self, "Open Files") self.addToPlaylist(fileNames) def addToPlaylist(self, fileNames): for name in fileNames: fileInfo = QFileInfo(name) if fileInfo.exists(): url = QUrl.fromLocalFile(fileInfo.absoluteFilePath()) if fileInfo.suffix().lower() == 'm3u': self.playlist.load(url) else: self.playlist.addMedia(QMediaContent(url)) else: url = QUrl(name) if url.isValid(): self.playlist.addMedia(QMediaContent(url)) def durationChanged(self, duration): duration /= 1000 self.duration = duration self.slider.setMaximum(duration) def positionChanged(self, progress): progress /= 1000 if not self.slider.isSliderDown(): self.slider.setValue(progress) self.updateDurationInfo(progress) def metaDataChanged(self): if self.player.isMetaDataAvailable(): self.setTrackInfo("%s - %s" % ( self.player.metaData(QMediaMetaData.AlbumArtist), self.player.metaData(QMediaMetaData.Title))) def previousClicked(self): # Go to the previous track if we are within the first 5 seconds of # playback. Otherwise, seek to the beginning. if self.player.position() <= 5000: self.playlist.previous() else: self.player.setPosition(0) def jump(self, index): if index.isValid(): self.playlist.setCurrentIndex(index.row()) self.player.play() def playlistPositionChanged(self, position): self.playlistView.setCurrentIndex( self.playlistModel.index(position, 0)) def seek(self, seconds): self.player.setPosition(seconds * 1000) def statusChanged(self, status): self.handleCursor(status) if status == QMediaPlayer.LoadingMedia: self.setStatusInfo("Loading...") elif status == QMediaPlayer.StalledMedia: self.setStatusInfo("Media Stalled") elif status == QMediaPlayer.EndOfMedia: QApplication.alert(self) elif status == QMediaPlayer.InvalidMedia: self.displayErrorMessage() else: self.setStatusInfo("") def handleCursor(self, status): if status in (QMediaPlayer.LoadingMedia, QMediaPlayer.BufferingMedia, QMediaPlayer.StalledMedia): self.setCursor(Qt.BusyCursor) else: self.unsetCursor() def bufferingProgress(self, progress): self.setStatusInfo("Buffering %d%" % progress) def videoAvailableChanged(self, available): if available: self.fullScreenButton.clicked.connect( self.videoWidget.setFullScreen) self.videoWidget.fullScreenChanged.connect( self.fullScreenButton.setChecked) if self.fullScreenButton.isChecked(): self.videoWidget.setFullScreen(True) else: self.fullScreenButton.clicked.disconnect( self.videoWidget.setFullScreen) self.videoWidget.fullScreenChanged.disconnect( self.fullScreenButton.setChecked) self.videoWidget.setFullScreen(False) self.colorButton.setEnabled(available) def setTrackInfo(self, info): self.trackInfo = info if self.statusInfo != "": self.setWindowTitle("%s | %s" % (self.trackInfo, self.statusInfo)) else: self.setWindowTitle(self.trackInfo) def setStatusInfo(self, info): self.statusInfo = info if self.statusInfo != "": self.setWindowTitle("%s | %s" % (self.trackInfo, self.statusInfo)) else: self.setWindowTitle(self.trackInfo) def displayErrorMessage(self): self.setStatusInfo(self.player.errorString()) def updateDurationInfo(self, currentInfo): duration = self.duration if currentInfo or duration: currentTime = QTime((currentInfo/3600)%60, (currentInfo/60)%60, currentInfo%60, (currentInfo*1000)%1000) totalTime = QTime((duration/3600)%60, (duration/60)%60, duration%60, (duration*1000)%1000); format = 'hh:mm:ss' if duration > 3600 else 'mm:ss' tStr = currentTime.toString(format) + " / " + totalTime.toString(format) else: tStr = "" self.labelDuration.setText(tStr) def showColorDialog(self): if self.colorDialog is None: brightnessSlider = QSlider(Qt.Horizontal) brightnessSlider.setRange(-100, 100) brightnessSlider.setValue(self.videoWidget.brightness()) brightnessSlider.sliderMoved.connect( self.videoWidget.setBrightness) self.videoWidget.brightnessChanged.connect( brightnessSlider.setValue) contrastSlider = QSlider(Qt.Horizontal) contrastSlider.setRange(-100, 100) contrastSlider.setValue(self.videoWidget.contrast()) contrastSlider.sliderMoved.connect(self.videoWidget.setContrast) self.videoWidget.contrastChanged.connect(contrastSlider.setValue) hueSlider = QSlider(Qt.Horizontal) hueSlider.setRange(-100, 100) hueSlider.setValue(self.videoWidget.hue()) hueSlider.sliderMoved.connect(self.videoWidget.setHue) self.videoWidget.hueChanged.connect(hueSlider.setValue) saturationSlider = QSlider(Qt.Horizontal) saturationSlider.setRange(-100, 100) saturationSlider.setValue(self.videoWidget.saturation()) saturationSlider.sliderMoved.connect( self.videoWidget.setSaturation) self.videoWidget.saturationChanged.connect( saturationSlider.setValue) layout = QFormLayout() layout.addRow("Brightness", brightnessSlider) layout.addRow("Contrast", contrastSlider) layout.addRow("Hue", hueSlider) layout.addRow("Saturation", saturationSlider) button = QPushButton("Close") layout.addRow(button) self.colorDialog = QDialog(self) self.colorDialog.setWindowTitle("Color Options") self.colorDialog.setLayout(layout) button.clicked.connect(self.colorDialog.close) self.colorDialog.show()