format with black

This commit is contained in:
Roy Olav Purser 2022-02-04 17:44:47 +01:00
parent 1c8df48822
commit 0fb28e0741
Signed by: roypur
GPG Key ID: E14D26A036F21656
4 changed files with 173 additions and 52 deletions

View File

@ -155,7 +155,9 @@ for key in providers:
proxies[elem] = [] proxies[elem] = []
new_providers[elem] = providers[key] new_providers[elem] = providers[key]
for proxy_link, region_name, country in zip(proxy_current, region_current, proxy_countries): for proxy_link, region_name, country in zip(
proxy_current, region_current, proxy_countries
):
new_key = key new_key = key
if country is not None: if country is not None:
new_key = f"{key}_{country}" new_key = f"{key}_{country}"
@ -177,8 +179,12 @@ for proxy_provider in proxies.values():
if isinstance(proxy_elem, ProxyElem) and isinstance(proxy_elem.proxy, str): if isinstance(proxy_elem, ProxyElem) and isinstance(proxy_elem.proxy, str):
proxy_keys.append(proxy_elem.proxy) proxy_keys.append(proxy_elem.proxy)
streamlink_sessions[proxy_elem.proxy] = streamlink.Streamlink() streamlink_sessions[proxy_elem.proxy] = streamlink.Streamlink()
streamlink_sessions[proxy_elem.proxy].set_option("http-proxy", "socks5://" + proxy_elem.proxy) streamlink_sessions[proxy_elem.proxy].set_option(
streamlink_sessions[proxy_elem.proxy].set_option("https-proxy", "socks5://" + proxy_elem.proxy) "http-proxy", "socks5://" + proxy_elem.proxy
)
streamlink_sessions[proxy_elem.proxy].set_option(
"https-proxy", "socks5://" + proxy_elem.proxy
)
if icecast_server is not None and stream_server is not None: if icecast_server is not None and stream_server is not None:
try: try:

View File

@ -6,7 +6,9 @@ import json
playlist = None playlist = None
try: try:
resp = requests.get("https://git.purser.it/roypur/icecast-relay/raw/branch/master/icecast.xml") resp = requests.get(
"https://git.purser.it/roypur/icecast-relay/raw/branch/master/icecast.xml"
)
dom = xml.dom.minidom.parseString(resp.text) dom = xml.dom.minidom.parseString(resp.text)
except Exception as e: except Exception as e:
print(e) print(e)
@ -39,7 +41,9 @@ except Exception as e:
print(e) print(e)
else: else:
try: try:
resp = requests.get(f'https://cdn.jsdelivr.net/npm/@silvermine/videojs-chromecast@{chromecast_version}/dist/silvermine-videojs-chromecast.min.css') resp = requests.get(
f"https://cdn.jsdelivr.net/npm/@silvermine/videojs-chromecast@{chromecast_version}/dist/silvermine-videojs-chromecast.min.css"
)
except Exception as e: except Exception as e:
print(e) print(e)
else: else:
@ -48,17 +52,19 @@ else:
with open("/app/version/chromecast.txt", "w") as f: with open("/app/version/chromecast.txt", "w") as f:
f.write(chromecast_version) f.write(chromecast_version)
def store_cdnjs(name): def store_cdnjs(name):
version = None version = None
try: try:
resp = requests.get(f'https://api.cdnjs.com/libraries/{name}?fields=version') resp = requests.get(f"https://api.cdnjs.com/libraries/{name}?fields=version")
data = json.loads(resp.text) data = json.loads(resp.text)
version = data["version"] version = data["version"]
except Exception as e: except Exception as e:
print(e) print(e)
else: else:
with open(f'/app/version/{name}.txt', "w") as f: with open(f"/app/version/{name}.txt", "w") as f:
f.write(version) f.write(version)
store_cdnjs("video.js") store_cdnjs("video.js")
store_cdnjs("font-awesome") store_cdnjs("font-awesome")

View File

@ -10,11 +10,15 @@ import config
import stream_providers import stream_providers
from typing import Optional, cast, Any from typing import Optional, cast, Any
logging.basicConfig(format='[%(filename)s:%(lineno)d] %(message)s', stream=sys.stdout, level=logging.INFO) logging.basicConfig(
format="[%(filename)s:%(lineno)d] %(message)s",
stream=sys.stdout,
level=logging.INFO,
)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class UpstreamHandler(): class UpstreamHandler:
def __init__(self): def __init__(self):
self.provider: Optional[str] = None self.provider: Optional[str] = None
self.raw: bool = False self.raw: bool = False
@ -45,18 +49,18 @@ class UpstreamHandler():
raw_str = handler.get_query_argument("raw", None) raw_str = handler.get_query_argument("raw", None)
direct_str = handler.get_query_argument("direct", None) direct_str = handler.get_query_argument("direct", None)
true_values = ['y', 'yes', 't', 'true', 'on', '1'] true_values = ["y", "yes", "t", "true", "on", "1"]
if isinstance(direct_str, str): if isinstance(direct_str, str):
try: try:
self.direct = (direct_str.lower() in true_values) self.direct = direct_str.lower() in true_values
except ValueError as e: except ValueError as e:
logger.info(e) logger.info(e)
if isinstance(raw_str, str): if isinstance(raw_str, str):
try: try:
self.raw = (raw_str.lower() in true_values) self.raw = raw_str.lower() in true_values
except ValueError as e: except ValueError as e:
logger.info(e) logger.info(e)
if self.provider in config.providers.keys(): if self.provider in config.providers:
self.valid = True self.valid = True
path = handler.request.path path = handler.request.path
if self.provider.startswith("nextcloud"): if self.provider.startswith("nextcloud"):
@ -82,7 +86,7 @@ class UpstreamHandler():
class MainHandler(tornado.web.RequestHandler): class MainHandler(tornado.web.RequestHandler):
async def handle_any(self, redir): async def handle_any(self):
handler = UpstreamHandler() handler = UpstreamHandler()
await handler.setup(self) await handler.setup(self)
if handler.valid: if handler.valid:
@ -102,18 +106,29 @@ class MainHandler(tornado.web.RequestHandler):
if config.template_script is not None and config.template_html is not None: if config.template_script is not None and config.template_html is not None:
provider_data = None provider_data = None
if handler.provider.startswith("nextcloud"): if handler.provider.startswith("nextcloud"):
provider_data = await stream_providers.get_nextcloud(handler.upstream, handler.proxy, logger) provider_data = await stream_providers.get_nextcloud(
handler.upstream, handler.proxy, logger
)
elif handler.provider.startswith("seafile"): elif handler.provider.startswith("seafile"):
provider_data = await stream_providers.get_seafile(handler.upstream, handler.proxy, logger) provider_data = await stream_providers.get_seafile(
handler.upstream, handler.proxy, logger
)
else: else:
provider_data = await stream_providers.get_any(handler.upstream, handler.proxy, logger) provider_data = await stream_providers.get_any(
handler.upstream, handler.proxy, logger
)
video_info = {} video_info = {}
if handler.direct: if handler.direct:
video_info["upstream"] = provider_data.upstream() video_info["upstream"] = provider_data.upstream()
video_info["poster"] = provider_data.thumbnail() video_info["poster"] = provider_data.thumbnail()
else: else:
proxied = await handler.proxy.proxy_url([(provider_data.upstream(), provider_data.proxy_ctype()), provider_data.thumbnail()]) proxied = await handler.proxy.proxy_url(
[
(provider_data.upstream(), provider_data.proxy_ctype()),
provider_data.thumbnail(),
]
)
video_info["upstream"] = proxied[0] video_info["upstream"] = proxied[0]
video_info["poster"] = proxied[1] video_info["poster"] = proxied[1]
@ -136,22 +151,24 @@ class MainHandler(tornado.web.RequestHandler):
script = config.template_script.generate(info=json.dumps(video_info)) script = config.template_script.generate(info=json.dumps(video_info))
b64 = str(base64.b64encode(script), "ascii") b64 = str(base64.b64encode(script), "ascii")
data = {} data = {}
script_file = f'data:text/javascript;charset=utf-8;base64,{b64}' script_file = f"data:text/javascript;charset=utf-8;base64,{b64}"
data["script"] = script_file data["script"] = script_file
data["videojs_version"] = config.videojs_version data["videojs_version"] = config.videojs_version
data["chromecast_version"] = config.chromecast_version data["chromecast_version"] = config.chromecast_version
data["font_awesome_version"] = config.font_awesome_version data["font_awesome_version"] = config.font_awesome_version
rendered_html = config.template_html.generate(data=data, meta=meta, title=title) rendered_html = config.template_html.generate(
data=data, meta=meta, title=title
)
self.write(rendered_html) self.write(rendered_html)
else: else:
self.set_status(404) self.set_status(404)
self.write("HTML template missing.") self.write("HTML template missing.")
async def get(self): async def get(self):
await self.handle_any(True) await self.handle_any()
async def head(self): async def head(self):
await self.handle_any(False) await self.handle_any()
def data_received(self, _): def data_received(self, _):
pass pass
@ -186,13 +203,32 @@ class StyleHandler(tornado.web.RequestHandler):
try: try:
handlers: list[tuple[tornado.routing.Matcher, tornado.web.RequestHandler]] = [] handlers: list[tuple[tornado.routing.Matcher, tornado.web.RequestHandler]] = []
handlers.append((cast(tornado.routing.Matcher, tornado.routing.PathMatches("/sources.m3u8")), cast(tornado.web.RequestHandler, PlaylistHandler))) handlers.append(
handlers.append((cast(tornado.routing.Matcher, tornado.routing.PathMatches("/favicon.ico")), cast(tornado.web.RequestHandler, IconHandler))) (
handlers.append((cast(tornado.routing.Matcher, tornado.routing.PathMatches("/style.css")), cast(tornado.web.RequestHandler, StyleHandler))) cast(tornado.routing.Matcher, tornado.routing.PathMatches("/sources.m3u8")),
handlers.append((cast(tornado.routing.Matcher, tornado.routing.AnyMatches()), cast(tornado.web.RequestHandler, MainHandler))) cast(tornado.web.RequestHandler, PlaylistHandler),
)
)
handlers.append(
(
cast(tornado.routing.Matcher, tornado.routing.PathMatches("/favicon.ico")),
cast(tornado.web.RequestHandler, IconHandler),
)
)
handlers.append(
(
cast(tornado.routing.Matcher, tornado.routing.PathMatches("/style.css")),
cast(tornado.web.RequestHandler, StyleHandler),
)
)
handlers.append(
(
cast(tornado.routing.Matcher, tornado.routing.AnyMatches()),
cast(tornado.web.RequestHandler, MainHandler),
)
)
app_web = tornado.web.Application(cast(Any, handlers)) app_web = tornado.web.Application(cast(Any, handlers))
app_web.listen(8080) app_web.listen(8080)
tornado.ioloop.IOLoop.current().start() tornado.ioloop.IOLoop.current().start()
except KeyboardInterrupt: except KeyboardInterrupt:
print() print()

View File

@ -11,21 +11,26 @@ import json
import re import re
import config import config
class DummyLogger():
class DummyLogger:
def debug(self, msg): def debug(self, msg):
pass pass
def warning(self, msg): def warning(self, msg):
pass pass
def error(self, msg): def error(self, msg):
pass pass
class MetaParser(html.parser.HTMLParser): class MetaParser(html.parser.HTMLParser):
def __init__(self): def __init__(self):
self.meta_data = {} self.meta_data = {}
super().__init__() super().__init__()
def handle_starttag_meta(self, attrs): def handle_starttag_meta(self, attrs):
name = None name = None
for attr in (attrs + attrs): for attr in attrs + attrs:
if len(attr) == 2: if len(attr) == 2:
if isinstance(name, str): if isinstance(name, str):
if attr[0] == "content": if attr[0] == "content":
@ -33,9 +38,10 @@ class MetaParser(html.parser.HTMLParser):
return return
elif attr[0] == "property": elif attr[0] == "property":
name = attr[1] name = attr[1]
def handle_starttag_input(self, attrs): def handle_starttag_input(self, attrs):
name = None name = None
for attr in (attrs + attrs): for attr in attrs + attrs:
if len(attr) == 2: if len(attr) == 2:
if isinstance(name, str): if isinstance(name, str):
if attr[0] == "value": if attr[0] == "value":
@ -43,14 +49,18 @@ class MetaParser(html.parser.HTMLParser):
return return
elif attr[0] == "name": elif attr[0] == "name":
name = attr[1] name = attr[1]
def handle_starttag(self, tag, attrs): def handle_starttag(self, tag, attrs):
if tag == "meta": if tag == "meta":
return self.handle_starttag_meta(attrs) return self.handle_starttag_meta(attrs)
elif tag == "input": elif tag == "input":
return self.handle_starttag_input(attrs) return self.handle_starttag_input(attrs)
class StreamData():
def __init__(self, upstream, ctype, proxy_ctype, thumbnail, title, description, override): class StreamData:
def __init__(
self, upstream, ctype, proxy_ctype, thumbnail, title, description, override
):
self.values = {} self.values = {}
self.values["upstream"] = upstream self.values["upstream"] = upstream
self.values["ctype"] = ctype self.values["ctype"] = ctype
@ -59,13 +69,16 @@ class StreamData():
self.values["title"] = title self.values["title"] = title
self.values["description"] = description self.values["description"] = description
self.override = override self.override = override
def update(self, key, value, override): def update(self, key, value, override):
missing = not isinstance(self.values.get(key), str) missing = not isinstance(self.values.get(key), str)
override = override and isinstance(value, str) override = override and isinstance(value, str)
if missing or override: if missing or override:
self.values[key] = value self.values[key] = value
def upstream(self): def upstream(self):
return self.values.get("upstream") return self.values.get("upstream")
def ctype(self): def ctype(self):
ctype = self.values.get("ctype") ctype = self.values.get("ctype")
proxy_ctype = self.values.get("proxy_ctype") proxy_ctype = self.values.get("proxy_ctype")
@ -73,21 +86,28 @@ class StreamData():
if not ctype.startswith("audio/") and not ctype.startswith("video/"): if not ctype.startswith("audio/") and not ctype.startswith("video/"):
return proxy_ctype return proxy_ctype
return ctype return ctype
def proxy_ctype(self): def proxy_ctype(self):
return self.values.get("proxy_ctype") return self.values.get("proxy_ctype")
def thumbnail(self): def thumbnail(self):
return self.values.get("thumbnail") return self.values.get("thumbnail")
def title(self): def title(self):
return self.values.get("title") return self.values.get("title")
def description(self): def description(self):
return self.values.get("description") return self.values.get("description")
def complete(self): def complete(self):
return None not in self.values.values() return None not in self.values.values()
def has_data(self): def has_data(self):
for elem in self.values.values(): for elem in self.values.values():
if isinstance(elem, str): if isinstance(elem, str):
return True return True
return False return False
def meta(self): def meta(self):
data = [] data = []
if isinstance(self.values.get("thumbnail"), str): if isinstance(self.values.get("thumbnail"), str):
@ -98,7 +118,8 @@ class StreamData():
data.append(("og:description", self.values.get("description"))) data.append(("og:description", self.values.get("description")))
return data return data
class StreamProvider():
class StreamProvider:
def __init__(self, upstream, proxy, logger): def __init__(self, upstream, proxy, logger):
self.name = self.__class__.__name__ self.name = self.__class__.__name__
self.upstream = upstream self.upstream = upstream
@ -106,6 +127,7 @@ class StreamProvider():
self.logger = logger self.logger = logger
if isinstance(proxy, config.ProxyElem): if isinstance(proxy, config.ProxyElem):
self.proxy = proxy self.proxy = proxy
def extract_mime(self, upstream): def extract_mime(self, upstream):
try: try:
url = urllib.parse.urlparse(upstream) url = urllib.parse.urlparse(upstream)
@ -121,6 +143,7 @@ class StreamProvider():
return "application/vnd.apple.mpegurl" return "application/vnd.apple.mpegurl"
return mime return mime
return None return None
def init_stream(self): def init_stream(self):
stream = {} stream = {}
stream["upstream"] = None stream["upstream"] = None
@ -131,6 +154,7 @@ class StreamProvider():
stream["description"] = None stream["description"] = None
stream["override"] = False stream["override"] = False
return stream return stream
def process(self): def process(self):
data = self.stream() data = self.stream()
proxy_ctype = data.proxy_ctype() proxy_ctype = data.proxy_ctype()
@ -144,7 +168,9 @@ class StreamProvider():
ctype = None ctype = None
upstream = data.upstream() upstream = data.upstream()
try: try:
resp = requests.head(data.upstream(), proxies=proxies, timeout=5, allow_redirects=True) resp = requests.head(
data.upstream(), proxies=proxies, timeout=5, allow_redirects=True
)
except Exception as e: except Exception as e:
self.logger.info("%s <%s>", e, self.upstream) self.logger.info("%s <%s>", e, self.upstream)
else: else:
@ -155,7 +181,16 @@ class StreamProvider():
ctype = None ctype = None
elif "mpegurl" in ctype: elif "mpegurl" in ctype:
ctype = "application/vnd.apple.mpegurl" ctype = "application/vnd.apple.mpegurl"
return StreamData(data.upstream(), ctype, proxy_ctype, data.thumbnail(), data.title(), data.description(), data.override) return StreamData(
data.upstream(),
ctype,
proxy_ctype,
data.thumbnail(),
data.title(),
data.description(),
data.override,
)
async def run(self): async def run(self):
data = None data = None
try: try:
@ -165,6 +200,7 @@ class StreamProvider():
self.logger.info("%s <%s>", e, self.upstream) self.logger.info("%s <%s>", e, self.upstream)
return data return data
class StreamlinkRunner(StreamProvider): class StreamlinkRunner(StreamProvider):
def stream(self): def stream(self):
try: try:
@ -184,11 +220,20 @@ class StreamlinkRunner(StreamProvider):
for key in reversed(streams): for key in reversed(streams):
stream = streams.get(key) stream = streams.get(key)
if hasattr(stream, "url"): if hasattr(stream, "url"):
return StreamData(stream.url, self.extract_mime(stream.url), None, None, None, None, False) return StreamData(
stream.url,
self.extract_mime(stream.url),
None,
None,
None,
None,
False,
)
except Exception as e: except Exception as e:
self.logger.info("%s <%s>", e, self.upstream) self.logger.info("%s <%s>", e, self.upstream)
return StreamData(None, None, None, None, None, None, False) return StreamData(None, None, None, None, None, None, False)
class YoutubeRunner(StreamProvider): class YoutubeRunner(StreamProvider):
def stream(self): def stream(self):
best_stream = self.init_stream() best_stream = self.init_stream()
@ -216,15 +261,17 @@ class YoutubeRunner(StreamProvider):
best_width = best_format.get("width") best_width = best_format.get("width")
best_height = best_format.get("height") best_height = best_format.get("height")
new_url = vformat.get("url") new_url = vformat.get("url")
if (isinstance(best_width, int) and if (
isinstance(best_height, int) and isinstance(best_width, int)
isinstance(current_width, int) and and isinstance(best_height, int)
isinstance(current_height, int) and and isinstance(current_width, int)
isinstance(new_url, str) and and isinstance(current_height, int)
current_width > best_width and and isinstance(new_url, str)
current_height > best_height and and current_width > best_width
acodec != "none" and and current_height > best_height
vcodec != "none"): and acodec != "none"
and vcodec != "none"
):
best_format = vformat best_format = vformat
best_stream["override"] = True best_stream["override"] = True
best_stream["upstream"] = new_url best_stream["upstream"] = new_url
@ -233,11 +280,14 @@ class YoutubeRunner(StreamProvider):
self.logger.info("%s <%s>", e, self.upstream) self.logger.info("%s <%s>", e, self.upstream)
return StreamData(**best_stream) return StreamData(**best_stream)
class SeafileRunner(StreamProvider): class SeafileRunner(StreamProvider):
def stream(self): def stream(self):
stream_data = self.init_stream() stream_data = self.init_stream()
json_data = None json_data = None
proc = subprocess.run(["/app/seafile.js", self.upstream], capture_output=True, encoding="utf-8") proc = subprocess.run(
["/app/seafile.js", self.upstream], capture_output=True, encoding="utf-8"
)
try: try:
json_data = json.loads(proc.stdout) json_data = json.loads(proc.stdout)
except Exception as e: except Exception as e:
@ -249,6 +299,7 @@ class SeafileRunner(StreamProvider):
stream_data["proxy_ctype"] = "video/mp4" stream_data["proxy_ctype"] = "video/mp4"
return StreamData(**stream_data) return StreamData(**stream_data)
class MetaProvider(StreamProvider): class MetaProvider(StreamProvider):
def parse_web(self): def parse_web(self):
stream_data = self.init_stream() stream_data = self.init_stream()
@ -268,6 +319,7 @@ class MetaProvider(StreamProvider):
stream_data["description"] = data.get("og:description") stream_data["description"] = data.get("og:description")
return stream_data return stream_data
class MetaRunner(MetaProvider): class MetaRunner(MetaProvider):
def stream(self): def stream(self):
stream_data = self.parse_web() stream_data = self.parse_web()
@ -275,14 +327,17 @@ class MetaRunner(MetaProvider):
stream_data["ctype"] = None stream_data["ctype"] = None
return StreamData(**stream_data) return StreamData(**stream_data)
class NextcloudRunner(MetaProvider): class NextcloudRunner(MetaProvider):
def stream(self): def stream(self):
stream_data = self.parse_web() stream_data = self.parse_web()
stream_data["thumbnail"] = None stream_data["thumbnail"] = None
return StreamData(**stream_data) return StreamData(**stream_data)
upstream_cache = expiringdict.ExpiringDict(max_len=512, max_age_seconds=18000) upstream_cache = expiringdict.ExpiringDict(max_len=512, max_age_seconds=18000)
async def get_from_runner(cache_key, runner, logger): async def get_from_runner(cache_key, runner, logger):
result = None result = None
cached = upstream_cache.get(cache_key) cached = upstream_cache.get(cache_key)
@ -298,20 +353,36 @@ async def get_from_runner(cache_key, runner, logger):
result = result_temp result = result_temp
return result return result
async def get_streamlink(upstream, proxy, logger): async def get_streamlink(upstream, proxy, logger):
return await get_from_runner((0, upstream), StreamlinkRunner(upstream, proxy, logger), logger) return await get_from_runner(
(0, upstream), StreamlinkRunner(upstream, proxy, logger), logger
)
async def get_ytdl(upstream, proxy, logger): async def get_ytdl(upstream, proxy, logger):
return await get_from_runner((1, upstream), YoutubeRunner(upstream, proxy, logger), logger) return await get_from_runner(
(1, upstream), YoutubeRunner(upstream, proxy, logger), logger
)
async def get_meta(upstream, proxy, logger): async def get_meta(upstream, proxy, logger):
return await get_from_runner((2, upstream), MetaRunner(upstream, proxy, logger), logger) return await get_from_runner(
(2, upstream), MetaRunner(upstream, proxy, logger), logger
)
async def get_nextcloud(upstream, proxy, logger): async def get_nextcloud(upstream, proxy, logger):
return await get_from_runner((3, upstream), NextcloudRunner(upstream, proxy, logger), logger) return await get_from_runner(
(3, upstream), NextcloudRunner(upstream, proxy, logger), logger
)
async def get_seafile(upstream, proxy, logger): async def get_seafile(upstream, proxy, logger):
return await get_from_runner((3, upstream), SeafileRunner(upstream, proxy, logger), logger) return await get_from_runner(
(3, upstream), SeafileRunner(upstream, proxy, logger), logger
)
async def get_any(upstream, proxy, logger): async def get_any(upstream, proxy, logger):
cache_key = (4, upstream) cache_key = (4, upstream)
@ -331,7 +402,9 @@ async def get_any(upstream, proxy, logger):
result.update("ctype", temp_result.ctype(), temp_result.override) result.update("ctype", temp_result.ctype(), temp_result.override)
result.update("thumbnail", temp_result.thumbnail(), temp_result.override) result.update("thumbnail", temp_result.thumbnail(), temp_result.override)
result.update("title", temp_result.title(), temp_result.override) result.update("title", temp_result.title(), temp_result.override)
result.update("description", temp_result.description(), temp_result.override) result.update(
"description", temp_result.description(), temp_result.override
)
if result.complete(): if result.complete():
upstream_cache[cache_key] = result upstream_cache[cache_key] = result
break break