Skip to content

Instantly share code, notes, and snippets.

@Axel-Erfurt Axel-Erfurt/TVPlayer2.py
Last active Dec 10, 2018

Embed
What would you like to do?
Livestream TVPlayer
#!/usr/bin/python3
# -*- coding: utf-8 -*-
#############################################################################
from PyQt5.QtCore import (QPoint, QRect, Qt, QUrl)
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import (QAction, QApplication, QMainWindow, QMessageBox, QMenu, QWidget)
from PyQt5.QtMultimedia import QMediaContent, QMediaPlayer
from PyQt5.QtMultimediaWidgets import QVideoWidget
import m3u8
import os
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.urlList = []
self.resolutions = ["320", "480", "640", "960", "1280"]
self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.StreamPlayback)
self.mediaPlayer.setVolume(95)
self.mediaPlayer.error.connect(self.handleError)
self.videoWidget = QVideoWidget(self)
self.videoWidget.setAspectRatioMode(1)
self.videoWidget.setContextMenuPolicy(Qt.CustomContextMenu);
self.videoWidget.customContextMenuRequested[QPoint].connect(self.contextMenuRequested)
self.setCentralWidget(self.videoWidget)
self.mediaPlayer.setVideoOutput(self.videoWidget)
try:
self.root = os.path.dirname(os.path.abspath(__file__))
except NameError:
import sys
self.root = os.path.dirname(os.path.abspath(sys.argv[0]))
self.fullscreen = False
self.setAttribute(Qt.WA_NoSystemBackground, True)
self.setMinimumSize(320, 180)
self.setGeometry(0, 0, 480, 480 / 1.778)
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
screen = QApplication.primaryScreen()
screenGeometry = QRect(screen.geometry())
screensize = QPoint(screenGeometry.width(), screenGeometry.height())
p = QPoint(self.mapToGlobal(QPoint(screensize)) -
QPoint(self.size().width() + 2, self.size().height() + 2))
self.move(p)
screenGeometry = QApplication.desktop().availableGeometry()
screenGeo = screenGeometry.bottomRight()
self.move(screenGeo)
self.myinfo = "TV-Player\n©2018\nAxel Schneider\n\nq = Exit\nf = toggle Fullscreen\n"
self.getLists()
self.playFirst()
def playURL(self):
clip = QApplication.clipboard()
myurl = clip.text()
self.mediaPlayer.setMedia(QMediaContent(QUrl(myurl)))
self.mediaPlayer.play()
def handleError(self):
print("Error: " + self.mediaPlayer.errorString())
def handleMute(self):
if not self.mediaPlayer.isMuted():
self.mediaPlayer.setMuted(True)
else:
self.mediaPlayer.setMuted(False)
def handleAbout(self):
msg = QMessageBox.about(self, "QT5 Player", self.myinfo)
def handleFullscreen(self):
if self.fullscreen == True:
self.fullscreen = False
print("no Fullscreen")
else:
self.rect = self.geometry()
self.showFullScreen()
self.fullscreen = True
print("Fullscreen entered")
if self.fullscreen == False:
self.showNormal()
self.setGeometry(self.rect)
def handleQuit(self):
self.mediaPlayer.stop()
print("Goodbye ...")
app.quit()
def keyPressEvent(self, e):
if e.key() == Qt.Key_Q:
self.handleQuit()
elif e.key() == Qt.Key_F:
self.handleFullscreen()
elif e.key() == Qt.Key_M:
self.handleMute()
elif e.key() == Qt.Key_I:
self.handleAbout()
elif e.key() == Qt.Key_U:
self.playURL()
else:
e.accept()
def getLists(self):
the_folder = self.root + "/tv_listen"
for entry in os.listdir(the_folder):
if str(entry).endswith(".m3u8"):
self.urlList.append(the_folder + "/" + str(entry))
self.urlList.sort()
def contextMenuRequested(self, point):
channels_menu = QMenu()
for url in self.urlList:
kanal = url.rpartition(".")[0].rpartition("/")[2].upper()
m = channels_menu.addMenu(QIcon.fromTheme("tv-symbolic"),kanal)
variant_m3u8 = m3u8.load(url)
variant_m3u8.is_variant
for playlist in variant_m3u8.playlists:
if not playlist.stream_info.resolution == None:
res = str(playlist.stream_info.resolution[0])
if res in self.resolutions:
a = QAction(QIcon.fromTheme('browser'), playlist.uri, self, triggered=self.getLink)
m.addMenu(QIcon.fromTheme("computer"), res).addAction(a)
channels_menu.addSeparator()
about_action = QAction(QIcon.fromTheme("help-about"), "Info (i)", triggered = self.handleAbout)
channels_menu.addAction(about_action)
channels_menu.addSeparator()
url_action = QAction(QIcon.fromTheme("application-exit"), "play URL from clipboard (u)", triggered = self.playURL)
channels_menu.addAction(url_action)
channels_menu.addSeparator()
quit_action = QAction(QIcon.fromTheme("application-exit"), "Quit (q)", triggered = self.handleQuit)
channels_menu.addAction(quit_action)
channels_menu.exec_(self.mapToGlobal(point))
def playFirst(self):
url = "http://daserstehdde-lh.akamaihd.net/i/daserstehd_de@629196/index_1216_av-b.m3u8?sd=10&rebase=on"
self.mediaPlayer.setMedia(QMediaContent(QUrl(url)))
self.mediaPlayer.play()
def getLink(self):
action = self.sender()
link = action.text()
self.mediaPlayer.setMedia(QMediaContent(QUrl(link)))
self.mediaPlayer.play()
def closeEvent(self, event):
event.accept()
def createStatusBar(self):
self.statusBar().showMessage("Ready")
def msgbox(self, message):
QMessageBox.warning(self, "Message", message)
def wheelEvent(self, event):
mwidth = self.frameGeometry().width()
mheight = self.frameGeometry().height()
mleft = self.frameGeometry().left()
mtop = self.frameGeometry().top()
mscale = event.angleDelta().y() / 3
self.resize(mwidth + mscale, (mwidth + mscale) / 1.778)
def mouseMoveEvent(self, event):
if event.buttons() == Qt.LeftButton:
self.move(event.globalPos() \
- QPoint(self.frameGeometry().width() / 2, \
self.frameGeometry().height() / 2))
event.accept()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
mainWin = MainWindow()
mainWin.show()
sys.exit(app.exec_())
@Axel-Erfurt

