Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
PyQt5 VideoPlayer
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from PyQt5.QtGui import QPalette, QKeySequence, QIcon
from PyQt5.QtCore import QDir, Qt, QUrl, QSize, QPoint, QTime, QMimeData, QProcess, QEvent
from PyQt5.QtMultimedia import QMediaContent, QMediaPlayer, QMediaMetaData
from PyQt5.QtMultimediaWidgets import QVideoWidget
from PyQt5.QtWidgets import (QApplication, QFileDialog, QHBoxLayout, QLineEdit,
QPushButton, QSizePolicy, QSlider, QMessageBox, QStyle, QVBoxLayout,
QWidget, QShortcut, QMenu)
import sys
import os
import subprocess
#QT_DEBUG_PLUGINS
class VideoPlayer(QWidget):
def __init__(self, aPath, parent=None):
super(VideoPlayer, self).__init__(parent)
self.setAttribute( Qt.WA_NoSystemBackground, True )
self.setAcceptDrops(True)
self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.StreamPlayback)
self.mediaPlayer.mediaStatusChanged.connect(self.printMediaData)
self.mediaPlayer.setVolume(80)
self.videoWidget = QVideoWidget(self)
self.lbl = QLineEdit('00:00:00')
self.lbl.setReadOnly(True)
self.lbl.setFixedWidth(70)
self.lbl.setUpdatesEnabled(True)
self.lbl.setStyleSheet(stylesheet(self))
self.lbl.selectionChanged.connect(lambda: self.lbl.setSelection(0, 0))
self.elbl = QLineEdit('00:00:00')
self.elbl.setReadOnly(True)
self.elbl.setFixedWidth(70)
self.elbl.setUpdatesEnabled(True)
self.elbl.setStyleSheet(stylesheet(self))
self.elbl.selectionChanged.connect(lambda: self.elbl.setSelection(0, 0))
self.playButton = QPushButton()
self.playButton.setEnabled(False)
self.playButton.setFixedWidth(32)
self.playButton.setStyleSheet("background-color: black")
self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
self.playButton.clicked.connect(self.play)
self.positionSlider = QSlider(Qt.Horizontal, self)
self.positionSlider.setStyleSheet (stylesheet(self))
self.positionSlider.setRange(0, 100)
self.positionSlider.sliderMoved.connect(self.setPosition)
self.positionSlider.setSingleStep(2)
self.positionSlider.setPageStep(20)
self.positionSlider.setAttribute(Qt.WA_TranslucentBackground, True)
self.clip = QApplication.clipboard()
self.process = QProcess(self)
self.process.readyRead.connect(self.dataReady)
self.process.finished.connect(self.playFromURL)
self.myurl = ""
controlLayout = QHBoxLayout()
controlLayout.setContentsMargins(5, 0, 5, 0)
controlLayout.addWidget(self.playButton)
controlLayout.addWidget(self.lbl)
controlLayout.addWidget(self.positionSlider)
controlLayout.addWidget(self.elbl)
layout = QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(self.videoWidget)
layout.addLayout(controlLayout)
self.setLayout(layout)
self.myinfo = "©2016\nAxel Schneider\n\nMouse Wheel = Zoom\nUP = Volume Up\nDOWN = Volume Down\n" + \
"LEFT = < 1 Minute\nRIGHT = > 1 Minute\n" + \
"SHIFT+LEFT = < 10 Minutes\nSHIFT+RIGHT = > 10 Minutes"
self.widescreen = True
#### shortcuts ####
self.shortcut = QShortcut(QKeySequence("q"), self)
self.shortcut.activated.connect(self.handleQuit)
self.shortcut = QShortcut(QKeySequence("u"), self)
self.shortcut.activated.connect(self.playFromURL)
self.shortcut = QShortcut(QKeySequence("y"), self)
self.shortcut.activated.connect(self.getYTUrl)
self.shortcut = QShortcut(QKeySequence("o"), self)
self.shortcut.activated.connect(self.openFile)
self.shortcut = QShortcut(QKeySequence(" "), self)
self.shortcut.activated.connect(self.play)
self.shortcut = QShortcut(QKeySequence("f"), self)
self.shortcut.activated.connect(self.handleFullscreen)
self.shortcut = QShortcut(QKeySequence("i"), self)
self.shortcut.activated.connect(self.handleInfo)
self.shortcut = QShortcut(QKeySequence("s"), self)
self.shortcut.activated.connect(self.toggleSlider)
self.shortcut = QShortcut(QKeySequence(Qt.Key_Right), self)
self.shortcut.activated.connect(self.forwardSlider)
self.shortcut = QShortcut(QKeySequence(Qt.Key_Left), self)
self.shortcut.activated.connect(self.backSlider)
self.shortcut = QShortcut(QKeySequence(Qt.Key_Up), self)
self.shortcut.activated.connect(self.volumeUp)
self.shortcut = QShortcut(QKeySequence(Qt.Key_Down), self)
self.shortcut.activated.connect(self.volumeDown)
self.shortcut = QShortcut(QKeySequence(Qt.ShiftModifier + Qt.Key_Right) , self)
self.shortcut.activated.connect(self.forwardSlider10)
self.shortcut = QShortcut(QKeySequence(Qt.ShiftModifier + Qt.Key_Left) , self)
self.shortcut.activated.connect(self.backSlider10)
self.mediaPlayer.setVideoOutput(self.videoWidget)
self.mediaPlayer.stateChanged.connect(self.mediaStateChanged)
self.mediaPlayer.positionChanged.connect(self.positionChanged)
self.mediaPlayer.durationChanged.connect(self.durationChanged)
self.mediaPlayer.error.connect(self.handleError)
print("QT5 Player started")
print("press 'o' to open file (see context menu for more)")
self.suspend_screensaver()
def mouseDoubleClickEvent(self, event):
self.handleFullscreen()
def playFromURL(self):
self.mediaPlayer.pause()
self.myurl = self.clip.text()
self.mediaPlayer.setMedia(QMediaContent(QUrl(self.myurl)))
self.playButton.setEnabled(True)
self.mediaPlayer.play()
self.hideSlider()
print(self.myurl)
def getYTUrl(self):
cmd = "youtube-dl -g -f best " + self.clip.text()
print("grabbing YouTube URL")
self.process.start(cmd)
def dataReady(self):
self.myurl = str(self.process.readAll(), encoding = 'utf8').rstrip() ###
self.myurl = self.myurl.partition("\n")[0]
print(self.myurl)
self.clip.setText(self.myurl)
self.playFromURL()
def suspend_screensaver(self):
'suspend linux screensaver'
proc = subprocess.Popen('gsettings set org.gnome.desktop.screensaver idle-activation-enabled false', shell=True)
proc.wait()
def resume_screensaver(self):
'resume linux screensaver'
proc = subprocess.Popen('gsettings set org.gnome.desktop.screensaver idle-activation-enabled true', shell=True)
proc.wait()
def openFile(self):
fileName, _ = QFileDialog.getOpenFileName(self, "Open Movie",
QDir.homePath() + "/Videos", "Media (*.webm *.mp4 *.ts *.avi *.mpeg *.mpg *.mkv *.VOB *.m4v *.3gp *.mp3 *.m4a *.wav *.ogg *.flac *.m3u *.m3u8)")
if fileName != '':
self.loadFilm(fileName)
print("File loaded")
def play(self):
if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
self.mediaPlayer.pause()
else:
self.mediaPlayer.play()
def mediaStateChanged(self, state):
if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
self.playButton.setIcon(
self.style().standardIcon(QStyle.SP_MediaPause))
else:
self.playButton.setIcon(
self.style().standardIcon(QStyle.SP_MediaPlay))
def positionChanged(self, position):
self.positionSlider.setValue(position)
mtime = QTime(0,0,0,0)
mtime = mtime.addMSecs(self.mediaPlayer.position())
self.lbl.setText(mtime.toString())
def durationChanged(self, duration):
self.positionSlider.setRange(0, duration)
mtime = QTime(0,0,0,0)
mtime = mtime.addMSecs(self.mediaPlayer.duration())
self.elbl.setText(mtime.toString())
def setPosition(self, position):
self.mediaPlayer.setPosition(position)
def handleError(self):
self.playButton.setEnabled(False)
print("Error: ", self.mediaPlayer.errorString())
def handleQuit(self):
self.mediaPlayer.stop()
self.resume_screensaver()
print("Goodbye ...")
app.quit()
def contextMenuRequested(self,point):
menu = QMenu()
actionFile = menu.addAction(QIcon.fromTheme("video-x-generic"),"open File (o)")
actionclipboard = menu.addSeparator()
actionURL = menu.addAction(QIcon.fromTheme("browser"),"URL from Clipboard (u)")
actionclipboard = menu.addSeparator()
actionYTurl = menu.addAction(QIcon.fromTheme("youtube"), "URL from YouTube (y)")
actionclipboard = menu.addSeparator()
actionToggle = menu.addAction(QIcon.fromTheme("next"),"show / hide Slider (s)")
actionFull = menu.addAction(QIcon.fromTheme("view-fullscreen"),"Fullscreen (f)")
action169 = menu.addAction(QIcon.fromTheme("tv-symbolic"),"16 : 9")
action43 = menu.addAction(QIcon.fromTheme("tv-symbolic"),"4 : 3")
actionSep = menu.addSeparator()
actionInfo = menu.addAction(QIcon.fromTheme("help-about"),"Info (i)")
action5 = menu.addSeparator()
actionQuit = menu.addAction(QIcon.fromTheme("application-exit"),"Exit (q)")
actionFile.triggered.connect(self.openFile)
actionQuit.triggered.connect(self.handleQuit)
actionFull.triggered.connect(self.handleFullscreen)
actionInfo.triggered.connect(self.handleInfo)
actionToggle.triggered.connect(self.toggleSlider)
actionURL.triggered.connect(self.playFromURL)
actionYTurl.triggered.connect(self.getYTUrl)
action169.triggered.connect(self.screen169)
action43.triggered.connect(self.screen43)
menu.exec_(self.mapToGlobal(point))
def wheelEvent(self,event):
mwidth = self.frameGeometry().width()
mheight = self.frameGeometry().height()
mleft = self.frameGeometry().left()
mtop = self.frameGeometry().top()
mscale = event.angleDelta().y() / 5
if self.widescreen == True:
self.setGeometry(mleft, mtop, mwidth + mscale, round((mwidth + mscale) / 1.778))
else:
self.setGeometry(mleft, mtop, mwidth + mscale, round((mwidth + mscale) / 1.33))
#elif self.positionSlider.hasFocus():
# self.positionSlider.value = self.positionSlider.value + 5
def screen169(self):
self.widescreen = True
mwidth = self.frameGeometry().width()
mheight = self.frameGeometry().height()
mleft = self.frameGeometry().left()
mtop = self.frameGeometry().top()
mratio = 1.778
self.setGeometry(mleft, mtop, mwidth, round(mwidth / mratio))
def screen43(self):
self.widescreen = False
mwidth = self.frameGeometry().width()
mheight = self.frameGeometry().height()
mleft = self.frameGeometry().left()
mtop = self.frameGeometry().top()
mratio = 1.33
self.setGeometry(mleft, mtop, mwidth, round(mwidth / mratio))
def handleFullscreen(self):
if self.windowState() & Qt.WindowFullScreen:
QApplication.setOverrideCursor(Qt.ArrowCursor)
self.showNormal()
print("no Fullscreen")
else:
self.showFullScreen()
QApplication.setOverrideCursor(Qt.BlankCursor)
print("Fullscreen entered")
def handleInfo(self):
msg = QMessageBox.about(self, "QT5 Player", self.myinfo)
def toggleSlider(self):
if self.positionSlider.isVisible():
self.hideSlider()
else:
self.showSlider()
def hideSlider(self):
self.playButton.hide()
self.lbl.hide()
self.positionSlider.hide()
self.elbl.hide()
mwidth = self.frameGeometry().width()
mheight = self.frameGeometry().height()
mleft = self.frameGeometry().left()
mtop = self.frameGeometry().top()
if self.widescreen == True:
self.setGeometry(mleft, mtop, mwidth, round(mwidth / 1.778))
else:
self.setGeometry(mleft, mtop, mwidth, round(mwidth / 1.33))
def showSlider(self):
self.playButton.show()
self.lbl.show()
self.positionSlider.show()
self.elbl.show()
mwidth = self.frameGeometry().width()
mheight = self.frameGeometry().height()
mleft = self.frameGeometry().left()
mtop = self.frameGeometry().top()
if self.widescreen == True:
self.setGeometry(mleft, mtop, mwidth, round(mwidth / 1.55))
else:
self.setGeometry(mleft, mtop, mwidth, round(mwidth / 1.33))
def forwardSlider(self):
self.mediaPlayer.setPosition(self.mediaPlayer.position() + 1000*60)
def forwardSlider10(self):
self.mediaPlayer.setPosition(self.mediaPlayer.position() + 10000*60)
def backSlider(self):
self.mediaPlayer.setPosition(self.mediaPlayer.position() - 1000*60)
def backSlider10(self):
self.mediaPlayer.setPosition(self.mediaPlayer.position() - 10000*60)
def volumeUp(self):
self.mediaPlayer.setVolume(self.mediaPlayer.volume() + 10)
print("Volume: " + str(self.mediaPlayer.volume()))
def volumeDown(self):
self.mediaPlayer.setVolume(self.mediaPlayer.volume() - 10)
print("Volume: " + str(self.mediaPlayer.volume()))
def mousePressEvent(self, evt):
self.oldPos = evt.globalPos()
def mouseMoveEvent(self, evt):
delta = QPoint(evt.globalPos() - self.oldPos)
self.move(self.x() + delta.x(), self.y() + delta.y())
self.oldPos = evt.globalPos()
def dragEnterEvent(self, event):
if event.mimeData().hasUrls():
event.accept()
elif event.mimeData().hasText():
event.accept()
else:
event.ignore()
def dropEvent(self, event):
print("drop")
if event.mimeData().hasUrls():
url = event.mimeData().urls()[0].toString()
print("url = ", url)
self.mediaPlayer.stop()
self.mediaPlayer.setMedia(QMediaContent(QUrl(url)))
self.playButton.setEnabled(True)
self.mediaPlayer.play()
elif event.mimeData().hasText():
mydrop = event.mimeData().text()
### YouTube url
if "youtube" in mydrop:
print("is YouTube", mydrop)
self.clip.setText(mydrop)
self.getYTUrl()
else:
### normal url
print("generic url = ", mydrop)
self.mediaPlayer.setMedia(QMediaContent(QUrl(mydrop)))
self.playButton.setEnabled(True)
self.mediaPlayer.play()
self.hideSlider()
def loadFilm(self, f):
self.mediaPlayer.setMedia(QMediaContent(QUrl.fromLocalFile(f)))
self.playButton.setEnabled(True)
self.mediaPlayer.play()
def printMediaData(self):
if self.mediaPlayer.mediaStatus() == 6:
if self.mediaPlayer.isMetaDataAvailable():
res = str(self.mediaPlayer.metaData("Resolution")).partition("PyQt5.QtCore.QSize(")[2].replace(", ", "x").replace(")", "")
print("%s%s" % ("Video Resolution = ",res))
if int(res.partition("x")[0]) / int(res.partition("x")[2]) < 1.5:
self.screen43()
else:
self.screen169()
else:
print("no metaData available")
def openFileAtStart(self, filelist):
matching = [s for s in filelist if ".myformat" in s]
if len(matching) > 0:
self.loadFilm(matching)
##################### end ##################################
def stylesheet(self):
return """
QSlider::handle:horizontal
{
background: transparent;
width: 8px;
}
QSlider::groove:horizontal {
border: 1px solid #444444;
height: 8px;
background: qlineargradient(y1: 0, y2: 1,
stop: 0 #2e3436, stop: 1.0 #000000);
}
QSlider::sub-page:horizontal {
background: qlineargradient( y1: 0, y2: 1,
stop: 0 #729fcf, stop: 1 #2a82da);
border: 1px solid #777;
height: 8px;
}
QSlider::handle:horizontal:hover {
background: #2a82da;
height: 8px;
width: 18px;
border: 1px solid #2e3436;
}
QSlider::sub-page:horizontal:disabled {
background: #bbbbbb;
border-color: #999999;
}
QSlider::add-page:horizontal:disabled {
background: #2a82da;
border-color: #999999;
}
QSlider::handle:horizontal:disabled {
background: #2a82da;
}
QLineEdit
{
background: black;
color: #585858;
border: 0px solid #076100;
font-size: 8pt;
font-weight: bold;
}
"""
if __name__ == '__main__':
app = QApplication(sys.argv)
player = VideoPlayer('')
player.setAcceptDrops(True)
player.setWindowTitle("QT5 Player")
player.setWindowIcon(QIcon.fromTheme("multimedia-video-player"))
player.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
player.setGeometry(100, 300, 600, 380)
player.setContextMenuPolicy(Qt.CustomContextMenu);
player.customContextMenuRequested[QPoint].connect(player.contextMenuRequested)
player.hideSlider()
player.show()
player.widescreen = True
if len(sys.argv) > 1:
print(sys.argv[1])
if sys.argv[1].startswith("http"):
player.myurl = sys.argv[1]
player.playFromURL()
else:
player.loadFilm(sys.argv[1])
sys.exit(app.exec_())
@Axel-Erfurt
Copy link
Author

