From e8d52c43ec24eb3be1218586241a6a042614d801 Mon Sep 17 00:00:00 2001 From: girst Date: Sun, 3 Jan 2021 15:47:37 +0100 Subject: [PATCH] working HLS proxy (for livestreams) --- app/common/common.py | 7 ++++- app/proxy/__init__.py | 72 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 73 insertions(+), 6 deletions(-) diff --git a/app/common/common.py b/app/common/common.py index 37726ab..14c4b8f 100644 --- a/app/common/common.py +++ b/app/common/common.py @@ -239,9 +239,11 @@ def get_video_info(video_id, sts=0, algo=""): maybe_metadata = metadata if 'videoDetails' in metadata else None return None, None, maybe_metadata, 'player', player_error if metadata['videoDetails'].get('isLive', False): + if sts != -1: # XXX temp for x-hls return None, None, metadata, 'livestream', None if not 'formats' in metadata['streamingData']: + if sts != -1: # XXX temp for x-hls continue # no urls formats = metadata['streamingData'].get('formats',[]) @@ -261,7 +263,10 @@ def get_video_info(video_id, sts=0, algo=""): stream_map.update({'dashManifestUrl': metadata['streamingData'].get('dashManifestUrl')}) # todo: check if we have urls or try again - url = sorted(formats, key=lambda k: k['height'], reverse=True)[0]['url'] + if sts != -1: # XXX temp for x-hls + url = sorted(formats, key=lambda k: k['height'], reverse=True)[0]['url'] + else: + url = None # ip-locked videos can be recovered if the proxy module is loaded: is_geolocked = 'geolocked' if 'gcr' in parse_qs(urlparse(url).query) else None diff --git a/app/proxy/__init__.py b/app/proxy/__init__.py index 8c33d78..2b3f9ff 100644 --- a/app/proxy/__init__.py +++ b/app/proxy/__init__.py @@ -1,3 +1,4 @@ +import re import requests, requests_cache from flask import Flask, Blueprint, request, Response from flask_login import current_user @@ -16,11 +17,19 @@ def silence_errors(generator): except requests.exceptions.RequestException: return None +@frontend.route("/videoplayback/") @frontend.route("/videoplayback") -def videoplayback(): - fvip = request.args.get("fvip", "3") - mn = request.args.get("mn", "").split(",")[-1] - host = f"r{fvip}---{mn}.googlevideo.com" +def videoplayback(path=None): + if path is not None: # seg.ts URL + args = path.split("/")[0:-1] + host = dict(zip(args[::2], args[1::2])).get('hls_chunk_host') + path = "/" + path.replace(';','%3B').replace('=','%3D') + #host = request.args.get('__host__') + else: + fvip = request.args.get("fvip", "3") + mn = request.args.get("mn", "").split(",")[-1] + host = f"r{fvip}---{mn}.googlevideo.com" + path = "" REQUEST_HEADERS_WHITELIST = { "accept", @@ -43,7 +52,7 @@ def videoplayback(): if k.lower() in REQUEST_HEADERS_WHITELIST } with requests_cache.disabled(): - r = requests.get(f"https://{host}/videoplayback", request.args.to_dict(), headers=req_headers, stream=True) + r = requests.get(f"https://{host}/videoplayback{path}", request.args.to_dict(), headers=req_headers, stream=True) resp_headers = { k:v for k,v in r.headers.items() @@ -51,8 +60,61 @@ def videoplayback(): } response = Response(silence_errors(r.iter_content(chunk_size=8192)), status=r.status_code, headers=resp_headers) response.call_on_close(lambda: r.close()) + response.headers['Access-Control-Allow-Origin'] = '*' return response +@frontend.route("/api/manifest/hls_playlist/") +@frontend.route("/api/manifest/hls_variant/") +def hls_manifest(path): + with requests_cache.disabled(): + path_fixed = request.path.replace(';','%3B').replace('=','%3D') + r = requests.get(f"https://manifest.googlevideo.com{path_fixed}", request.args.to_dict()) + if not r.ok: + return "", r.status_code + + lines = [] + for line in r.text.splitlines(): + if line.startswith('#'): lines.append(line) + elif line.startswith('https://'): + scheme, _, host, _path = line.split('/', 3) + assert '.googlevideo.com' in host + #lines.append("/" + _path + "?__host__=" + host) + lines.append("/" + _path ) + else: raise StopIteration + #if line.startswith('https://manifest.googlevideo.com/'): + rv = "\n".join(lines) + #rv = r.text + #rv = rv.replace("https://manifest.googlevideo.com/", f"https://{request.host}/") + #rv = re.sub(r"https://([a-z0-9-]+\.googlevideo\.com)", r"https://subscriptions.gir.st/ts-proxy/\1", rv) + return rv, {'content-type': 'application/x-mpegURL', 'Access-Control-Allow-Origin': '*'} + + +@frontend.route("/x-hls/") +@frontend.route("/x-hls/") +def xxx(vid="5qap5aO4i9A"): + from ..common.common import get_video_info + _, map, _, _, _ = get_video_info(vid, -1) + url = map['hlsManifestUrl'].replace("https://manifest.googlevideo.com", "") + return f""" + + + + + + +""" + if __name__ == '__main__': app().run(debug=True) -- 2.39.3