Issue
I'm trying to write a flask webpage which streams a specific range of bytes to the client, so you can watch large video files(mp4) from a network drive without having to load the whole video. It already works on my laptop and on my pc which hosts the webpage but I'm constantly running into a problem when I'm trying to use the webpage on an apple mobile device. The video just isn't loading. The videoplayer remains empty, shows no video durration and its not possible to start and watch the video.
I already tried using another browser. I tried Safari, Google and Firefox but nothing worked.
This is the HTML for the streaming page:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{title}}</title>
</head>
<body>
<video controls="true" width="650" playsinline autoplay muted loop>
<source src="{{url_for('stream.getMovie',id=id, ver=version)}}" type="video/mp4">
</video>
</body>
</html>
and this is the related flask file:
import mimetypes
from flask import Response, render_template, request, Blueprint
import os
from helper import getMovieById, getMovieList
stream = Blueprint("stream", __name__)
def removeNonDigit(str):
final = ""
for i in str:
if i in "0123456789":
final += i
return final
@stream.route("/movie/<id>/<ver>", methods=['GET'])
def getMovie(id, ver):
range = request.headers.get("range")
if not range:
return Response("Requires Range header",status=400)
videoPath: str = getMovieById(id).get("versions")[int(ver)][1]
videoSize: bytes = os.stat(videoPath).st_size
# parse Range
# "bytes=3323-"
CHUNK_SIZE = (10 ** 6)*1 # about 1 MB
x = range[:range.find("-")]
start = int(removeNonDigit(x))
end = min(start + CHUNK_SIZE, videoSize-1)
# read the data
with open(videoPath, "rb") as v:
v.seek(start)
data = v.read(CHUNK_SIZE)
content_length = end - start + 1
headers = {
"Content-Range": f'bytes {start}-{end}/{videoSize}',
"Accept-Ranges": "bytes",
"Content-Length": content_length,
"Content-Type": "video/mp4",
}
return Response(data, status=206, headers=headers, mimetype=mimetypes.guess_type(videoPath)[0],direct_passthrough=True)
@stream.route("/watch/<id>/<version>")
def player(id, version):
return render_template("movie.html", title=getMovieById(id).get("name"), version=version, id=id)
@stream.route("/banner/<id>")
def banner(id):
img = getMovieById(id).get("img")
with open(img, "rb") as f:
data = f.read()
return Response(data, status=202)
this might be a bit messy I'm trying for a long time by now
This is the output I get from the flask console when I'm connecting from my PC and not starting the video:
127.0.0.1 - - [13/Nov/2023 20:14:02] "GET /watch/1/1 HTTP/1.1" 200 -
127.0.0.1 - - [13/Nov/2023 20:14:04] "GET /movie/1/1 HTTP/1.1" 206 -
127.0.0.1 - - [13/Nov/2023 20:14:04] "GET /movie/1/1 HTTP/1.1" 206 -
But connecting from my IPad I only get this output:
192.xxx.xxx.xxx - - [13/Nov/2023 20:15:36] "GET /watch/1/0 HTTP/1.1" 200 -
192.xxx.xxx.xxx - - [13/Nov/2023 20:15:38] "GET /movie/1/0 HTTP/1.1" 206 -
I also found out that the Headers in the Request from the IPad have a byte-range of 'bytes=0-1', from the PC the byte-range is 'bytes=0-'
I'm not an expert when it comes to requests but maybe this helps...
If you need any more details or code snippets please let me know.
Thank you in advance :3
Solution
So after spending more and more time trying to figure out what was going on I found out that the browser on the phone sends a test request requesting only one bite before accepting larger ranges. I fixed it by checking if the range header is "bytes=0-1" and if so I just return one bite of the video the browser is requesting instead of sending an 1MB Chunk back.
Answered By - LennikGamez
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.