This comment has been minimized.

Copy link
Owner Author

commented Dec 10, 2018

Requirements:
python2 or python3
PyQt5
m3u8

made and testet in Linux

place your m3u8 files inside the tv_listen folder
(create a tv_listen folder in the folder where TVPlayer2.py is located)

it reads the m3u8 files and creates a contextmenu (named from file) with the resolutions (as submenu) defined in

self.resolutions = ["320", "480", "640", "960", "1280"]

(you can add more if you want)

m3u8 should look like this:

#EXTM3U
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=184000,RESOLUTION=320x180,CODECS="avc1.66.30, mp4a.40.2"
http://daserstehdde-lh.akamaihd.net/i/daserstehd_de@629196/index_184_av-p.m3u8?sd=10&rebase=on
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=184000,RESOLUTION=320x180,CODECS="avc1.66.30, mp4a.40.2"
http://daserstehdde-lh.akamaihd.net/i/daserstehd_de@629196/index_184_av-b.m3u8?sd=10&rebase=on
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=320000,RESOLUTION=480x270,CODECS="avc1.66.30, mp4a.40.2"
http://daserstehdde-lh.akamaihd.net/i/daserstehd_de@629196/index_320_av-p.m3u8?sd=10&rebase=on
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=320000,RESOLUTION=480x270,CODECS="avc1.66.30, mp4a.40.2"
http://daserstehdde-lh.akamaihd.net/i/daserstehd_de@629196/index_320_av-b.m3u8?sd=10&rebase=on
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=640000,RESOLUTION=512x288,CODECS="avc1.77.30, mp4a.40.2"
http://daserstehdde-lh.akamaihd.net/i/daserstehd_de@629196/index_640_av-p.m3u8?sd=10&rebase=on
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=640000,RESOLUTION=512x288,CODECS="avc1.77.30, mp4a.40.2"
http://daserstehdde-lh.akamaihd.net/i/daserstehd_de@629196/index_640_av-b.m3u8?sd=10&rebase=on
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1216000,RESOLUTION=640x360,CODECS="avc1.77.30, mp4a.40.2"
http://daserstehdde-lh.akamaihd.net/i/daserstehd_de@629196/index_1216_av-p.m3u8?sd=10&rebase=on
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1216000,RESOLUTION=640x360,CODECS="avc1.77.30, mp4a.40.2"
http://daserstehdde-lh.akamaihd.net/i/daserstehd_de@629196/index_1216_av-b.m3u8?sd=10&rebase=on
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1992000,RESOLUTION=960x540,CODECS="avc1.77.30, mp4a.40.2"
http://daserstehdde-lh.akamaihd.net/i/daserstehd_de@629196/index_1992_av-p.m3u8?sd=10&rebase=on
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1992000,RESOLUTION=960x540,CODECS="avc1.77.30, mp4a.40.2"
http://daserstehdde-lh.akamaihd.net/i/daserstehd_de@629196/index_1992_av-b.m3u8?sd=10&rebase=on
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2691000,RESOLUTION=960x540,CODECS="avc1.77.30, mp4a.40.2"
http://daserstehdde-lh.akamaihd.net/i/daserstehd_de@629196/index_2692_av-p.m3u8?sd=10&rebase=on
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2691000,RESOLUTION=960x540,CODECS="avc1.77.30, mp4a.40.2"
http://daserstehdde-lh.akamaihd.net/i/daserstehd_de@629196/index_2692_av-b.m3u8?sd=10&rebase=on
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=3776000,RESOLUTION=1280x720,CODECS="avc1.64001f, mp4a.40.2"
http://daserstehdde-lh.akamaihd.net/i/daserstehd_de@629196/index_3776_av-p.m3u8?sd=10&rebase=on
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=3776000,RESOLUTION=1280x720,CODECS="avc1.64001f, mp4a.40.2"
http://daserstehdde-lh.akamaihd.net/i/daserstehd_de@629196/index_3776_av-b.m3u8?sd=10&rebase=on

@Axel-Erfurt

This comment has been minimized.

Copy link
Owner Author

commented Dec 10, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.