Fix HLS proxy and player functionality (first working version)

This commit is contained in:
Mikhail Yevchenko
2026-04-01 18:21:11 +00:00
parent 198f85b67d
commit 9bbbbc5a65
5 changed files with 681 additions and 303 deletions
+42 -21
View File
@@ -35,12 +35,19 @@ def player():
try:
stream_info = dlp.get_stream_info(video_url)
from urllib.parse import quote
# URL encode for path (use -- as delimiter)
encoded_url = quote(video_url, safe="")
proxy_hls_url = f"/hls?url={encoded_url}&path=index.m3u8"
# Only set HLS URL if we actually have HLS
hls_url = stream_info.get("hls_url")
proxy_hls_url = f"/hls/{encoded_url}--index.m3u8" if hls_url else None
return render_template(
"player.html",
video_url=video_url,
proxy_hls_url=proxy_hls_url,
direct_url=stream_info.get("direct_url"),
title=stream_info.get("title", "Video"),
thumbnail=stream_info.get("thumbnail")
)
@@ -49,35 +56,49 @@ def player():
abort(500, description=str(e))
@app.route("/hls")
def hls_proxy():
@app.route("/hls/<path:full_path>")
def hls_proxy(full_path):
try:
url_param = request.args.get("url", "")
if not url_param:
abort(400, description="Missing url parameter")
from urllib.parse import unquote
path = request.args.get("path", "")
video_url = unquote(url_param)
# Split: last part is filename, rest is video URL
# Format: /hls/<encoded_video_url>/<filename>
# Since / is ambiguous (in URL and in video URL), we use a delimiter
# Format: /hls/<encoded_video_url>--<filename>
if "--" not in full_path:
abort(400, description="Invalid path format")
parts = full_path.rsplit("--", 1)
if len(parts) != 2:
abort(400, description="Invalid path format")
encoded_video_url = parts[0]
filename = parts[1]
# Decode the video URL
video_url = unquote(encoded_video_url)
if not is_valid_url(video_url):
abort(400, description="Invalid URL")
# Main playlist request - get from yt-dlp and rewrite URLs
if path == "index.m3u8" or path == "":
playlist = dlp.get_hls_playlist(video_url)
return Response(playlist, mimetype="application/vnd.apple.mpegurl")
# Sub-playlist or segment request - path is the absolute URL
segment_data = dlp.get_hls_segment(video_url, path)
# Main playlist request
if filename == "index.m3u8":
playlist = dlp.get_hls_playlist(video_url)
return Response(playlist, mimetype="application/vnd.apple.mpegurl", headers={"Cache-Control": "public, max-age=31536000"})
# Sub-playlist or segment request
segment_url = unquote(filename)
segment_data = dlp.get_hls_segment_with_retry(video_url, segment_url)
if segment_data is None:
abort(500, description="Failed to fetch segment")
if path.endswith(".m3u8"):
return Response(segment_data, mimetype="application/vnd.apple.mpegurl")
return Response(segment_data, mimetype="video/mp2t")
# Determine content-type by filename extension
if filename.endswith(".m3u8"):
return Response(segment_data, mimetype="application/vnd.apple.mpegurl", headers={"Cache-Control": "public, max-age=31536000"})
return Response(segment_data, mimetype="video/mp2t", headers={"Cache-Control": "public, max-age=31536000"})
except HTTPException:
raise
@@ -86,7 +107,7 @@ def hls_proxy():
abort(400, description=str(e))
except Exception as e:
logger.error(f"HLS proxy error: {e}")
abort(500, description="Error fetching stream")
return Response(str(e), status=500, mimetype="text/plain")
@app.errorhandler(Exception)