improved video streams
This commit is contained in:
@@ -126,14 +126,43 @@ def stream_video():
|
||||
def is_hls(url):
|
||||
return '.m3u8' in urllib.parse.urlparse(url).path
|
||||
|
||||
def is_dash(url):
|
||||
return urllib.parse.urlparse(url).path.lower().endswith('.mpd')
|
||||
|
||||
def guess_content_type(url):
|
||||
path = urllib.parse.urlparse(url).path.lower()
|
||||
if path.endswith('.m3u8'):
|
||||
return 'application/vnd.apple.mpegurl'
|
||||
if path.endswith('.mpd'):
|
||||
return 'application/dash+xml'
|
||||
if path.endswith('.mp4') or path.endswith('.m4v') or path.endswith('.m4s'):
|
||||
return 'video/mp4'
|
||||
if path.endswith('.webm'):
|
||||
return 'video/webm'
|
||||
if path.endswith('.ts'):
|
||||
return 'video/mp2t'
|
||||
if path.endswith('.mov'):
|
||||
return 'video/quicktime'
|
||||
if path.endswith('.m4a'):
|
||||
return 'audio/mp4'
|
||||
if path.endswith('.mp3'):
|
||||
return 'audio/mpeg'
|
||||
if path.endswith('.ogg') or path.endswith('.oga'):
|
||||
return 'audio/ogg'
|
||||
return None
|
||||
|
||||
def is_direct_media(url):
|
||||
path = urllib.parse.urlparse(url).path.lower()
|
||||
return any(path.endswith(ext) for ext in ('.mp4', '.m4v', '.m4s', '.ts', '.webm', '.mov'))
|
||||
|
||||
def proxy_response(target_url, content_type_override=None):
|
||||
# Extract the base domain to spoof the referer
|
||||
parsed_uri = urllib.parse.urlparse(target_url)
|
||||
referer = f"{parsed_uri.scheme}://{parsed_uri.netloc}/"
|
||||
referer_override = request.args.get('referer')
|
||||
if referer_override:
|
||||
referer = referer_override
|
||||
else:
|
||||
parsed_uri = urllib.parse.urlparse(target_url)
|
||||
referer = f"{parsed_uri.scheme}://{parsed_uri.netloc}/"
|
||||
|
||||
safe_request_headers = {
|
||||
'User-Agent': request.headers.get('User-Agent'),
|
||||
@@ -153,16 +182,23 @@ def stream_video():
|
||||
}
|
||||
|
||||
forwarded_headers = []
|
||||
response_content_type = None
|
||||
for name, value in resp.headers.items():
|
||||
if name.lower() in hop_by_hop:
|
||||
continue
|
||||
if name.lower() == 'content-length':
|
||||
forwarded_headers.append((name, value))
|
||||
continue
|
||||
if name.lower() == 'content-type':
|
||||
response_content_type = value
|
||||
if name.lower() == 'content-type' and content_type_override:
|
||||
continue
|
||||
forwarded_headers.append((name, value))
|
||||
|
||||
if not content_type_override:
|
||||
if not response_content_type or 'application/octet-stream' in response_content_type:
|
||||
content_type_override = guess_content_type(target_url)
|
||||
|
||||
if content_type_override:
|
||||
forwarded_headers.append(('Content-Type', content_type_override))
|
||||
|
||||
@@ -180,7 +216,7 @@ def stream_video():
|
||||
|
||||
return Response(generate(), status=resp.status_code, headers=forwarded_headers)
|
||||
|
||||
def proxy_hls_playlist(playlist_url):
|
||||
def proxy_hls_playlist(playlist_url, referer_hint=None):
|
||||
headers = {
|
||||
'User-Agent': request.headers.get('User-Agent', 'Mozilla/5.0'),
|
||||
'Accept': request.headers.get('Accept', '*/*')
|
||||
@@ -188,16 +224,30 @@ def stream_video():
|
||||
resp = session.get(playlist_url, headers=headers, timeout=30)
|
||||
resp.raise_for_status()
|
||||
base_url = resp.url
|
||||
if referer_hint:
|
||||
referer = referer_hint
|
||||
else:
|
||||
referer = f"{urllib.parse.urlparse(base_url).scheme}://{urllib.parse.urlparse(base_url).netloc}/"
|
||||
|
||||
def proxied_url(target):
|
||||
absolute = urljoin(base_url, target)
|
||||
return f"/api/stream?url={urllib.parse.quote(absolute, safe='')}&referer={urllib.parse.quote(referer, safe='')}"
|
||||
|
||||
lines = resp.text.splitlines()
|
||||
rewritten = []
|
||||
for line in lines:
|
||||
stripped = line.strip()
|
||||
if not stripped or stripped.startswith('#'):
|
||||
# Rewrite URI attributes inside tags (keys/maps)
|
||||
if 'URI="' in line:
|
||||
def repl(match):
|
||||
uri = match.group(1)
|
||||
return f'URI="{proxied_url(uri)}"'
|
||||
import re
|
||||
line = re.sub(r'URI="([^"]+)"', repl, line)
|
||||
rewritten.append(line)
|
||||
continue
|
||||
absolute = urljoin(base_url, stripped)
|
||||
proxied = f"/api/stream?url={urllib.parse.quote(absolute, safe='')}"
|
||||
rewritten.append(proxied)
|
||||
rewritten.append(proxied_url(stripped))
|
||||
if request.method == 'HEAD':
|
||||
return Response("", status=200, content_type='application/vnd.apple.mpegurl')
|
||||
|
||||
@@ -206,7 +256,11 @@ def stream_video():
|
||||
|
||||
if is_hls(video_url):
|
||||
try:
|
||||
return proxy_hls_playlist(video_url)
|
||||
referer_hint = request.args.get('referer')
|
||||
if not referer_hint:
|
||||
parsed = urllib.parse.urlparse(video_url)
|
||||
referer_hint = f"{parsed.scheme}://{parsed.netloc}/"
|
||||
return proxy_hls_playlist(video_url, referer_hint)
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
@@ -219,7 +273,8 @@ def stream_video():
|
||||
try:
|
||||
# Configure yt-dlp options
|
||||
ydl_opts = {
|
||||
'format': 'best[ext=mp4]/best[vcodec^=avc1]/best[vcodec^=vp]/best',
|
||||
# 'format': 'best[protocol*=m3u8]/best[ext=mp4]/best',
|
||||
'format_sort': ['res', 'fps', 'vcodec:avc1', 'acodec:aac'],
|
||||
'quiet': True,
|
||||
'no_warnings': True,
|
||||
'socket_timeout': 30,
|
||||
@@ -237,6 +292,7 @@ def stream_video():
|
||||
|
||||
# Try to get the URL from the info dict (works for progressive downloads)
|
||||
stream_url = info.get('url')
|
||||
protocol = info.get('protocol')
|
||||
|
||||
# If no direct URL, try to get it from formats
|
||||
if not stream_url and 'formats' in info:
|
||||
@@ -249,8 +305,21 @@ def stream_video():
|
||||
if not stream_url:
|
||||
return jsonify({"error": "Could not extract stream URL"}), 500
|
||||
|
||||
referer_hint = None
|
||||
if isinstance(info.get('http_headers'), dict):
|
||||
referer_hint = info['http_headers'].get('Referer') or info['http_headers'].get('referer')
|
||||
if not referer_hint:
|
||||
parsed = urllib.parse.urlparse(video_url)
|
||||
referer_hint = f"{parsed.scheme}://{parsed.netloc}/"
|
||||
|
||||
if protocol and 'm3u8' in protocol:
|
||||
return proxy_hls_playlist(stream_url, referer_hint)
|
||||
|
||||
if is_hls(stream_url):
|
||||
return proxy_hls_playlist(stream_url)
|
||||
return proxy_hls_playlist(stream_url, referer_hint)
|
||||
|
||||
if is_dash(stream_url):
|
||||
return proxy_response(stream_url, content_type_override='application/dash+xml')
|
||||
|
||||
return proxy_response(stream_url)
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user