I have a Qt application to show videos from urls using:
player = QMediaPlayer()
...
player.setMedia(QMediaContent(QUrl(video.url)))
...
But unable to download the video using the same url with urllib.request
, response code is always 200
but Content-Length
is zero.
from urllib.request import urlopen, Request
rq = Request(video.url)
rp = urlopen(rq)
rp.headers["Content-Length"] # always 0
How Qt is able to show video when im failing to download?
MWE
from PyQt5.QtWidgets import QApplication
from PyQt5.QtMultimediaWidgets import QVideoWidget
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent
from PyQt5.QtCore import QUrl
from urllib.request import urlopen
import sys
class Test(QVideoWidget):
def __init__(self, *args, **kwargs):
QVideoWidget.__init__(self, *args, **kwargs)
self.player = QMediaPlayer()
self.player.setVideoOutput(self)
self.player.mediaStatusChanged.connect(self.statusChanged)
self.url = "https://api16-normal-c-useast1a.tiktokv.com/aweme/v1/play/?video_id=v09044190000brfkq160bkbi3ui1oko0&line=0&ratio=540p&media_type=4&vr_type=0&improve_bitrate=0&logo_name=tiktok_m&quality_type=11&source=PackSourceEnum_AWEME_DETAIL"
self.player.setMedia(QMediaContent(QUrl(self.url)))
def statusChanged(self, status):
if status == QMediaPlayer.LoadedMedia:
self.player.play()
elif status == QMediaPlayer.EndOfMedia:
self.player.play()
def download(self):
rp = urlopen(self.url)
if int(rp.headers["Content-Length"]) != 0:
with open("test.mp4", "wb") as mp4:
while True:
chunk = rp.read(1024)
if not chunk: break
mp4.write(chunk)
else:
raise Exception("Content-Length is Zero")
if __name__ == "__main__":
app = QApplication(sys.argv)
test = Test()
test.show()
# uncomment to download
# test.download()
sys.exit(app.exec_())
After several attempts I found that you must set the "user-agent". Qt does not directly handle QMediaPlayer requests but backends like gstreamer on Linux, and these set the user-agent so it works correctly.
I also found that you shouldn't be the user-agent of browsers like "Mozilla/5.0 ..." are probably rejected as a means of protection.
from urllib.request import Request, urlopen
# ...
class Test(QVideoWidget):
# ...
def download(self):
rq = Request(self.url, headers={"user-agent": "MyApp/1.0"})
with urlopen(rq) as rp:
with open("test.mp4", "wb") as mp4:
while True:
chunk = rp.read(1024)
if not chunk:
break
mp4.write(chunk)