]> git.gir.st - subscriptionfeed.git/blob - app/proxy/__init__.py
handle the case when we can't connect to googlevideo
[subscriptionfeed.git] / app / proxy / __init__.py
1 import re
2 import requests, requests_cache
3 from flask import Flask, Blueprint, request, Response
4 from flask_login import current_user
5
6 frontend = Blueprint('proxy', __name__)
7
8 def app():
9 app = Flask(__name__)
10 app.register_blueprint(frontend)
11 return app
12
13 # https://github.com/psf/requests/issues/5612
14 def silence_errors(generator):
15 try:
16 yield from generator
17 except requests.exceptions.RequestException:
18 return None
19
20 @frontend.route("/videoplayback/<path:path>")
21 @frontend.route("/videoplayback")
22 def videoplayback(path=None):
23 if path is not None: # seg.ts URL
24 args = path.split("/")[0:-1]
25 host = dict(zip(args[::2], args[1::2])).get('hls_chunk_host')
26 path = "/" + path.replace(';','%3B').replace('=','%3D')
27 else:
28 fvip = request.args.get("fvip", "3")
29 mn = request.args.get("mn", "").split(",")[-1]
30 host = f"r{fvip}---{mn}.googlevideo.com"
31 path = ""
32
33 REQUEST_HEADERS_WHITELIST = {
34 "accept",
35 "accept-encoding",
36 "cache-control",
37 "range",
38 }
39 RESPONSE_HEADERS_WHITELIST = {
40 "accept-ranges",
41 "cache-control",
42 "content-length",
43 "content-range",
44 "content-type",
45 "expires",
46 "x-content-type-options",
47 }
48
49 req_headers = {
50 k:v for k,v in request.headers
51 if k.lower() in REQUEST_HEADERS_WHITELIST
52 }
53 with requests_cache.disabled():
54 try:
55 r = requests.get(f"https://{host}/videoplayback{path}",
56 request.args.to_dict(),
57 headers=req_headers,
58 stream=True)
59 except requests.exceptions.ConnectionError:
60 return f"unable to connect to {host}", 502
61
62 resp_headers = {
63 k:v for k,v in r.headers.items()
64 if k.lower() in RESPONSE_HEADERS_WHITELIST
65 }
66 response = Response(
67 silence_errors(r.iter_content(chunk_size=8192)),
68 status=r.status_code, headers=resp_headers)
69 response.call_on_close(lambda: r.close())
70 response.headers['Access-Control-Allow-Origin'] = cors_origin()
71 return response
72
73 @frontend.route("/api/manifest/hls_playlist/<path:path>")
74 @frontend.route("/api/manifest/hls_variant/<path:path>")
75 def hls_manifest(path):
76 path_fixed = request.path.replace(';','%3B').replace('=','%3D')
77 with requests_cache.disabled():
78 r = requests.get(f"https://manifest.googlevideo.com{path_fixed}")
79 if not r.ok:
80 return "", r.status_code
81
82 rv = re.sub(r"^https://[a-z0-9-]+\.googlevideo\.com", "", r.text, flags=re.M)
83 if "/api/manifest/hls_variant" in request.path:
84 rv = rv + "\n#EXT-X-ENDLIST"
85 return rv, {
86 'Content-Type': 'application/x-mpegURL',
87 'Access-Control-Allow-Origin': cors_origin()
88 }
89
90 def cors_origin():
91 return f"{request.environ.get('wsgi.url_scheme')}://{request.host}"
92
93
94 if __name__ == '__main__':
95 app().run(debug=True)
Imprint / Impressum