From e20b388a690f877a29ab8c7147cbc094df5a8283 Mon Sep 17 00:00:00 2001 From: Roy Olav Purser Date: Fri, 14 May 2021 11:31:30 +0200 Subject: [PATCH] refactor upstream --- frontend/index.html | 29 +++--- stream.py | 229 ++++++++++++++++++++++---------------------- 2 files changed, 127 insertions(+), 131 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index 6e2095b..1752073 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1,23 +1,24 @@ - - + + - - - - - - - - - - + + + + + + + + + + + - - + + diff --git a/stream.py b/stream.py index 62f75ba..be14dea 100755 --- a/stream.py +++ b/stream.py @@ -18,6 +18,10 @@ providers["nrk"] = "https://tv.nrk.no" providers["svt"] = "https://svtplay.se" providers["youtube"] = "https://www.youtube.com/watch?v=" providers["twitch"] = "https://twitch.tv" +playlist = None +icecast_server = os.environ.get("ICECAST_SERVER") +stream_server = os.environ.get("STREAM_SERVER") +proxy_server = os.environ.get("PROXY_SERVER") class ProxyElem(): def __init__(self, proxy): @@ -35,7 +39,7 @@ class ProxyElem(): return session def __repr__(self): return str(self.proxy) - + proxies = {} for key in providers: proxies[key] = [] @@ -50,10 +54,69 @@ for key in providers: for proxy in current: proxies[key].append(ProxyElem(proxy)) -playlist = None -icecast_server = os.environ.get("ICECAST_SERVER") -stream_server = os.environ.get("STREAM_SERVER") -proxy_server = os.environ.get("PROXY_SERVER") +class UpstreamHandler(): + def __init__(self, handler): + self.render_url = None + self.stream_url = None + self.proxy = None + self.upstream = None + self.upstream_safe = None + self.render = False + self.stream = True + provider = handler.get_query_argument("provider", None) + render_str = handler.get_query_argument("render", "false") + if render_str.lower() == "true": + self.render = True + self.stream = False + if provider in providers.keys(): + path = handler.request.path + if provider == "youtube": + path = path.strip("/") + if isinstance(stream_server, str): + self.render_url = f'{stream_server}{handler.request.path}?provider={provider}&render=true' + self.stream_url = f'{stream_server}{handler.request.path}?provider={provider}' + else: + self.render_url = f'{handler.request.path}?provider={provider}&render=true' + self.stream_url = f'{handler.request.path}?provider={provider}' + + src = providers[provider] + path + proxy_list = None + proxy_iter = None + proxy_list_orig = proxies.get(provider) + if isinstance(proxy_list_orig, list): + proxy_list = proxy_list_orig.copy() + proxy_iter = proxy_list_orig.copy() + if isinstance(proxy_list, list): + for i in proxy_iter: + current_list = proxy_list.copy() + current = proxy_list.pop() + proxy_list = [current] + proxy_list + try: + resp = requests.head(src, allow_redirects=True, proxies=current.req, timeout=2) + except Exception as e: + logger.info(e) + else: + if resp.url.lower().startswith("https://consent.youtube.com"): + self.upstream = src + self.upstream_safe = urllib.parse.quote(src) + else: + self.upstream = resp.url + self.upstream_safe = urllib.parse.quote(resp.url) + self.proxy = current + proxies[provider] = current_list + return + def meta(self): + data = {} + try: + embed_url = f'https://noembed.com/embed?url={self.upstream_safe}' + logger.info(embed_url) + resp = requests.get(embed_url) + data_raw = json.loads(resp.text) + if isinstance(data_raw, dict): + data = data_raw + except Exception as e: + logger.info(e) + return data if icecast_server is not None and stream_server is not None: try: @@ -156,136 +219,68 @@ def rewrite(current, provider, proxy): ndata += links.pop(0) ndata += "\n" return ndata - class MainHandler(tornado.web.RequestHandler): - def handle_any(self, write): - provider = self.get_query_argument("provider", None) - render = self.get_query_argument("render", "false") - embed = self.get_query_argument("embed", "false") - if isinstance(provider, str): - if render.lower() == "true": - self.handle_render(provider) - elif embed.lower() == "true": - self.handle_embed(provider) - else: - self.handle_stream(provider, write) + def handle_any(self): + handler = UpstreamHandler(self) + if handler.render: + self.handle_render(handler) + elif handler.stream: + self.handle_stream(handler) else: logger.info(f'provider missing {self.request.uri}') self.set_status(404) - if write: - self.write("Stream not found. (provider missing)") + self.write("Stream not found. (provider missing)") - def handle_render(self, provider): + def handle_render(self, handler): if template_js is not None and template_html is not None: - origin = self.request.path - if stream_server is not None: - origin = f'{stream_server}{self.request.path}' - stream_path = f'{origin}?provider={provider}' - rendered_js = template_js.generate(stream=stream_path); + rendered_js = template_js.generate(stream=handler.stream_url); b64_js = str(base64.b64encode(rendered_js), "ascii") script = f'data:text/javascript;charset=utf-8;base64,{b64_js}' - rendered_html = template_html.generate(script=script, videojs_version=videojs_version, chromecast_version=chromecast_version, custom_style=custom_style, provider=provider, origin=origin) + meta = handler.meta() + data = {} + if meta is not None: + data["og:title"] = meta.get("title", "") + data["og:image"] = meta.get("thumbnail_url", "") + data["og:video:height"] = meta.get("height", "180") + data["og:video:width"] = meta.get("width", "320") + data["og:image:height"] = meta.get("thumbnail_height", "180") + data["og:image:width"] = meta.get("thumbnail_width", "320") + data["script"] = script + data["videojs_version"] = videojs_version + data["chromecast_version"] = chromecast_version + data["custom_style"] = custom_style + data["stream_url"] = handler.stream_url + data["render_url"] = handler.render_url + rendered_html = template_html.generate(data=data) self.write(rendered_html) else: self.set_status(404) self.write("HTML template missing.") - def handle_embed(self, provider): - width_str = self.get_query_argument("maxwidth", None) - height_str = self.get_query_argument("maxheight", None) - width = 320 - height = 180 - try: - if isinstance(width_str, str): - width_new = int(width_str) - if width_new >= 16: - width = width_new - else: - raise ValueError("invalid width") - if isinstance(height_str, str): - height_new = int(height_str) - if height_new >= 9: - height = height_new - else: - raise ValueError("invalid height") - except Exception: - pass - else: - if isinstance(width_str, str) and isinstance(height_str, str): - pass - elif isinstance(width_str, str): - width = int((width // 16) * 16) - height = int((width * 9) // 16) - elif isinstance(height_str, str): - height = int((height // 9) * 9) - width = int((height * 16) // 9) - origin = self.request.path - if stream_server is not None: - origin = f'{stream_server}{self.request.path}' - embed_json = {} - embed_json["version"] = "1.0" - embed_json["type"] = "video" - embed_json["width"] = width - embed_json["height"] = height - embed_json["provider_name"] = "stream.purser.it" - embed_json["provider_url"] = "https://stream.purser.it" - embed_json["html"] = str(template_embed.generate(origin=origin, provider=provider, width=width, height=height), "utf-8") - self.set_header("Content-Type", "application/json; charset=utf-8") - self.write(json.dumps(embed_json)) - def handle_stream(self, provider, write): + def handle_stream(self, handler): upstream = None - proxy = None - if provider in providers.keys(): - path = self.request.path - if provider == "youtube": - path = path.strip("/") - src = providers[provider] + path - proxy_list = None - proxy_iter = None - proxy_list_orig = proxies.get(provider) - if isinstance(proxy_list_orig, list): - proxy_list = proxy_list_orig.copy() - proxy_iter = proxy_list_orig.copy() - if isinstance(proxy_list, list): - for i in proxy_iter: - current_list = proxy_list.copy() - current = proxy_list.pop() - proxy_list = [current] + proxy_list - try: - resp = requests.head(src, allow_redirects=True, proxies=current.req, timeout=2) - if resp is not None: - logger.info(src) - src = resp.url - except Exception as e: - logger.info(e) - else: - proxies[provider] = current_list - proxy = current + if handler.proxy is not None: + try: + logger.info(proxy) + streams = handler.proxy.stream().streams(handler.upstream) + except Exception as e: + logger.info(e) + else: + for key in reversed(streams): + stream = streams.get(key) + logger.info(stream) + if hasattr(stream, "url"): + upstream = stream.url break - if proxy is not None: - try: - logger.info(proxy) - streams = proxy.stream().streams(src) - except Exception as e: - logger.info(e) - else: - for key in reversed(streams): - stream = streams.get(key) - logger.info(stream) - if hasattr(stream, "url"): - upstream = stream.url - break else: logger.info(f'invalid provider ({provider})') self.set_status(404) - if write: - self.write("Stream not found. (invalid provider)") + self.write("Stream not found. (invalid provider)") return if upstream is None: logger.info(f'invalid upstream ({provider})') self.set_status(404) - if write: - self.write("Stream not found. (invalid upstream)") + self.write("Stream not found. (invalid upstream)") else: ctype = upstream_type(upstream, proxy) data = None @@ -294,8 +289,8 @@ class MainHandler(tornado.web.RequestHandler): else: ldata = {} ldata["upstream"] = upstream - ldata["proxy"] = proxy.proxy - ldata["proxied"] = isinstance(proxy.proxy, str) + ldata["proxy"] = handler.proxy.proxy + ldata["proxied"] = isinstance(handler.proxy.proxy, str) links = [ldata] if isinstance(proxy_server, str): try: @@ -312,9 +307,9 @@ class MainHandler(tornado.web.RequestHandler): self.set_header("Content-Type", "application/vnd.apple.mpegurl") self.write(data) def get(self): - self.handle_any(True) + self.handle_any() def head(self): - self.handle_any(False) + self.handle_any() class FileHandler(tornado.web.RequestHandler): def get(self):