import re import requests, requests_cache from flask import Flask, Blueprint, request, Response, g from flask_login import current_user from werkzeug.exceptions import Forbidden from ..common.common import cf frontend = Blueprint('proxy', __name__) def app(): app = Flask(__name__) app.register_blueprint(frontend) return app # https://github.com/psf/requests/issues/5612 def silence_errors(generator): try: yield from generator except requests.exceptions.RequestException: return None @frontend.route("/videoplayback/") @frontend.route("/videoplayback") 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') 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", "accept-encoding", "cache-control", "range", } RESPONSE_HEADERS_WHITELIST = { "accept-ranges", "cache-control", "content-length", "content-range", "content-type", "expires", "x-content-type-options", } req_headers = { k:v for k,v in request.headers if k.lower() in REQUEST_HEADERS_WHITELIST } with requests_cache.disabled(): try: r = requests.get(f"https://{host}/videoplayback{path}", request.args.to_dict(), headers=req_headers, stream=True) except requests.exceptions.ConnectionError: return f"unable to connect to {host}", 502 resp_headers = { k:v for k,v in r.headers.items() if k.lower() in RESPONSE_HEADERS_WHITELIST } 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'] = cors_origin() return response @frontend.route("/api/manifest/hls_playlist/") @frontend.route("/api/manifest/hls_variant/") def hls_manifest(path): path_fixed = request.path.replace(';','%3B').replace('=','%3D') with requests_cache.disabled(): r = requests.get(f"https://manifest.googlevideo.com{path_fixed}") if not r.ok: return "", r.status_code rv = re.sub(r"^https://[a-z0-9-]+\.googlevideo\.com", "", r.text, flags=re.M) if "/api/manifest/hls_variant" in request.path: rv = rv + "\n#EXT-X-ENDLIST" return rv, { 'Content-Type': 'application/x-mpegURL', 'Access-Control-Allow-Origin': cors_origin() } def cors_origin(): return f"{request.environ.get('wsgi.url_scheme')}://{request.host}" def proxy_allowed(): """ the proxy can be restricted to logged-in users by a config flag """ require_auth = cf.getboolean('proxy', 'require_auth', fallback=False) is_authd = not current_user.is_anonymous return is_authd or not require_auth @frontend.before_request def check_auth(): if not proxy_allowed(): raise Forbidden("limited to authenticated users") @frontend.before_app_request def propagate_auth_requirement(): g.proxy_on = proxy_allowed() if __name__ == '__main__': app().run(debug=True)