format with black
This commit is contained in:
		@@ -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:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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()
 | 
				
			||||||
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user