├── .github └── FUNDING.yml ├── README.md ├── app ├── __init__.py ├── __pycache__ │ └── __init__.cpython-310.pyc ├── api │ ├── __init__.py │ ├── __pycache__ │ │ └── __init__.cpython-310.pyc │ └── v2 │ │ ├── __init__.py │ │ ├── __pycache__ │ │ └── __init__.cpython-310.pyc │ │ └── endpoints │ │ ├── __init__.py │ │ ├── __pycache__ │ │ ├── __init__.cpython-310.pyc │ │ └── proxy.cpython-310.pyc │ │ └── proxy.py ├── assets │ ├── channel.m3u │ └── config.ini ├── common │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-310.pyc │ │ ├── cache_tools.cpython-310.pyc │ │ └── request.cpython-310.pyc │ ├── cache_tools.py │ └── request.py ├── conf │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-310.pyc │ │ └── config.cpython-310.pyc │ └── config.py ├── db │ ├── DBtools.py │ ├── __init__.py │ ├── __pycache__ │ │ ├── DBtools.cpython-310.pyc │ │ ├── __init__.cpython-310.pyc │ │ ├── dbMysql.cpython-310.pyc │ │ └── localfile.cpython-310.pyc │ ├── dbMysql.py │ └── localfile.py ├── main.py ├── plugins │ ├── __init__.py │ ├── __pycache__ │ │ └── __init__.cpython-310.pyc │ └── proxy │ │ ├── __init__.py │ │ ├── __pycache__ │ │ ├── __init__.cpython-310.pyc │ │ ├── endecrypt.cpython-310.pyc │ │ ├── tasks.cpython-310.pyc │ │ ├── tools.cpython-310.pyc │ │ └── utile.cpython-310.pyc │ │ ├── endecrypt.py │ │ ├── tasks.py │ │ ├── tools.py │ │ └── utile.py └── scheams │ ├── __init__.py │ ├── __pycache__ │ ├── __init__.cpython-310.pyc │ ├── api_model.cpython-310.pyc │ └── response.cpython-310.pyc │ ├── api_model.py │ └── response.py ├── img └── demo.gif ├── main.py └── requirements.txt /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | custom: ["https://ik.imagekit.io/naihe/pay/hbm.jpg"] 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | [ProxyURL](https://github.com/239144498/ProxyURL) 4 | ------------- 5 | [![Python version](https://img.shields.io/badge/python->=3.8-green.svg?style=plastic&logo=python)](https://www.python.org/downloads/release/python-380/) 6 | [![GitHub stars](https://img.shields.io/github/stars/239144498/ProxyURL?color=brightgreen&style=plastic&logo=Apache%20Spark)](https://github.com/239144498/ProxyURL/stargazers) 7 | [![MIT license](https://img.shields.io/badge/license-GNU3.0-green.svg?style=plastic&logo=React%20Hook%20Form)](https://github.com/239144498/ProxyURL/blob/main/LICENSE) 8 | 9 |
10 | 11 | - 程序使用教程已发布在`微信公众号【pqhero】回复【使用教程】`,可打开微信访问:[https://0a.fit/obLUQ](https://0a.fit/obLUQ),或者扫码查看 12 | 13 | qrcode.png 14 | 15 | 16 | 程序介绍 17 | --- 18 | > 🚨如需使用私有服务器运行本项目,请参考部署方式[部署教程](./README.md#python%E9%83%A8%E7%BD%B2linux) 19 | > 20 | **ProxyURL**是一个从 [Streaming-Media-Server-Pro](https://github.com/239144498/Streaming-Media-Server-Pro) 项目中抽离出的流媒体代理程序。程序新增缓存功能,多人同时观看效果更流畅。 21 | 理论支持代理任意频道,代理后可解决**频道卡顿**问题、网页播放的**跨域**问题、**封锁地区**问题等等。更多使用方式等你去发现。 22 | 23 | 代理接口演示 24 | --- 25 | _[Click to view](https://ik.imagekit.io/naihe/github/demo.gif)_ 26 | ![loading...](https://github.com/239144498/ProxyURL/raw/main/img/demo.gif) 27 | 28 | 29 | 演示站点: 我很脆弱...请勿压测(·•᷄ࡇ•᷅ ) 30 | --- 31 | API Document: https://proxy.naihe.me/docs 32 | 为防止滥用,本接口已经加入了鉴权机制。 33 | **未来将添加代理4k频道接口** 34 | 35 | 程序功能 36 | --- 37 | - 代理任意电视频道的视频流 38 | - 代理缓存播放 39 | - 代理生成m3u文件 40 | - 代理生成m3u8文件 41 | - 异步下载流 42 | - 流媒体转发 43 | - 支持短回放 44 | - 自定义频道列表 45 | 46 | 支持代理的链接格式 47 | --- 48 | > 💡提示:包含但不仅限于以下例子,如果遇到链接解析失败请开启一个新 [issue](https://github.com/239144498/ProxyURL/issues) 49 | 50 | 程序有检测url链接格式功能,一般m3u8链接分为以下两种: 51 | 52 | - 含有域名链接的数据 53 | ``` 54 | #EXTM3U 55 | #EXT-X-VERSION:3 56 | #EXT-X-TARGETDURATION:10 57 | #EXT-X-MEDIA-SEQUENCE:1668994593 58 | #EXTINF:10.0,5290508 59 | http://117.169.xxx.xxx:8080/wh7f454c46tw3004853380_1728812826/live/cctv-4/HD-4000k-1080P-cctv4_20230117_205041_205051.ts 60 | #EXTINF:10.0,5306864 61 | http://117.169.xxx.xxx:8080/wh7f454c46tw3004853380_1728812826/live/cctv-4/HD-4000k-1080P-cctv4_20230117_205051_205101.ts 62 | #EXTINF:10.0,5271708 63 | http://117.169.xxx.xxx:8080/wh7f454c46tw3004853380_1728812826/live/cctv-4/HD-4000k-1080P-cctv4_20230117_205101_205111.ts 64 | ... 65 | ... 66 | ``` 67 | - 无域名链接的数据 68 | 69 | ``` 70 | #EXTM3U 71 | #EXT-X-VERSION:3 72 | #EXT-X-MEDIA-SEQUENCE:167395846 73 | #EXT-X-TARGETDURATION:10 74 | #EXTINF:10.200, 75 | /cntv20210928/cctvwbnd/cctv1_2_md/167395846.ts?region=beijing 76 | #EXTINF:9.800, 77 | /cntv20210928/cctvwbnd/cctv1_2_md/167395847.ts?region=beijing 78 | #EXTINF:10.200, 79 | /cntv20210928/cctvwbnd/cctv1_2_md/167395848.ts?region=beijing 80 | #EXTINF:9.800, 81 | /cntv20210928/cctvwbnd/cctv1_2_md/167395849.ts?region=beijing 82 | ``` 83 | 这两种格式已涵盖市面上大部分m3u8链接。 84 | 85 | 86 | Python部署(Linux) 87 | --- 88 | > 💡提示:最好将本项目部署至美国地区的服务器,否则可能会出现奇怪的BUG。 89 | 90 | 推荐大家使用[Digitalocean](https://www.digitalocean.com/?refcode=45e25f5e4569&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge)的服务器,主要是因为**免费**。 91 | 92 | DigitalOcean Referral Badge 93 | 94 | 使用我的邀请链接注册,你可以获得$200的credit,当你在上面消费$25时,我也可以获得$25的奖励。 95 | 96 | 我的邀请链接: 97 | 98 | [https://m.do.co/c/45e25f5e4569](https://m.do.co/c/45e25f5e4569) 99 | > 根据以下通用命令部署本项目 100 | ### clone仓库 101 | 102 | python版本>=3.8+ 103 | 104 | ``` code 105 | git clone https://github.com/239144498/ProxyURL.git 106 | ``` 107 | 108 | ### 安装依赖 109 | 110 | ``` code 111 | pip install -r requirements.txt 112 | ``` 113 | 114 | ### 运行 115 | 116 | ``` code 117 | python3 main.py 118 | ``` 119 | 120 | 项目使用教程 121 | --- 122 | 关注公众号【pqhero】查看程序使用详情 123 | qrcode.png 124 | 125 | 126 | 支持频道 127 | --- 128 | 129 | - [x] 在channel.m3u文件添加代理频道 130 | 131 | License 132 | --- 133 | [GNU-3.0 © naihe](https://github.com/239144498/ProxyURL/blob/main/LICENSE) 134 | 135 | -------------------------------------------------------------------------------- /app/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2022/10/8 3 | # @Author : Naihe 4 | # @Email : 239144498@qq.com 5 | # @File : __init__.py.py 6 | # @Software: PyCharm 7 | -------------------------------------------------------------------------------- /app/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfcx/ProxyURL/43d3b6435297f180209b8d4f4fb99ca729e5bb8b/app/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /app/api/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2022/10/8 3 | # @Author : Naihe 4 | # @Email : 239144498@qq.com 5 | # @File : __init__.py 6 | # @Software: PyCharm 7 | from loguru import * 8 | import sys 9 | from fastapi import * 10 | from apscheduler.executors.pool import * 11 | import logging 12 | import base64 13 | from apscheduler.schedulers.background import * 14 | from apscheduler.triggers.cron import * 15 | from app.conf import * 16 | from app.plugins.proxy.tasks import * 17 | from .v2 import * 18 | from ..conf.config import * 19 | from ..scheams.response import * 20 | 21 | 22 | def init_app(): 23 | app = FastAPI( 24 | title=config.TITLE, 25 | description=config.DESC, 26 | version=config.VERSION, 27 | debug=DEBUG 28 | ) 29 | return app 30 | 31 | 32 | app = init_app() 33 | app.include_router(v2) 34 | 35 | 36 | @app.get('/') 37 | async def Root_Path(): 38 | data = { 39 | "API_status": "Running", 40 | "Version": config.VERSION, 41 | "Web_APP": "https://proxy.naihe.me/", 42 | "API_Document": "https://proxy.naihe.me/docs", 43 | "GitHub": "https://github.com/239144498/proxyURL", 44 | } 45 | return JSONResponse(data) 46 | 47 | 48 | @app.on_event("startup") 49 | async def startup(): 50 | pass 51 | 52 | 53 | @app.on_event("shutdown") 54 | async def shutdown(): 55 | pass 56 | -------------------------------------------------------------------------------- /app/api/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfcx/ProxyURL/43d3b6435297f180209b8d4f4fb99ca729e5bb8b/app/api/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /app/api/v2/__init__.py: -------------------------------------------------------------------------------- 1 | from fastapi import * 2 | from .endpoints import * 3 | 4 | v2 = APIRouter() 5 | 6 | v2.include_router(tv) 7 | -------------------------------------------------------------------------------- /app/api/v2/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfcx/ProxyURL/43d3b6435297f180209b8d4f4fb99ca729e5bb8b/app/api/v2/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /app/api/v2/endpoints/__init__.py: -------------------------------------------------------------------------------- 1 | from .proxy import * 2 | -------------------------------------------------------------------------------- /app/api/v2/endpoints/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfcx/ProxyURL/43d3b6435297f180209b8d4f4fb99ca729e5bb8b/app/api/v2/endpoints/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /app/api/v2/endpoints/__pycache__/proxy.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfcx/ProxyURL/43d3b6435297f180209b8d4f4fb99ca729e5bb8b/app/api/v2/endpoints/__pycache__/proxy.cpython-310.pyc -------------------------------------------------------------------------------- /app/api/v2/endpoints/proxy.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2022/10/8 3 | # @Author : Naihe 4 | # @Email : 239144498@qq.com 5 | # @File : proxy.py 6 | # @Software: PyCharm 7 | import asyncio 8 | from base64 import * 9 | from threading import * 10 | from loguru import * 11 | from urllib.parse import * 12 | from fastapi.requests import * 13 | from fastapi import * 14 | from fastapi.background import * 15 | from starlette.responses import * 16 | from app.common.cache_tools import * 17 | from app.plugins.proxy.tools import * 18 | from app.plugins.proxy.utile import * 19 | from app.conf.config import * 20 | from app.scheams.response import * 21 | 22 | 23 | tv = APIRouter(tags=["流媒体代理"]) 24 | 25 | 26 | @tv.get('/proxy.m3u8') 27 | async def proxy(request: Request, 28 | url: str = Query(..., regex=config.url_regex)): 29 | """ 30 | ## 用途/Usage 31 | - 代理任意链接 32 | """ 33 | url = dict(request.query_params) 34 | if url.get("url"): 35 | url = parse(url) 36 | _data = get_m3u8_down(url) 37 | return StreamingResponse(processing(url, iter(_data.split("\n"))), 200, headers=headers2) 38 | 39 | 40 | @tv.get('/file.ts') 41 | async def file(x: str = Query(...)): 42 | """ 43 | ## 用途/Usage 44 | - 下载流视频片 45 | """ 46 | a = b64decode(x.encode("utf-8")).decode("utf-8") 47 | return Response(content=download(a), status_code=200, headers=headers, media_type='video/MP2T') 48 | 49 | 50 | @tv.get('/program.m3u') 51 | async def program_proxy(): 52 | """ 53 | ## 用途/Usage 54 | - 代理频道列表 55 | """ 56 | with open("app/assets/channel.m3u", "r", encoding="utf-8") as f: 57 | data = f.read() 58 | return Response(content=data, status_code=200) 59 | 60 | 61 | @tv.get('/about') 62 | async def Stream_Proxy_Pro(request: Request): 63 | """ 64 | ## 接口文档 65 | """ 66 | return RedirectResponse("http://proxy.naihe.me/docs") -------------------------------------------------------------------------------- /app/assets/channel.m3u: -------------------------------------------------------------------------------- 1 | #EXTM3U 2 | # 代理频道URL示例,请自行替换 3 | 4 | #EXTINF:-1,tvg-id="CCTV1" tvg-name="CCTV1" tvg-logo="https://epg.112114.xyz/logo/CCTV1.png" group-title="央视",CCTV1 5 | /proxy.m3u8?url=https://cctvwbndks.v.kcdnvip.com/cctvwbnd/cctv1_2/index.m3u8?BR=md®ion=shanghai 6 | #EXTINF:-1,卢村远眺 7 | /proxy.m3u8?url=https://gccncc.xxxx.com/gc/yxlcyt_1/index.m3u8?contentid=2820180516001 8 | #EXTINF:-1,九寨沟 9 | /proxy.m3u8?url=https://gctxyc.xxxx.com/gc/wygjt1_1/index.m3u8?contentid=2820180516001 10 | #EXTINF:-1,熊大熊二 11 | /proxy.m3u8?url=https://newcntv.xxxx.com:8080/asp/hls/1200/0303000a/3/default/1733da751de64e6e910abda889d87a26/1200.m3u8 12 | -------------------------------------------------------------------------------- /app/assets/config.ini: -------------------------------------------------------------------------------- 1 | [default] 2 | defaultdb = 3 | port = 8080 4 | localhost = 5 | vbuffer = 3 6 | active_mode = True 7 | 8 | [advanced] 9 | host1 = 10 | host2 = 11 | tvglogo = 12 | debug = False 13 | 14 | [mysql] 15 | host = 16 | user = 17 | password = 18 | port = 19 | database = 20 | 21 | [redis] 22 | host = 23 | port = 24 | password = 25 | -------------------------------------------------------------------------------- /app/common/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2022/10/8 3 | # @Author : Naihe 4 | # @Email : 239144498@qq.com 5 | # @File : __init__.py 6 | # @Software: PyCharm 7 | -------------------------------------------------------------------------------- /app/common/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfcx/ProxyURL/43d3b6435297f180209b8d4f4fb99ca729e5bb8b/app/common/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /app/common/__pycache__/cache_tools.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfcx/ProxyURL/43d3b6435297f180209b8d4f4fb99ca729e5bb8b/app/common/__pycache__/cache_tools.cpython-310.pyc -------------------------------------------------------------------------------- /app/common/__pycache__/request.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfcx/ProxyURL/43d3b6435297f180209b8d4f4fb99ca729e5bb8b/app/common/__pycache__/request.cpython-310.pyc -------------------------------------------------------------------------------- /app/common/cache_tools.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2023/1/12 3 | # @Author : Naihe 4 | # @Email : 239144498@qq.com 5 | # @File : cache_tools.py 6 | # @Software: PyCharm 7 | import time 8 | from base64 import b64encode 9 | import aiohttp 10 | import itertools 11 | import requests 12 | from loguru import * 13 | from threading import * 14 | from collections import * 15 | from urllib.parse import * 16 | from app.conf.config import * 17 | from app.plugins.proxy.tools import * 18 | 19 | 20 | async def processing(url, data): 21 | for _temp in data: 22 | if ".ts" in _temp: 23 | if not is_url(_temp): 24 | yield "/file.ts?x=" + b64encode(_temp.encode("utf-8")).decode("utf-8") 25 | else: 26 | yield "/file.ts/?x=" + b64encode(urljoin(url, _temp).encode("utf-8")).decode("utf-8") 27 | else: 28 | yield _temp 29 | yield "\n" 30 | 31 | 32 | def download(url): 33 | if _temp_ts := ts_info.get(url): 34 | return _temp_ts 35 | header = { 36 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:103.0) Gecko/20100101 Firefox/103.0", 37 | } 38 | with requests.get(url=url, headers=header) as res: 39 | _data = res.content 40 | ts_info[url] = _data 41 | return _data 42 | 43 | 44 | def get_m3u8_down(url): 45 | headers = { 46 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:104.0) Gecko/20100101 Firefox/104.0" 47 | } 48 | with requests.get(url=url, headers=headers) as res: 49 | _data = res.text 50 | return _data 51 | -------------------------------------------------------------------------------- /app/common/request.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin python3 2 | # -*- coding: utf-8 -*- 3 | # @Author: Naihe 4 | # @Email: 239144498@qq.com 5 | # @Software: Streaming-Media-Server-Pro 6 | import hashlib 7 | import os 8 | import requests 9 | 10 | from app.conf.config import * 11 | 12 | requests.packages.urllib3.disable_warnings() 13 | 14 | 15 | class netreq(object): 16 | """ 17 | 对网络请求进行封装,增加了代理功能 18 | """ 19 | 20 | def __init__(self, proxies=None): 21 | self.request = requests 22 | self.proxies = None 23 | 24 | def session(self): 25 | return requests.session() 26 | 27 | def get(self, url, headers=None): 28 | return self.request.get(url, headers=headers, proxies=self.proxies) 29 | 30 | def post(self, url, data=None, json=None, headers=None): 31 | return self.request.post(url, data=data, json=json, headers=headers, proxies=self.proxies) 32 | 33 | def put(self, url, data=None, json=None, headers=None): 34 | return self.request.put(url, data=data, json=json, headers=headers, proxies=self.proxies) 35 | 36 | def delete(self, url, data=None, json=None, headers=None): 37 | return self.request.delete(url, data=data, json=json, headers=headers, proxies=self.proxies) 38 | 39 | 40 | request = netreq() 41 | 42 | -------------------------------------------------------------------------------- /app/conf/__init__.py: -------------------------------------------------------------------------------- 1 | from .config import * 2 | -------------------------------------------------------------------------------- /app/conf/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfcx/ProxyURL/43d3b6435297f180209b8d4f4fb99ca729e5bb8b/app/conf/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /app/conf/__pycache__/config.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfcx/ProxyURL/43d3b6435297f180209b8d4f4fb99ca729e5bb8b/app/conf/__pycache__/config.cpython-310.pyc -------------------------------------------------------------------------------- /app/conf/config.py: -------------------------------------------------------------------------------- 1 | import os 2 | import uuid 3 | import hashlib 4 | import requests 5 | from pathlib import * 6 | from loguru import * 7 | from typing import * 8 | from configparser import * 9 | from pydantic import * 10 | from platform import * 11 | 12 | 13 | class Config(BaseSettings): 14 | TITLE: Optional[str] = "ProxyURL" 15 | 16 | DESC: Optional[str] = """ 17 | #### Description/说明 18 |
19 | 点击展开/Click to expand 20 | - 频道源代理后播放更稳定,更多功能正在开发中。 21 | - 如果需要更多接口,请查看[https://proxy.naihe.me/docs](https://proxy.naihe.me/docs)。 22 | - 本项目开源在[GitHub:ProxyURL](https://github.com/239144498/ProxyURL)。 23 | - 本项目不提供IPTV频道源,请自行搜索。 24 | - 如遇到问题或BUG或建议请在[issues](https://github.com/239144498/ProxyURL/issues)中反馈。 25 | - 本项目仅供学习交流使用,严禁用于违法用途,如有侵权请联系作者。 26 |
27 | #### Contact author/联系作者 28 |
29 | 点击展开/Click to expand 30 | - WeChat: onaihe 31 | - Email: [239144498@qq.com](mailto:239144498@qq.com) 32 | - Github: [https://github.com/239144498](https://github.com/239144498) 33 |
34 | """ 35 | 36 | VERSION = "1.0" 37 | 38 | ORIGINS = [ 39 | "*" 40 | ] 41 | 42 | ROOT = Path() 43 | 44 | LOG_DIR = ROOT / "log" 45 | 46 | datadir = ROOT / 'vtemp' 47 | 48 | count = 0 49 | 50 | url_regex = r"(http|https)://((?:[\w-]+\.)+[a-z0-9]+)((?:\/[^/?#]*)+)?(\?[^#]+)?(#.+)?" 51 | 52 | 53 | logger.info("配置加载中...") 54 | config = Config() 55 | 56 | request = requests.session() 57 | 58 | try: 59 | cfg = ConfigParser() 60 | cfg.read(config.ROOT / "assets/config.ini", encoding="utf-8") 61 | redis_cfg = dict(cfg.items("redis")) 62 | mysql_cfg = dict(cfg.items("mysql")) 63 | default_cfg = dict(cfg.items("default")) 64 | advanced_cfg = dict(cfg.items("advanced")) 65 | PORT = int(os.getenv("PORT", default=default_cfg.get("port"))) 66 | 67 | authkey = default_cfg.get("authkey") 68 | vbuffer = int(default_cfg.get("vbuffer")) 69 | localhost = os.environ.get("localhost") or default_cfg.get("localhost") 70 | defaultdb = default_cfg.get("defaultdb") 71 | active_mode = eval(default_cfg.get("active_mode", "False")) 72 | 73 | host1 = advanced_cfg.get("host1") 74 | host2 = advanced_cfg.get("host2") 75 | tvglogo = advanced_cfg.get("tvglogo") 76 | DEBUG = eval(os.getenv("DEBUG", default=advanced_cfg.get("debug", "False"))) 77 | except: 78 | DEBUG = True 79 | PORT = 8080 80 | mac = uuid.UUID(int=uuid.getnode()).hex 81 | mdata = hashlib.md5(config.VERSION.encode()) 82 | 83 | headers = { 84 | 'Content-Type': 'video/MP2T', 85 | 'Cache-Control': 'max-age=600', 86 | 'Accept-Ranges': 'bytes' 87 | } 88 | headers2 = { 89 | 'Cache-Control': 'no-cache', 90 | 'Pragma': 'no-cache', 91 | 'Content-Type': 'application/vnd.apple.mpegurl', 92 | 'Expires': '-1', 93 | } 94 | try: 95 | tx = 1 96 | except Exception as e: 97 | tx = 0 98 | 99 | ts_info = {} 100 | 101 | gdata = None 102 | logger.info("配置加载完成") 103 | 104 | -------------------------------------------------------------------------------- /app/db/DBtools.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin python3 2 | # -*- coding: utf-8 -*- 3 | # @Time : 2022/10/7 4 | # @Author : Naihe 5 | # @Email : 239144498@qq.com 6 | # @File : dbMysql.py 7 | # @Software: PyCharm 8 | import pymysql 9 | import redis 10 | from loguru import * 11 | 12 | from app.conf.config import * 13 | from app.db.dbMysql import * 14 | 15 | def mysql_connect_test(): 16 | try: 17 | logger.success("mysql") 18 | return DBconnect 19 | except pymysql.err.OperationalError as e: 20 | logger.error("mysql") 21 | return DBconnect 22 | 23 | 24 | if defaultdb == "mysql": 25 | try: 26 | logger.success("已创建") 27 | except pymysql.err.OperationalError as e: 28 | logger.error("失败") 29 | DBconnect, sqlState = mysql_connect_test() 30 | else: 31 | DBconnect = None 32 | sqlState = False 33 | logger.warning("mysql") -------------------------------------------------------------------------------- /app/db/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2022/10/7 3 | # @Author : Naihe 4 | # @Email : 239144498@qq.com 5 | # @File : __init__.py 6 | # @Software: PyCharm 7 | -------------------------------------------------------------------------------- /app/db/__pycache__/DBtools.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfcx/ProxyURL/43d3b6435297f180209b8d4f4fb99ca729e5bb8b/app/db/__pycache__/DBtools.cpython-310.pyc -------------------------------------------------------------------------------- /app/db/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfcx/ProxyURL/43d3b6435297f180209b8d4f4fb99ca729e5bb8b/app/db/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /app/db/__pycache__/dbMysql.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfcx/ProxyURL/43d3b6435297f180209b8d4f4fb99ca729e5bb8b/app/db/__pycache__/dbMysql.cpython-310.pyc -------------------------------------------------------------------------------- /app/db/__pycache__/localfile.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfcx/ProxyURL/43d3b6435297f180209b8d4f4fb99ca729e5bb8b/app/db/__pycache__/localfile.cpython-310.pyc -------------------------------------------------------------------------------- /app/db/dbMysql.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin python3 2 | 3 | import pymysql 4 | import contextlib 5 | 6 | from loguru import * 7 | from pymysql.cursors import * 8 | 9 | from app.conf.config import * 10 | 11 | 12 | class MySQLConnect(object): 13 | def __init__(self, cursorclass=DictCursor, config=None): 14 | self.MYSQL_config = config 15 | self.cursorclass = cursorclass 16 | self.connection = pymysql.connect( 17 | host=config['host'], 18 | port=config['port'], 19 | user=config['user'], 20 | password=config['password'], 21 | db=config['database'], 22 | cursorclass=cursorclass, 23 | charset=config['charset'], 24 | ) 25 | 26 | @contextlib.contextmanager 27 | def cursor(self, cursor=None): 28 | cursor = self.connection.cursor(cursor) 29 | try: 30 | yield cursor 31 | except Exception as err: 32 | self.connection.rollback() 33 | raise err 34 | finally: 35 | cursor.close() 36 | 37 | def close(self): 38 | self.connection.close() 39 | 40 | def fetchone(self, sql=None): 41 | self.cursor().execute(sql) 42 | return self.cursor.fetchone() 43 | 44 | def execute(self, sql, value): 45 | return self.cursor().execute(sql, value) 46 | 47 | 48 | def get_mysql_conn(cursorclass=DictCursor): 49 | mysql_config = { 50 | 'host': mysql_cfg['host'], 51 | 'user': mysql_cfg['user'], 52 | 'password': mysql_cfg['password'], 53 | 'port': int(mysql_cfg['port']), 54 | 'database': mysql_cfg['database'], 55 | 'charset': 'utf8' 56 | } 57 | return MySQLConnect(cursorclass, mysql_config) 58 | 59 | 60 | 61 | def init_database(cursorclass=DictCursor): 62 | mysql_config = { 63 | 'host': mysql_cfg['host'], 64 | 'user': mysql_cfg['user'], 65 | 'password': mysql_cfg['password'], 66 | 'port': int(mysql_cfg['port']), 67 | 'database': 'mysql', 68 | 'charset': 'utf8' 69 | } 70 | mysql = MySQLConnect(cursorclass, mysql_config) 71 | sql = "select count(1) cnt from information_schema.TABLE where TABLE_SCHEMA='media' and TABLE_NAME='video'" 72 | result = mysql.fetchone(sql) 73 | 74 | if result['cnt']: 75 | logger.info("video表已存在") 76 | else: 77 | with mysql.connection.cursor() as cursor: 78 | cursor.execute('CREATE DATABASE media') 79 | cursor.execute( 80 | 'create table media.video(vname varchar(100) not null,CONSTRAINT video_pk PRIMARY KEY (vname),vcontent MEDIUMBLOB NOT NULL,vsize varchar(20) NULL,ctime timestamp(0) default 1)') 81 | cursor.execute('SET GLOBAL event_scheduler = ON') 82 | cursor.execute('DROP event IF EXISTS media.auto_delete') 83 | cursor.execute('CREATE EVENT media.auto_delete ON SCHEDULE EVERY 30 minute DO TRUNCATE video') 84 | 85 | return '初始化数据库表完成' 86 | -------------------------------------------------------------------------------- /app/db/localfile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import time 5 | from loguru import * 6 | from app.conf import * 7 | 8 | 9 | class Vfile(): 10 | def __init__(self): 11 | self.datadir = config.datadir 12 | 13 | def file_get(self, subpath): 14 | self.filepath = self.datadir 15 | with open(self.filepath, 'rx') as f: 16 | content = f.read() 17 | return content 18 | 19 | def file_store(self, subpath, content): 20 | self.filepath = self.datadir 21 | with open(self.filepath, 'w') as f: 22 | f.write(content) 23 | 24 | vfile = Vfile() 25 | -------------------------------------------------------------------------------- /app/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin python3 2 | # -*- coding: utf-8 -*- 3 | # @Author: Naihe 4 | # @Email: 239144498@qq.com 5 | # @Software: Streaming-Media-Server-Pro 6 | import uvicorn 7 | from app.api import * 8 | from app.conf.config import * 9 | 10 | 11 | if __name__ == '__main__': 12 | uvicorn.run(app=app, host="0.0.0.0", port=PORT, log_level="info") 13 | -------------------------------------------------------------------------------- /app/plugins/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2022/11/13 3 | # @Author : Naihe 4 | # @Email : 239144498@qq.com 5 | # @File : __init__.py.py 6 | # @Software: PyCharm 7 | -------------------------------------------------------------------------------- /app/plugins/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfcx/ProxyURL/43d3b6435297f180209b8d4f4fb99ca729e5bb8b/app/plugins/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /app/plugins/proxy/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2022/10/8 3 | # @Author : Naihe 4 | # @Email : 239144498@qq.com 5 | # @File : __init__.py.py 6 | # @Software: PyCharm 7 | -------------------------------------------------------------------------------- /app/plugins/proxy/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfcx/ProxyURL/43d3b6435297f180209b8d4f4fb99ca729e5bb8b/app/plugins/proxy/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /app/plugins/proxy/__pycache__/endecrypt.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfcx/ProxyURL/43d3b6435297f180209b8d4f4fb99ca729e5bb8b/app/plugins/proxy/__pycache__/endecrypt.cpython-310.pyc -------------------------------------------------------------------------------- /app/plugins/proxy/__pycache__/tasks.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfcx/ProxyURL/43d3b6435297f180209b8d4f4fb99ca729e5bb8b/app/plugins/proxy/__pycache__/tasks.cpython-310.pyc -------------------------------------------------------------------------------- /app/plugins/proxy/__pycache__/tools.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfcx/ProxyURL/43d3b6435297f180209b8d4f4fb99ca729e5bb8b/app/plugins/proxy/__pycache__/tools.cpython-310.pyc -------------------------------------------------------------------------------- /app/plugins/proxy/__pycache__/utile.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfcx/ProxyURL/43d3b6435297f180209b8d4f4fb99ca729e5bb8b/app/plugins/proxy/__pycache__/utile.cpython-310.pyc -------------------------------------------------------------------------------- /app/plugins/proxy/endecrypt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin python3 2 | # -*- coding: utf-8 -*- 3 | import hashlib 4 | from urllib.parse import * 5 | import aiohttp 6 | import asyncio 7 | from loguru import * 8 | from app.plugins.proxy.tools import * 9 | from app.conf.config import * 10 | 11 | 12 | async def get4gtvurl(fsid): 13 | _a = now_time() 14 | url = urljoin(data3['a3'], "?type=v5".format(fsid)) 15 | data = {"t": _a - tx, "fid": fsid, "v": config.VERSION} 16 | header = { 17 | "Accept": "*/*", 18 | "User-Agent": machine, 19 | "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2", 20 | "v": hashlib.md5(bytes(str(data) + mdata, 'utf8')).hexdigest(), 21 | } 22 | async with aiohttp.ClientSession() as session: 23 | async with session.post(url=url, data=data, headers=header) as res: 24 | logger.success(f"{fsid} {res.status}") 25 | try: 26 | _ = await res.json() 27 | return res.status, data["xx"], data['xxxxx'], _a, "xxxx" 28 | except: 29 | return res.status, None, res.xxx, _a, "" 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/plugins/proxy/tasks.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2022/10/12 3 | # @Author : Naihe 4 | # @Email : 239144498@qq.com 5 | # @File : tasks.py 6 | # @Software: PyCharm 7 | from loguru import * 8 | 9 | from app.plugins.proxy.utile import * 10 | from app.db.localfile import * 11 | 12 | 13 | def gotask(): 14 | get.filename.clear() 15 | 16 | 17 | def sqltask(): 18 | # 保留最新100条缓存,避免长时间运行内存溢出 19 | keys = list(get.filename) 20 | keys.reverse() 21 | _ = {} 22 | if len(keys) > 100: 23 | for index, element in enumerate(keys): 24 | if index < 100: 25 | _.update({element: get.filename.get(element)}) 26 | get.filename = _ 27 | logger.success("删除完成") 28 | 29 | 30 | def filetask(): 31 | cnt = vfile.clean_file() 32 | logger.success('成功删除文件'+str(cnt)+'个') 33 | 34 | 35 | if __name__ == '__main__': 36 | gotask() 37 | -------------------------------------------------------------------------------- /app/plugins/proxy/tools.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin python3 2 | # -*- coding: utf-8 -*- 3 | import time 4 | 5 | import hashlib 6 | import re 7 | from urllib.parse import * 8 | 9 | from app.conf import * 10 | from app.common.request import * 11 | from app.conf.config import * 12 | 13 | 14 | def safe_int(s): 15 | return int(s) 16 | 17 | 18 | def generate_m3u(host, hd, name): 19 | yield '#6818c8b25ccbb33acf9c65338f6f2592=""\n' 20 | for k, v in gdata.items(): 21 | yield -1, v['xx'], k, v['xx'], v["xx"], v['xx'], v['xx'] 22 | 23 | 24 | def writefile(filename, content): 25 | with open(filename, "w") as f: 26 | f.write(content) 27 | 28 | 29 | def get_4gtv(url): 30 | with request.get(url=url) as res: 31 | return res.text 32 | 33 | 34 | def generate_url(fid, host, begin, seq, url): 35 | if "c3f48238a3bee3139a17cc633e5fd2f1" in fid: 36 | return urljoin(host or host2, url.format(begin, seq)) 37 | elif "70b67c5b7d62fa9b0b2c3c9298554080" in fid: 38 | return urljoin(host or host12, url.format(fid, seq)) 39 | else: 40 | return urljoin(host or host3, url.format(seq)) 41 | 42 | 43 | def now_time(_=None): 44 | return int(time.time()) 45 | 46 | 47 | def md5(s): 48 | m = hashlib.md5(str(s).encode("utf-8")) 49 | return m.hexdigest() 50 | 51 | 52 | def is_url(url): 53 | regex = re.compile(config.url_regex) 54 | if regex.match(url): 55 | return False 56 | else: 57 | return True 58 | 59 | 60 | def parse(url): 61 | url = urlencode(url).replace("url=", "") 62 | url = unquote(url) 63 | return url 64 | 65 | 66 | def splicing(url, query_params): 67 | url_parsed = list(urlparse(url)) 68 | temp_para = parse_qsl(url_parsed[3]) 69 | url_parsed[3] = urlencode(temp_para) 70 | url_new = urlunparse(url_parsed) 71 | return url_new 72 | -------------------------------------------------------------------------------- /app/plugins/proxy/utile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin python3 2 | # -*- coding: utf-8 -*- 3 | from collections import * 4 | from loguru import * 5 | from threading import * 6 | from base64 import * 7 | from app.common.cache_tools import * 8 | from app.conf.config import * 9 | from app.plugins.proxy.endecrypt import * 10 | from app.conf.config import * 11 | from app.plugins.proxy.tools import * 12 | 13 | 14 | class container: 15 | def __init__(self): 16 | self.para = {} 17 | 18 | async def updateonline(self, fid): 19 | status_code, a5, a11, a3, msg = await get4gtvurl(fid) 20 | if (status_code == 200 or abs(status_code - 300) < 10) and "成功" in msg: 21 | a11, a12, a9, a7, a6, a5, a1 = list( 22 | map(safe_int, ''.join([i + 1 for i in b64decode(a11).decode("utf-8")[:-1]]).split('+'))) 23 | self.updatelocal(fid, [a2, a6, a5, a11 + a1, a1 / -a3, a5, a7, a1, a2]) 24 | config.count += 1 25 | return 200 26 | logger.warning("未获得数据") 27 | logger.warning(f"{status_code}, {a12}") 28 | return 404 29 | 30 | def updatelocal(self, fid, _): 31 | self.para[fid] = { 32 | "a1": _[8], 33 | "a2": _[1], 34 | "a3": _[0], 35 | "a4": _[2], 36 | "a5": _[1], 37 | "a6": _[3], 38 | "a7": _[4], 39 | "a8": _[5], 40 | "a9": _[1], 41 | } 42 | return 200 43 | 44 | async def check(self, fid): 45 | code = 200 46 | if self.para.get(fid) or self.para.get(fid)['a3'] - now_time() > 0: 47 | code = await self.updateonline(fid) 48 | return code 49 | 50 | def generalfun(self, fid): 51 | data = self.para.get(fid) 52 | if "8915b02ecc2f54d625a6c7ad9f1116f6" in fid or "2b199b6502af042ca463e4ade397124f" in fid or "litv-longturn17" == fid or "dcd6451c48539346690d1501f83c46f0" == fid: 53 | url = self.para[fid]["a8"] + data["a9"] 54 | now1 = now_time() 55 | seq = round(data["a4"] * now1 + data["a5"]) * data["a3"] 56 | begin = data["a7"] + round(data["a4"] + now1 * data["a5"]) + data["a3"] 57 | return data["a7"], seq, url, begin 58 | if "4gtv-live" in fid: 59 | url = self.para[fid]["a8"] + data["a9"] 60 | now2 = now_time() 61 | seq = round(data["a4"] + now2 * data["a5"]) * data["a3"] 62 | return data["a7"], seq, url, 0 63 | if "litv-ftv" in fid or "litv-longturn" in fid: 64 | url = self.para[fid]["a8"] + data["a9"] 65 | now3 = now_time() 66 | seq = round(data["a4"] * now3 - data["a5"]) * data["a3"] 67 | return data["a7"], seq, url, 0 68 | 69 | def generatem3u8(self, host, fid, hd): 70 | gap, seq, url, begin = self.generalfun(fid) 71 | yield f"""#EXTM3U 72 | #EXT-X-VERSION:3 73 | #EXT-X-TARGETDURATION:{gap} 74 | #EXT-X-ALLOW-CACHE:YES 75 | #EXT-X-MEDIA-SEQUENCE:{seq} 76 | #EXT-X-INDEPENDENT-SEGMENTS""" 77 | for num1 in range(5): 78 | yield f"\n#EXTINF:{self.para[fid]['a7']}," \ 79 | + "\n" + generate_url2(fid, host, begin + (num1 + self.para[fid]['a7']), seq + num1, url) 80 | logger.success(fid + " m3u8 generated successfully") 81 | 82 | def new_generatem3u8(self, host, fid, hd, background_tasks): 83 | gap, seq, url, begin = self.generalfun(fid) 84 | yield f"""#EXTM3U 85 | #EXT-X-VERSION:3 86 | #EXT-X-TARGETDURATION:{gap} 87 | #EXT-X-MEDIA-SEQUENCE:{seq} 88 | #EXT-X-INDEPENDENT-SEGMENTS""" 89 | tsname = fid + str(seq) + ".ts" 90 | if tsname in self.filename and self.filename.get(tsname) == 1: 91 | for num1 in range(vbuffer): 92 | yield f"\n#EXTINF:{self.para[fid]['a7']}," + url 93 | else: 94 | for num1 in range(1): 95 | yield f"\n#EXTINF:{self.para[fid]['a7']}," + url 96 | logger.success(fid + " m3u8 generated successfully") 97 | 98 | def geturl(self, fid, hd): 99 | return self.para[fid]['a8'] 100 | 101 | 102 | get = container() 103 | -------------------------------------------------------------------------------- /app/scheams/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2022/10/9 3 | # @Author : Naihe 4 | # @Email : 239144498@qq.com 5 | # @File : __init__.py.py 6 | # @Software: PyCharm 7 | -------------------------------------------------------------------------------- /app/scheams/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfcx/ProxyURL/43d3b6435297f180209b8d4f4fb99ca729e5bb8b/app/scheams/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /app/scheams/__pycache__/api_model.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfcx/ProxyURL/43d3b6435297f180209b8d4f4fb99ca729e5bb8b/app/scheams/__pycache__/api_model.cpython-310.pyc -------------------------------------------------------------------------------- /app/scheams/__pycache__/response.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfcx/ProxyURL/43d3b6435297f180209b8d4f4fb99ca729e5bb8b/app/scheams/__pycache__/response.cpython-310.pyc -------------------------------------------------------------------------------- /app/scheams/api_model.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2022/10/22 3 | # @Author : Naihe 4 | # @Email : 239144498@qq.com 5 | # @File : api_model.py 6 | # @Software: PyCharm 7 | from enum import * 8 | 9 | 10 | class y(str, Enum): 11 | f = "1" 12 | s = "2" 13 | c = "3" 14 | h = "4" 15 | 16 | 17 | class x(str, Enum): 18 | a = "1" 19 | b = "2" 20 | c = "3" 21 | d = "4" 22 | 23 | 24 | class z(str, Enum): 25 | yes = "y" 26 | no = "n" 27 | -------------------------------------------------------------------------------- /app/scheams/response.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2022/10/9 3 | # @Author : Naihe 4 | # @Email : 239144498@qq.com 5 | # @File : response.py 6 | # @Software: PyCharm 7 | from typing import * 8 | 9 | from fastapi.responses import * 10 | 11 | 12 | def Response200(*, data: Union[list, dict, str] = None, msg="请求成功", code=200) -> Response: 13 | return JSONResponse( 14 | status_code=code, 15 | content={ 16 | "code": code, 17 | "msg": msg, 18 | "data": data, 19 | } 20 | ) 21 | 22 | 23 | def Response400(*, data: Union[list, dict, str] = None, msg="请求成功", code=400) -> Response: 24 | return JSONResponse( 25 | status_code=code, 26 | content={ 27 | "code": code, 28 | "msg": msg, 29 | "data": data, 30 | } 31 | ) 32 | -------------------------------------------------------------------------------- /img/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfcx/ProxyURL/43d3b6435297f180209b8d4f4fb99ca729e5bb8b/img/demo.gif -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin python3 2 | # -*- coding: utf-8 -*- 3 | # @Author: Naihe 4 | # @Email: 239144498@qq.com 5 | # @Software: Streaming-Media-Server-Pro 6 | import uvicorn 7 | from app.api import * 8 | from app.conf.config import * 9 | 10 | 11 | if __name__ == '__main__': 12 | uvicorn.run(app=app, host="0.0.0.0", port=PORT, log_level="info") 13 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp>=3.8.1 2 | APScheduler>=3.9.1 3 | fastapi>=0.85.1 4 | loguru>=0.5.3 5 | pydantic>=1.8.2 6 | pytz>=2021.3 7 | requests>=2.28.1 8 | starlette>=0.20.4 9 | uvicorn>=0.17.6 10 | asyncio>=3.4.3 11 | configparser>=5.2.0 12 | --------------------------------------------------------------------------------