Axel-Erfurt commented Sep 8, 2018

added YouTube (needs youtube-dl)

@joselalupa
Copy link

joselalupa commented Aug 6, 2019

hi axel, i'm having trouble with pyqt5.multimedia, it's not in the instalation and can't find any information on how to make it work, could you help me? what i need to do is really simple, play a video file from my computer.
i'm working in latest ubuntu

@Axel-Erfurt
Copy link
Author

Axel-Erfurt commented Aug 9, 2019

sudo apt-get install qtmultimedia5-dev

@onechannel
Copy link

onechannel commented Apr 27, 2020

Thanks! Interesting concept. I'll try importing youtube-dl ... or have you posted that ?

@soyuznik
Copy link

soyuznik commented May 29, 2021

nice

@nguyentrongnhat4869
Copy link

nguyentrongnhat4869 commented Oct 31, 2022

Hi i'm trying to play a m3u8 link (rtsp to hls conversion) to run a stream, i got it running and the m3u8 link updates continuously. But the problem is that the position of the stream changes but the duration is always 0, so I can't rewind the stream. I tried another public m3u8 link and found that if it had the #EXT-X-ENDLIST tag, the stream would still be rewindable, and the link my m3u8 created would not have the #EXT-X-ENDLIST tag because it changed change continuously until rtsp is always interrupted. Is there any way I can rewind in this case as hls.js has implemented. Thank you very much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment