From 8f160e7a7a01642898fd006fc9a6841b3aca1f85 Mon Sep 17 00:00:00 2001 From: jin Date: Wed, 18 Jan 2023 12:24:37 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E8=A1=A5=E5=85=85/proxy=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 补充/proxy接口,如果获取的是不含域名的m3u8列表,则拼出完整地址;如果获取的是m3u8列表,则继续进行代理获取 --- app/common/cache_tools.py | 11 +++++++++-- app/plugins/proxy/tools.py | 4 ++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/app/common/cache_tools.py b/app/common/cache_tools.py index fca6c77..a72536f 100644 --- a/app/common/cache_tools.py +++ b/app/common/cache_tools.py @@ -20,12 +20,19 @@ async def processing(url, data): for _temp in data: if ".ts" in _temp: - if not is_url(_temp): + if is_url(_temp): yield "/file.ts?x=" + b64encode(_temp.encode("utf-8")).decode("utf-8") else: yield "/file.ts/?x=" + b64encode(urljoin(url, _temp).encode("utf-8")).decode("utf-8") else: - yield _temp + if _temp.startswith("#"): + yield _temp + # 如果获取的是m3u8列表,则继续进行代理获取 + elif is_url(_temp): + print(is_url(_temp),_temp) + yield "/proxy.m3u8?url=" + _temp + else: + yield "/proxy.m3u8?url=" + urljoin(url, _temp) yield "\n" diff --git a/app/plugins/proxy/tools.py b/app/plugins/proxy/tools.py index df8178f..881a819 100644 --- a/app/plugins/proxy/tools.py +++ b/app/plugins/proxy/tools.py @@ -52,9 +52,9 @@ def md5(s): def is_url(url): regex = re.compile(config.url_regex) if regex.match(url): - return False - else: return True + else: + return False def parse(url): From 889de22ac815d7049d808f61c767d432f08ef1b3 Mon Sep 17 00:00:00 2001 From: jin Date: Wed, 18 Jan 2023 13:06:04 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E5=A2=9E=E5=8A=A0Youtube=E4=BB=A3=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增加Youtube代理接口/ytb.m3u8 --- app/api/v2/endpoints/proxy.py | 33 ++++++++++++++++++++++++++- app/common/cache_tools.py | 43 ++++++++++++++++++++++++++++++++--- app/conf/config.py | 3 +++ 3 files changed, 75 insertions(+), 4 deletions(-) diff --git a/app/api/v2/endpoints/proxy.py b/app/api/v2/endpoints/proxy.py index 9cf1f57..4379825 100644 --- a/app/api/v2/endpoints/proxy.py +++ b/app/api/v2/endpoints/proxy.py @@ -63,4 +63,35 @@ async def Stream_Proxy_Pro(request: Request): """ ## 接口文档 """ - return RedirectResponse("http://proxy.naihe.me/docs") \ No newline at end of file + return RedirectResponse("http://proxy.naihe.me/docs") + + +@tv.get('/ytb.m3u8') +async def ytb(request: Request, + url: str = Query(..., regex="^[0-9A-Za-z]{11}$")): + """ + ## 用途/Usage + - 代理youtube直播链接 + - 输入参数为11位ID,如`https://www.youtube.com/watch?v=abcdEFg12H&ab_channel=SPACE%28Official%29`,ID是`abcdEFg12H` + """ + url = dict(request.query_params) + if url.get("url"): + stream_id = parse(url) + stream_url = get_ytb(stream_id) + _data = get_m3u8_down(stream_url) + return StreamingResponse(processing(stream_url, iter(_data.split("\n"))), 200, headers=headers2) + +@tv.get('/ytbproxy', summary="代理youtube m3u8") +async def ytbproxy(request: Request, url: str): + """ + 代理youtube链接 + """ + url = dict(request.query_params) + if url.get("url"): + url = url.get("url") + url = b64decode(url.encode("utf-8")).decode("utf-8") + url = url.replace('=','%3D') + url = url.replace(';','%3B') + + _data = get_m3u8_down(url) + return StreamingResponse(processing(url, iter(_data.split("\n"))), 200, headers=headers2) \ No newline at end of file diff --git a/app/common/cache_tools.py b/app/common/cache_tools.py index a72536f..9605488 100644 --- a/app/common/cache_tools.py +++ b/app/common/cache_tools.py @@ -25,12 +25,14 @@ async def processing(url, data): else: yield "/file.ts/?x=" + b64encode(urljoin(url, _temp).encode("utf-8")).decode("utf-8") else: - if _temp.startswith("#"): + if _temp.startswith("#") or len(_temp)==0: yield _temp # 如果获取的是m3u8列表,则继续进行代理获取 elif is_url(_temp): - print(is_url(_temp),_temp) - yield "/proxy.m3u8?url=" + _temp + if "googlevideo.com" in _temp: + yield "/ytbproxy?url="+b64encode(_temp.encode("utf-8")).decode("utf-8") + else: + yield "/proxy.m3u8?url=" + _temp else: yield "/proxy.m3u8?url=" + urljoin(url, _temp) yield "\n" @@ -55,3 +57,38 @@ def get_m3u8_down(url): with requests.get(url=url, headers=headers) as res: _data = res.text return _data + + +def get_ytb(stream_id): + ytburl = f"https://www.youtube.com/watch?v={stream_id}" + if ytb_stream.get(stream_id) is not None: + if ytb_stream[stream_id]["expire"] > time.time()-60: + logger.success(f"从缓存加载直播源 {stream_id}") + else: + logger.info(f"youtube直播源已过期 {stream_id}") + update_ytb(ytburl,stream_id) + else: + logger.info(f"youtube直播源不存在 {stream_id}") + update_ytb(ytburl,stream_id) + stream_url = ytb_stream[stream_id]['stream_url'] + return stream_url + + +# 获取YouTube直播源 +def update_ytb(url,stream_id): + headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:104.0) Gecko/20100101 Firefox/104.0" + } + print(url) + response = requests.get(url, headers=headers).text + + #20230115有效,直接获取链接 + pattern = re.search(r'hlsManifestUrl.+?(https://.+?m3u8)',response) + stream_url = pattern.group(1) + pattern2 = re.search(r'expire/(\d{10})',stream_url) + expire = int(pattern2.group(1)) + + ytb_stream[stream_id] = {"expire":expire,"url":url,"stream_url":stream_url} + logger.success(f"youtube直播源已更新 {ytb_stream[stream_id]}") + logger.success(f"总数 {len(ytb_stream.keys())}") + \ No newline at end of file diff --git a/app/conf/config.py b/app/conf/config.py index 9a4675f..60a977b 100644 --- a/app/conf/config.py +++ b/app/conf/config.py @@ -99,5 +99,8 @@ class Config(BaseSettings): ts_info = {} gdata = None + +ytb_stream = {} + logger.info("配置加载完成")