From 6a74b090b13a9e1ff37338332627eb5f16ed7d40 Mon Sep 17 00:00:00 2001 From: Eddy Pedroni Date: Sun, 1 Nov 2020 19:47:29 +0100 Subject: Initial commit --- mediaplayer.zip | Bin 0 -> 117177 bytes mediaplayer/.DS_Store | Bin 0 -> 6148 bytes mediaplayer/MainWindow.py | 141 ++++++++++++++++++ mediaplayer/README.md | 20 +++ mediaplayer/images/.DS_Store | Bin 0 -> 6148 bytes mediaplayer/images/application-image.png | Bin 0 -> 544 bytes mediaplayer/images/control-pause.png | Bin 0 -> 427 bytes mediaplayer/images/control-skip-180.png | Bin 0 -> 577 bytes mediaplayer/images/control-skip.png | Bin 0 -> 572 bytes mediaplayer/images/control-stop-square.png | Bin 0 -> 411 bytes mediaplayer/images/control.png | Bin 0 -> 470 bytes mediaplayer/images/speaker-volume.png | Bin 0 -> 566 bytes mediaplayer/mainwindow.ui | 227 +++++++++++++++++++++++++++++ mediaplayer/mediaplayer.py | 185 +++++++++++++++++++++++ mediaplayer/requirements.txt | 2 + mediaplayer/screenshot-mediaplayer1.jpg | Bin 0 -> 51397 bytes mediaplayer/screenshot-mediaplayer2.jpg | Bin 0 -> 83906 bytes mediaplayer/test.py | 15 ++ 18 files changed, 590 insertions(+) create mode 100644 mediaplayer.zip create mode 100644 mediaplayer/.DS_Store create mode 100644 mediaplayer/MainWindow.py create mode 100644 mediaplayer/README.md create mode 100644 mediaplayer/images/.DS_Store create mode 100755 mediaplayer/images/application-image.png create mode 100755 mediaplayer/images/control-pause.png create mode 100755 mediaplayer/images/control-skip-180.png create mode 100755 mediaplayer/images/control-skip.png create mode 100755 mediaplayer/images/control-stop-square.png create mode 100755 mediaplayer/images/control.png create mode 100755 mediaplayer/images/speaker-volume.png create mode 100644 mediaplayer/mainwindow.ui create mode 100644 mediaplayer/mediaplayer.py create mode 100644 mediaplayer/requirements.txt create mode 100644 mediaplayer/screenshot-mediaplayer1.jpg create mode 100644 mediaplayer/screenshot-mediaplayer2.jpg create mode 100644 mediaplayer/test.py diff --git a/mediaplayer.zip b/mediaplayer.zip new file mode 100644 index 0000000..402dd72 Binary files /dev/null and b/mediaplayer.zip differ diff --git a/mediaplayer/.DS_Store b/mediaplayer/.DS_Store new file mode 100644 index 0000000..beea7ac Binary files /dev/null and b/mediaplayer/.DS_Store differ diff --git a/mediaplayer/MainWindow.py b/mediaplayer/MainWindow.py new file mode 100644 index 0000000..09df321 --- /dev/null +++ b/mediaplayer/MainWindow.py @@ -0,0 +1,141 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'mainwindow.ui' +# +# Created by: PyQt5 UI code generator 5.10 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_MainWindow(object): + def setupUi(self, MainWindow): + MainWindow.setObjectName("MainWindow") + MainWindow.resize(484, 371) + self.centralWidget = QtWidgets.QWidget(MainWindow) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.centralWidget.sizePolicy().hasHeightForWidth()) + self.centralWidget.setSizePolicy(sizePolicy) + self.centralWidget.setObjectName("centralWidget") + self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralWidget) + self.horizontalLayout.setContentsMargins(11, 11, 11, 11) + self.horizontalLayout.setSpacing(6) + self.horizontalLayout.setObjectName("horizontalLayout") + self.verticalLayout = QtWidgets.QVBoxLayout() + self.verticalLayout.setSpacing(6) + self.verticalLayout.setObjectName("verticalLayout") + self.playlistView = QtWidgets.QListView(self.centralWidget) + self.playlistView.setAcceptDrops(True) + self.playlistView.setProperty("showDropIndicator", True) + self.playlistView.setDragDropMode(QtWidgets.QAbstractItemView.DropOnly) + self.playlistView.setAlternatingRowColors(True) + self.playlistView.setUniformItemSizes(True) + self.playlistView.setObjectName("playlistView") + self.verticalLayout.addWidget(self.playlistView) + self.horizontalLayout_4 = QtWidgets.QHBoxLayout() + self.horizontalLayout_4.setSpacing(6) + self.horizontalLayout_4.setObjectName("horizontalLayout_4") + self.currentTimeLabel = QtWidgets.QLabel(self.centralWidget) + self.currentTimeLabel.setMinimumSize(QtCore.QSize(80, 0)) + self.currentTimeLabel.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.currentTimeLabel.setObjectName("currentTimeLabel") + self.horizontalLayout_4.addWidget(self.currentTimeLabel) + self.timeSlider = QtWidgets.QSlider(self.centralWidget) + self.timeSlider.setOrientation(QtCore.Qt.Horizontal) + self.timeSlider.setObjectName("timeSlider") + self.horizontalLayout_4.addWidget(self.timeSlider) + self.totalTimeLabel = QtWidgets.QLabel(self.centralWidget) + self.totalTimeLabel.setMinimumSize(QtCore.QSize(80, 0)) + self.totalTimeLabel.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.totalTimeLabel.setObjectName("totalTimeLabel") + self.horizontalLayout_4.addWidget(self.totalTimeLabel) + self.verticalLayout.addLayout(self.horizontalLayout_4) + self.horizontalLayout_5 = QtWidgets.QHBoxLayout() + self.horizontalLayout_5.setSpacing(6) + self.horizontalLayout_5.setObjectName("horizontalLayout_5") + self.previousButton = QtWidgets.QPushButton(self.centralWidget) + self.previousButton.setText("") + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap("images/control-skip-180.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.previousButton.setIcon(icon) + self.previousButton.setObjectName("previousButton") + self.horizontalLayout_5.addWidget(self.previousButton) + self.playButton = QtWidgets.QPushButton(self.centralWidget) + self.playButton.setText("") + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap("images/control.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.playButton.setIcon(icon1) + self.playButton.setObjectName("playButton") + self.horizontalLayout_5.addWidget(self.playButton) + self.pauseButton = QtWidgets.QPushButton(self.centralWidget) + self.pauseButton.setText("") + icon2 = QtGui.QIcon() + icon2.addPixmap(QtGui.QPixmap("images/control-pause.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.pauseButton.setIcon(icon2) + self.pauseButton.setObjectName("pauseButton") + self.horizontalLayout_5.addWidget(self.pauseButton) + self.stopButton = QtWidgets.QPushButton(self.centralWidget) + self.stopButton.setText("") + icon3 = QtGui.QIcon() + icon3.addPixmap(QtGui.QPixmap("images/control-stop-square.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.stopButton.setIcon(icon3) + self.stopButton.setObjectName("stopButton") + self.horizontalLayout_5.addWidget(self.stopButton) + self.nextButton = QtWidgets.QPushButton(self.centralWidget) + self.nextButton.setText("") + icon4 = QtGui.QIcon() + icon4.addPixmap(QtGui.QPixmap("images/control-skip.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.nextButton.setIcon(icon4) + self.nextButton.setObjectName("nextButton") + self.horizontalLayout_5.addWidget(self.nextButton) + self.viewButton = QtWidgets.QPushButton(self.centralWidget) + self.viewButton.setText("") + icon5 = QtGui.QIcon() + icon5.addPixmap(QtGui.QPixmap("images/application-image.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.viewButton.setIcon(icon5) + self.viewButton.setCheckable(True) + self.viewButton.setObjectName("viewButton") + self.horizontalLayout_5.addWidget(self.viewButton) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout_5.addItem(spacerItem) + self.label = QtWidgets.QLabel(self.centralWidget) + self.label.setText("") + self.label.setPixmap(QtGui.QPixmap("images/speaker-volume.png")) + self.label.setObjectName("label") + self.horizontalLayout_5.addWidget(self.label) + self.volumeSlider = QtWidgets.QSlider(self.centralWidget) + self.volumeSlider.setMaximum(100) + self.volumeSlider.setProperty("value", 100) + self.volumeSlider.setOrientation(QtCore.Qt.Horizontal) + self.volumeSlider.setObjectName("volumeSlider") + self.horizontalLayout_5.addWidget(self.volumeSlider) + self.verticalLayout.addLayout(self.horizontalLayout_5) + self.horizontalLayout.addLayout(self.verticalLayout) + MainWindow.setCentralWidget(self.centralWidget) + self.menuBar = QtWidgets.QMenuBar(MainWindow) + self.menuBar.setGeometry(QtCore.QRect(0, 0, 484, 22)) + self.menuBar.setObjectName("menuBar") + self.menuFIle = QtWidgets.QMenu(self.menuBar) + self.menuFIle.setObjectName("menuFIle") + MainWindow.setMenuBar(self.menuBar) + self.statusBar = QtWidgets.QStatusBar(MainWindow) + self.statusBar.setObjectName("statusBar") + MainWindow.setStatusBar(self.statusBar) + self.open_file_action = QtWidgets.QAction(MainWindow) + self.open_file_action.setObjectName("open_file_action") + self.menuFIle.addAction(self.open_file_action) + self.menuBar.addAction(self.menuFIle.menuAction()) + + self.retranslateUi(MainWindow) + QtCore.QMetaObject.connectSlotsByName(MainWindow) + + def retranslateUi(self, MainWindow): + _translate = QtCore.QCoreApplication.translate + MainWindow.setWindowTitle(_translate("MainWindow", "Failamp")) + self.currentTimeLabel.setText(_translate("MainWindow", "0:00")) + self.totalTimeLabel.setText(_translate("MainWindow", "0:00")) + self.menuFIle.setTitle(_translate("MainWindow", "FIle")) + self.open_file_action.setText(_translate("MainWindow", "Open file...")) + diff --git a/mediaplayer/README.md b/mediaplayer/README.md new file mode 100644 index 0000000..5bd24f7 --- /dev/null +++ b/mediaplayer/README.md @@ -0,0 +1,20 @@ +# Failamp — Simple mediaplayer build in PyQt + +Simple app to listen to and watch videos and audio files, +with built in playlist. Uses QtMultimedia and QtMultimediaWidgets +to handle playback and manage the playlist. + +The main interface offers a playlist window in which you can drag-drop +media files to be played. Standard media controls are provided, along +with a timeline scrub widget and a volume control. + +![Mediaplayer](screenshot-mediaplayer1.jpg) + +For video playback you can pop out an external video viewer window +which floats on top. + +![Mediaplayer](screenshot-mediaplayer2.jpg) + +> If you think this app is neat and want to learn more about +PyQt in general, take a look at my [free PyQt tutorials](https://www.learnpyqt.com) +which cover everything you need to know to start building your own applications with PyQt. \ No newline at end of file diff --git a/mediaplayer/images/.DS_Store b/mediaplayer/images/.DS_Store new file mode 100644 index 0000000..a2cca49 Binary files /dev/null and b/mediaplayer/images/.DS_Store differ diff --git a/mediaplayer/images/application-image.png b/mediaplayer/images/application-image.png new file mode 100755 index 0000000..915d63f Binary files /dev/null and b/mediaplayer/images/application-image.png differ diff --git a/mediaplayer/images/control-pause.png b/mediaplayer/images/control-pause.png new file mode 100755 index 0000000..af57b25 Binary files /dev/null and b/mediaplayer/images/control-pause.png differ diff --git a/mediaplayer/images/control-skip-180.png b/mediaplayer/images/control-skip-180.png new file mode 100755 index 0000000..62daa7a Binary files /dev/null and b/mediaplayer/images/control-skip-180.png differ diff --git a/mediaplayer/images/control-skip.png b/mediaplayer/images/control-skip.png new file mode 100755 index 0000000..e4a4930 Binary files /dev/null and b/mediaplayer/images/control-skip.png differ diff --git a/mediaplayer/images/control-stop-square.png b/mediaplayer/images/control-stop-square.png new file mode 100755 index 0000000..7c6af7f Binary files /dev/null and b/mediaplayer/images/control-stop-square.png differ diff --git a/mediaplayer/images/control.png b/mediaplayer/images/control.png new file mode 100755 index 0000000..2dfaef5 Binary files /dev/null and b/mediaplayer/images/control.png differ diff --git a/mediaplayer/images/speaker-volume.png b/mediaplayer/images/speaker-volume.png new file mode 100755 index 0000000..62fcfc6 Binary files /dev/null and b/mediaplayer/images/speaker-volume.png differ diff --git a/mediaplayer/mainwindow.ui b/mediaplayer/mainwindow.ui new file mode 100644 index 0000000..2d8a4ba --- /dev/null +++ b/mediaplayer/mainwindow.ui @@ -0,0 +1,227 @@ + + + MainWindow + + + + 0 + 0 + 484 + 371 + + + + Failamp + + + + + 0 + 0 + + + + + + + true + + + true + + + QAbstractItemView::DropOnly + + + Qt::CopyAction + + + true + + + true + + + + + + + + + + 80 + 0 + + + + 0:00 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + + + + + 80 + 0 + + + + 0:00 + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + + + + + + + images/control-skip-180.pngimages/control-skip-180.png + + + + + + + + + + + images/control.pngimages/control.png + + + + + + + + + + + images/control-pause.pngimages/control-pause.png + + + + + + + + + + + images/control-stop-square.pngimages/control-stop-square.png + + + + + + + + + + + images/control-skip.pngimages/control-skip.png + + + + + + + + + + + images/application-image.pngimages/application-image.png + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + images/speaker-volume.png + + + + + + + 100 + + + 100 + + + Qt::Horizontal + + + + + + + + + + + 0 + 0 + 484 + 22 + + + + + FIle + + + + + + + + + Open file... + + + + + + + diff --git a/mediaplayer/mediaplayer.py b/mediaplayer/mediaplayer.py new file mode 100644 index 0000000..fe4cc50 --- /dev/null +++ b/mediaplayer/mediaplayer.py @@ -0,0 +1,185 @@ +from PyQt5.QtGui import * +from PyQt5.QtWidgets import * +from PyQt5.QtCore import * +from PyQt5.QtMultimedia import * +from PyQt5.QtMultimediaWidgets import * + +from MainWindow import Ui_MainWindow + +def hhmmss(ms): + # s = 1000 + # m = 60000 + # h = 360000 + h, r = divmod(ms, 36000) + m, r = divmod(r, 60000) + s, _ = divmod(r, 1000) + return ("%d:%02d:%02d" % (h,m,s)) if h else ("%d:%02d" % (m,s)) + +class ViewerWindow(QMainWindow): + state = pyqtSignal(bool) + + def closeEvent(self, e): + # Emit the window state, to update the viewer toggle button. + self.state.emit(False) + + +class PlaylistModel(QAbstractListModel): + def __init__(self, playlist, *args, **kwargs): + super(PlaylistModel, self).__init__(*args, **kwargs) + self.playlist = playlist + + def data(self, index, role): + if role == Qt.DisplayRole: + media = self.playlist.media(index.row()) + return media.canonicalUrl().fileName() + + def rowCount(self, index): + return self.playlist.mediaCount() + + +class MainWindow(QMainWindow, Ui_MainWindow): + def __init__(self, *args, **kwargs): + super(MainWindow, self).__init__(*args, **kwargs) + self.setupUi(self) + + self.player = QMediaPlayer() + + self.player.error.connect(self.erroralert) + self.player.play() + + # Setup the playlist. + self.playlist = QMediaPlaylist() + self.player.setPlaylist(self.playlist) + + # Add viewer for video playback, separate floating window. + self.viewer = ViewerWindow(self) + self.viewer.setWindowFlags(self.viewer.windowFlags() | Qt.WindowStaysOnTopHint) + self.viewer.setMinimumSize(QSize(480,360)) + + videoWidget = QVideoWidget() + self.viewer.setCentralWidget(videoWidget) + self.player.setVideoOutput(videoWidget) + + # Connect control buttons/slides for media player. + self.playButton.pressed.connect(self.player.play) + self.pauseButton.pressed.connect(self.player.pause) + self.stopButton.pressed.connect(self.player.stop) + self.volumeSlider.valueChanged.connect(self.player.setVolume) + + self.viewButton.toggled.connect(self.toggle_viewer) + self.viewer.state.connect(self.viewButton.setChecked) + + self.previousButton.pressed.connect(self.playlist.previous) + self.nextButton.pressed.connect(self.playlist.next) + + self.model = PlaylistModel(self.playlist) + self.playlistView.setModel(self.model) + self.playlist.currentIndexChanged.connect(self.playlist_position_changed) + selection_model = self.playlistView.selectionModel() + selection_model.selectionChanged.connect(self.playlist_selection_changed) + + self.player.durationChanged.connect(self.update_duration) + self.player.positionChanged.connect(self.update_position) + self.timeSlider.valueChanged.connect(self.player.setPosition) + + self.open_file_action.triggered.connect(self.open_file) + + self.setAcceptDrops(True) + + self.show() + + def dragEnterEvent(self, e): + if e.mimeData().hasUrls(): + e.acceptProposedAction() + + def dropEvent(self, e): + for url in e.mimeData().urls(): + self.playlist.addMedia( + QMediaContent(url) + ) + + self.model.layoutChanged.emit() + + # If not playing, seeking to first of newly added + play. + if self.player.state() != QMediaPlayer.PlayingState: + i = self.playlist.mediaCount() - len(e.mimeData().urls()) + self.playlist.setCurrentIndex(i) + self.player.play() + + def open_file(self): + path, _ = QFileDialog.getOpenFileName(self, "Open file", "", "mp3 Audio (*.mp3);mp4 Video (*.mp4);Movie files (*.mov);All files (*.*)") + + if path: + self.playlist.addMedia( + QMediaContent( + QUrl.fromLocalFile(path) + ) + ) + + self.model.layoutChanged.emit() + + def update_duration(self, duration): + print("!", duration) + print("?", self.player.duration()) + + self.timeSlider.setMaximum(duration) + + if duration >= 0: + self.totalTimeLabel.setText(hhmmss(duration)) + + def update_position(self, position): + if position >= 0: + self.currentTimeLabel.setText(hhmmss(position)) + + # Disable the events to prevent updating triggering a setPosition event (can cause stuttering). + self.timeSlider.blockSignals(True) + self.timeSlider.setValue(position) + self.timeSlider.blockSignals(False) + + def playlist_selection_changed(self, ix): + # We receive a QItemSelection from selectionChanged. + i = ix.indexes()[0].row() + self.playlist.setCurrentIndex(i) + + def playlist_position_changed(self, i): + if i > -1: + ix = self.model.index(i) + self.playlistView.setCurrentIndex(ix) + + def toggle_viewer(self, state): + if state: + self.viewer.show() + else: + self.viewer.hide() + + def erroralert(self, *args): + print(args) + + + + +if __name__ == '__main__': + app = QApplication([]) + app.setApplicationName("Failamp") + app.setStyle("Fusion") + + # Fusion dark palette from https://gist.github.com/QuantumCD/6245215. + palette = QPalette() + palette.setColor(QPalette.Window, QColor(53, 53, 53)) + palette.setColor(QPalette.WindowText, Qt.white) + palette.setColor(QPalette.Base, QColor(25, 25, 25)) + palette.setColor(QPalette.AlternateBase, QColor(53, 53, 53)) + palette.setColor(QPalette.ToolTipBase, Qt.white) + palette.setColor(QPalette.ToolTipText, Qt.white) + palette.setColor(QPalette.Text, Qt.white) + palette.setColor(QPalette.Button, QColor(53, 53, 53)) + palette.setColor(QPalette.ButtonText, Qt.white) + palette.setColor(QPalette.BrightText, Qt.red) + palette.setColor(QPalette.Link, QColor(42, 130, 218)) + palette.setColor(QPalette.Highlight, QColor(42, 130, 218)) + palette.setColor(QPalette.HighlightedText, Qt.black) + app.setPalette(palette) + app.setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }") + + window = MainWindow() + app.exec_() diff --git a/mediaplayer/requirements.txt b/mediaplayer/requirements.txt new file mode 100644 index 0000000..8cb824a --- /dev/null +++ b/mediaplayer/requirements.txt @@ -0,0 +1,2 @@ +PyQt5>=5.6 +sip diff --git a/mediaplayer/screenshot-mediaplayer1.jpg b/mediaplayer/screenshot-mediaplayer1.jpg new file mode 100644 index 0000000..86090a2 Binary files /dev/null and b/mediaplayer/screenshot-mediaplayer1.jpg differ diff --git a/mediaplayer/screenshot-mediaplayer2.jpg b/mediaplayer/screenshot-mediaplayer2.jpg new file mode 100644 index 0000000..053ff56 Binary files /dev/null and b/mediaplayer/screenshot-mediaplayer2.jpg differ diff --git a/mediaplayer/test.py b/mediaplayer/test.py new file mode 100644 index 0000000..afdd625 --- /dev/null +++ b/mediaplayer/test.py @@ -0,0 +1,15 @@ +import sys +from PySide2 import QtWidgets +from MainWindow2 import Ui_MainWindow + +class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.setupUi(self) + + +app = QtWidgets.QApplication(sys.argv) +window = MainWindow() +window.show() +app.exec_() \ No newline at end of file -- cgit v1.2.3