mirror of
https://github.com/alantang1977/JunTV.git
synced 2024-12-05 00:33:10 +02:00
Merge branch 'Guovin:master' into main
This commit is contained in:
commit
0a8c1270ec
34 changed files with 2006 additions and 979 deletions
54
.github/workflows/main.yml
vendored
54
.github/workflows/main.yml
vendored
|
@ -10,7 +10,6 @@ on:
|
|||
- main
|
||||
- dev
|
||||
- gd
|
||||
- gd-test
|
||||
jobs:
|
||||
push:
|
||||
runs-on: ${{ matrix.operating-system }}
|
||||
|
@ -25,10 +24,10 @@ jobs:
|
|||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ env.BRANCH_NAME }}
|
||||
- name: Run with setup-python 3.8
|
||||
- name: Run with setup-python 3.13
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.8'
|
||||
python-version: '3.13'
|
||||
update-environment: true
|
||||
cache: 'pipenv'
|
||||
- name: Check open_driver config
|
||||
|
@ -41,33 +40,36 @@ jobs:
|
|||
except:
|
||||
open_driver = False
|
||||
print(open_driver)')" >> $GITHUB_ENV
|
||||
- name: Check open_ffmpeg config
|
||||
id: check_ffmpeg
|
||||
run: |
|
||||
echo "OPEN_FFMPEG=$(python -c '
|
||||
try:
|
||||
from utils.config import config
|
||||
open_ffmpeg = config.open_ffmpeg
|
||||
except:
|
||||
open_ffmpeg = False
|
||||
print(open_ffmpeg)')" >> $GITHUB_ENV
|
||||
# - name: Check open_ffmpeg config
|
||||
# id: check_ffmpeg
|
||||
# run: |
|
||||
# echo "OPEN_FFMPEG=$(python -c '
|
||||
# try:
|
||||
# from utils.config import config
|
||||
# open_ffmpeg = config.open_ffmpeg
|
||||
# except:
|
||||
# open_ffmpeg = False
|
||||
# print(open_ffmpeg)')" >> $GITHUB_ENV
|
||||
- name: Set up Chrome
|
||||
if: env.OPEN_DRIVER == 'True' || env.OPEN_DRIVER == 'true'
|
||||
if: env.OPEN_DRIVER == 'True'
|
||||
uses: browser-actions/setup-chrome@latest
|
||||
with:
|
||||
chrome-version: stable
|
||||
- name: Download chrome driver
|
||||
if: env.OPEN_DRIVER == 'True' || env.OPEN_DRIVER == 'true'
|
||||
if: env.OPEN_DRIVER == 'True'
|
||||
uses: nanasess/setup-chromedriver@master
|
||||
- name: Install FFmpeg
|
||||
if: env.OPEN_FFMPEG == 'True' || env.OPEN_FFMPEG == 'true'
|
||||
run: sudo apt-get update && sudo apt-get install -y ffmpeg
|
||||
# - name: Install FFmpeg
|
||||
# if: env.OPEN_FFMPEG == 'True'
|
||||
# run: sudo apt-get update && sudo apt-get install -y ffmpeg
|
||||
- name: Install pipenv
|
||||
run: pip3 install --user pipenv
|
||||
- name: Install dependecies
|
||||
run: pipenv --python 3.8 && pipenv install
|
||||
- name: Build
|
||||
run: pipenv run build
|
||||
run: pipenv --python 3.13 && pipenv install --deploy
|
||||
- name: Install selenium
|
||||
if: env.OPEN_DRIVER == 'True'
|
||||
run: pipenv install selenium
|
||||
- name: Update
|
||||
run: pipenv run dev
|
||||
- name: Commit and push if changed
|
||||
run: |
|
||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
@ -95,13 +97,11 @@ jobs:
|
|||
if [[ -f "$final_m3u_file" ]]; then
|
||||
git add -f "$final_m3u_file"
|
||||
fi
|
||||
if [[ -f "output/result_cache.pkl" ]]; then
|
||||
git add -f "output/result_cache.pkl"
|
||||
if [[ -f "output/cache.pkl" ]]; then
|
||||
git add -f "output/cache.pkl"
|
||||
fi
|
||||
if [[ -f "output/user_result.log" ]]; then
|
||||
git add -f "output/user_result.log"
|
||||
elif [[ -f "output/result.log" ]]; then
|
||||
git add -f "output/result.log"
|
||||
if [[ -f "output/sort.log" ]]; then
|
||||
git add -f "output/sort.log"
|
||||
fi
|
||||
if [[ -f "updates/fofa/fofa_hotel_region_result.pkl" ]]; then
|
||||
git add -f "updates/fofa/fofa_hotel_region_result.pkl"
|
||||
|
|
13
.github/workflows/release.yml
vendored
13
.github/workflows/release.yml
vendored
|
@ -14,7 +14,7 @@ jobs:
|
|||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.8'
|
||||
python-version: '3.13'
|
||||
update-environment: true
|
||||
cache: 'pipenv'
|
||||
|
||||
|
@ -22,10 +22,7 @@ jobs:
|
|||
run: pip3 install --user pipenv
|
||||
|
||||
- name: Install dependencies with pipenv
|
||||
run: pipenv --python 3.8 && pipenv install
|
||||
|
||||
- name: Install PyInstaller
|
||||
run: pipenv install pyinstaller
|
||||
run: pipenv --python 3.13 && pipenv install --dev
|
||||
|
||||
- name: Build the application
|
||||
run: pipenv run pyinstaller tkinter_ui/tkinter_ui.spec
|
||||
|
@ -36,7 +33,7 @@ jobs:
|
|||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: iptv-update-tool
|
||||
name: IPTV
|
||||
path: dist
|
||||
|
||||
- name: Get version from version.json
|
||||
|
@ -79,6 +76,6 @@ jobs:
|
|||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: dist/iptv-update-tool.exe
|
||||
asset_name: iptv-update-tool.exe
|
||||
asset_path: dist/IPTV.exe
|
||||
asset_name: IPTV.exe
|
||||
asset_content_type: application/octet-stream
|
||||
|
|
35
CHANGELOG.md
35
CHANGELOG.md
|
@ -1,5 +1,36 @@
|
|||
# 更新日志(Changelog)
|
||||
|
||||
## v1.5.4
|
||||
|
||||
### 2024/11/29
|
||||
|
||||
- ⚠️ Python 升级至 3.13,该版本已不支持 Win7,若有需要请使用 v1.5.3
|
||||
- ⚠️ Github 仓库改名:iptv-api,使用旧接口地址请及时更换新地址
|
||||
- ⚠️ Docker 新镜像仓库启用:guovern/iptv-api(旧版的 tv-driver 改为:guovern/iptv-api:latest,tv-requests 改为 guovern/iptv-api:lite),iptv-api:latest 为完整版、iptv-api:lite 为精简版,请使用新的名称命令进行拉取,旧仓库将不再维护
|
||||
- ❤️ 新增微信公众号关注途径(公众号搜索:Govin),推荐关注公众号,可订阅更新通知与使用技巧等文章推送,还可进行交流讨论
|
||||
- ✨ 更换测速方法(yt-dlp),重构测速逻辑,提升准确性、稳定性与效率,减小接口切换延迟(#563)
|
||||
- ✨ 新增支持 ARM v7(#562)
|
||||
- ✨ 新增双结果 API 访问(ip/m3u, ip/txt)(#581)
|
||||
- ✨ 新增启动 API 服务命令(pipenv run service)
|
||||
- 🪄 优化 Docker 镜像大小(完整版:-25%,精简版:-66%)
|
||||
- 🐛 修复部分播放器不支持的信息间隔符(#581)
|
||||
|
||||
<details>
|
||||
<summary>English</summary>
|
||||
|
||||
- ⚠️ Python has been upgraded to version 3.13, which no longer supports Win7. If needed, please use version v1.5.3.
|
||||
- ⚠️ The GitHub repository has been renamed to iptv-api. If you are using the old API address, please update it to the new one promptly.
|
||||
- ⚠️ New Docker image repository is now active: guovern/iptv-api (the old tv-driver is now guovern/iptv-api:latest, and tv-requests is now guovern/iptv-api:lite). iptv-api:latest is the full version, and iptv-api:lite is the lightweight version. Please use the new names to pull the images, as the old repository will no longer be maintained.
|
||||
- ❤️ A new way to follow the WeChat official account (search for: Govin) has been added. It is recommended to follow the official account to subscribe to update notifications, usage tips, and engage in discussions.
|
||||
- ✨ The speed measurement method has been changed to yt-dlp, and the speed measurement logic has been refactored to improve accuracy, stability, and efficiency, reducing interface switching delay (#563).
|
||||
- ✨ Support for ARM v7 has been added (#562).
|
||||
- ✨ Dual result API access (ip/m3u, ip/txt) has been added (#581).
|
||||
- ✨ A command to start the API service (pipenv run service) has been added.
|
||||
- 🪄 The size of the Docker image has been optimized (Full version: -25%, Lite version: -66%).
|
||||
- 🐛 Fixed the information delimiter issue for some players that do not support it (#581).
|
||||
|
||||
</details>
|
||||
|
||||
## v1.5.3
|
||||
|
||||
### 2024/11/19
|
||||
|
@ -59,7 +90,7 @@
|
|||
- ✨ 新增频道接口白名单:不参与测速,永远保留在结果最前面(#470)
|
||||
使用方法:
|
||||
1. 模板频道接口地址后添加$!即可实现(如:广东珠江,http://xxx.m3u$! )
|
||||
2. 额外信息补充(如:广东珠江,http://xxx.m3u$!额外信息 ),更多接口白名单请至https://github.com/Guovin/TV/issues/514 讨论
|
||||
2. 额外信息补充(如:广东珠江,http://xxx.m3u$!额外信息 ),更多接口白名单请至https://github.com/Guovin/iptv-api/issues/514 讨论
|
||||
- ✨ 新增 🈳 无结果频道分类:无结果频道默认归类至该底部分类下(#473)
|
||||
- ✨ 接口地址增加来源类型说明
|
||||
- ✨ 默认模板增加广东民生(#481)、广州综合(#504)
|
||||
|
@ -78,7 +109,7 @@
|
|||
- ✨ Added channel interface whitelist: Not participating in speed testing, always kept at the very front of the results. (#470)
|
||||
Usage:
|
||||
1. Add $! after the template channel interface address (e.g., Guangdong Pearl River, http://xxx.m3u$!).
|
||||
2. Additional information can be appended (e.g., Guangdong Pearl River, http://xxx.m3u$! Additional Information) (#470). For more interface whitelists, please discuss at https://github.com/Guovin/TV/issues/514.
|
||||
2. Additional information can be appended (e.g., Guangdong Pearl River, http://xxx.m3u$! Additional Information) (#470). For more interface whitelists, please discuss at https://github.com/Guovin/iptv-api/issues/514.
|
||||
- ✨ Added 🈳 No Results Channel Category: Channels without results are categorized under this bottom category by default (#473).
|
||||
- ✨ Interface addresses now include source type descriptions.
|
||||
- ✨ Default templates now include Guangdong People's Livelihood (#481) and Guangzhou Comprehensive (#504).
|
||||
|
|
73
Dockerfile
73
Dockerfile
|
@ -1,51 +1,58 @@
|
|||
FROM python:3.8-slim
|
||||
FROM python:3.13 AS builder
|
||||
|
||||
ARG APP_WORKDIR=/tv
|
||||
ARG LITE=False
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY Pipfile* ./
|
||||
|
||||
RUN pip install -i https://mirrors.aliyun.com/pypi/simple pipenv
|
||||
|
||||
RUN PIPENV_VENV_IN_PROJECT=1 pipenv install --deploy\
|
||||
&& if [ "$LITE" = False ]; then pipenv install selenium; fi
|
||||
|
||||
|
||||
FROM python:3.13-slim
|
||||
|
||||
ARG APP_WORKDIR=/iptv-api
|
||||
ARG LITE=False
|
||||
|
||||
ENV APP_WORKDIR=$APP_WORKDIR
|
||||
|
||||
COPY . $APP_WORKDIR
|
||||
ENV LITE=$LITE
|
||||
ENV PATH="/.venv/bin:$PATH"
|
||||
|
||||
WORKDIR $APP_WORKDIR
|
||||
|
||||
RUN pip install -i https://mirrors.aliyun.com/pypi/simple pipenv \
|
||||
&& pipenv install
|
||||
COPY . $APP_WORKDIR
|
||||
|
||||
RUN echo "deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm main contrib non-free non-free-firmware\n \
|
||||
deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm main contrib non-free non-free-firmware\n \
|
||||
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm-updates main contrib non-free non-free-firmware\n \
|
||||
deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm-updates main contrib non-free non-free-firmware\n \
|
||||
deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm-updates main contrib non-free non-free-firmware\n \
|
||||
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm-backports main contrib non-free non-free-firmware\n \
|
||||
deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm-backports main contrib non-free non-free-firmware\n \
|
||||
deb https://mirrors.tuna.tsinghua.edu.cn/debian-security/ bookworm-security main contrib non-free non-free-firmware\n \
|
||||
deb-src https://mirrors.tuna.tsinghua.edu.cn/debian-security/ bookworm-security main contrib non-free non-free-firmware\n" \
|
||||
COPY --from=builder /app/.venv /.venv
|
||||
|
||||
RUN echo "deb https://mirrors.aliyun.com/debian/ bookworm main contrib non-free non-free-firmware\n \
|
||||
deb-src https://mirrors.aliyun.com/debian/ bookworm main contrib non-free non-free-firmware\n \
|
||||
deb https://mirrors.aliyun.com/debian/ bookworm-updates main contrib non-free non-free-firmware\n \
|
||||
deb-src https://mirrors.aliyun.com/debian/ bookworm-updates main contrib non-free non-free-firmware\n \
|
||||
deb-src https://mirrors.aliyun.com/debian/ bookworm-updates main contrib non-free non-free-firmware\n \
|
||||
deb https://mirrors.aliyun.com/debian/ bookworm-backports main contrib non-free non-free-firmware\n \
|
||||
deb-src https://mirrors.aliyun.com/debian/ bookworm-backports main contrib non-free non-free-firmware\n \
|
||||
deb https://mirrors.aliyun.com/debian-security/ bookworm-security main contrib non-free non-free-firmware\n \
|
||||
deb-src https://mirrors.aliyun.com/debian-security/ bookworm-security main contrib non-free non-free-firmware\n" \
|
||||
> /etc/apt/sources.list
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
cron \
|
||||
ffmpeg
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends cron
|
||||
|
||||
ARG INSTALL_CHROMIUM=false
|
||||
|
||||
RUN if [ "$INSTALL_CHROMIUM" = "true" ]; then \
|
||||
apt-get install -y --no-install-recommends \
|
||||
chromium \
|
||||
chromium-driver; \
|
||||
fi
|
||||
|
||||
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
RUN if [ "$LITE" = False ]; then apt-get install -y --no-install-recommends chromium chromium-driver; fi \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN (crontab -l ; \
|
||||
echo "0 22 * * * cd $APP_WORKDIR && /usr/local/bin/pipenv run python main.py scheduled_task"; \
|
||||
echo "0 10 * * * cd $APP_WORKDIR && /usr/local/bin/pipenv run python main.py scheduled_task") | crontab -
|
||||
echo "0 22 * * * cd $APP_WORKDIR && /.venv/bin/python main.py"; \
|
||||
echo "0 10 * * * cd $APP_WORKDIR && /.venv/bin/python main.py") | crontab -
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
COPY entrypoint.sh /tv_entrypoint.sh
|
||||
COPY entrypoint.sh /iptv-api-entrypoint.sh
|
||||
|
||||
COPY config /tv_config
|
||||
COPY config /iptv-api-config
|
||||
|
||||
RUN chmod +x /tv_entrypoint.sh
|
||||
RUN chmod +x /iptv-api-entrypoint.sh
|
||||
|
||||
ENTRYPOINT /tv_entrypoint.sh
|
||||
ENTRYPOINT /iptv-api-entrypoint.sh
|
26
Pipfile
26
Pipfile
|
@ -4,13 +4,16 @@ url = "https://mirrors.aliyun.com/pypi/simple"
|
|||
verify_ssl = true
|
||||
|
||||
[scripts]
|
||||
build = "python main.py"
|
||||
dev = "python main.py"
|
||||
service = "python service/app.py"
|
||||
ui = "python tkinter_ui/tkinter_ui.py"
|
||||
multicast_tmp = "python updates/multicast/update_tmp.py"
|
||||
docker_run = "docker run -v ./config:/iptv-api/config -v ./output:/iptv-api/output -d -p 8000:8000 guovern/iptv-api"
|
||||
docker_run_lite = "docker run -v ./config:/iptv-api-lite/config -v ./output:/iptv-api-lite/output -d -p 8000:8000 guovern/iptv-api:lite"
|
||||
tkinter_build = "pyinstaller tkinter_ui/tkinter_ui.spec"
|
||||
docker_build = "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 --build-arg APP_WORKDIR=/iptv-api -t guovern/iptv-api ."
|
||||
docker_build_lite = "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 --build-arg APP_WORKDIR=/iptv-api-lite --build-arg LITE=True -t guovern/iptv-api:lite ."
|
||||
|
||||
[dev-packages]
|
||||
|
||||
[packages]
|
||||
requests = "*"
|
||||
selenium = "*"
|
||||
bs4 = "*"
|
||||
|
@ -21,8 +24,21 @@ aiohttp = "*"
|
|||
flask = "*"
|
||||
opencc-python-reimplemented = "*"
|
||||
fake-useragent = "*"
|
||||
pillow = "*"
|
||||
yt-dlp = "*"
|
||||
|
||||
[packages]
|
||||
requests = "*"
|
||||
bs4 = "*"
|
||||
tqdm = "*"
|
||||
async-timeout = "*"
|
||||
aiohttp = "*"
|
||||
flask = "*"
|
||||
opencc-python-reimplemented = "*"
|
||||
fake-useragent = "*"
|
||||
gunicorn = "*"
|
||||
pillow = "*"
|
||||
yt-dlp = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.8"
|
||||
python_version = "3.13"
|
||||
|
|
1743
Pipfile.lock
generated
1743
Pipfile.lock
generated
File diff suppressed because it is too large
Load diff
108
README.md
108
README.md
|
@ -1,9 +1,9 @@
|
|||
<div align="center">
|
||||
<img src="./static/images/logo.png" alt="logo"/>
|
||||
<h1 align="center">IPTV电视直播源更新工具</h1>
|
||||
<h1 align="center">IPTV-API</h1>
|
||||
</div>
|
||||
|
||||
<div align="center">自定义频道菜单,根据模板频道,自动获取并更新最新的直播源接口,测速校验后生成可用的接口文件</div>
|
||||
<div align="center">自定义频道,自动获取直播源接口,测速验效后生成可用的结果</div>
|
||||
<div align="center">默认结果包含:📺央视频道、💰央视付费频道、📡卫视频道、🏠广东频道、🌊港·澳·台频道、🎬电影频道、🎥咪咕直播、🏀体育频道、🪁动画频道、🎮游戏频道、🎵音乐频道、🏛经典剧场</div>
|
||||
|
||||
<details>
|
||||
|
@ -62,14 +62,17 @@
|
|||
</details>
|
||||
<br>
|
||||
<p align="center">
|
||||
<a href="https://github.com/Guovin/TV/releases/latest">
|
||||
<img src="https://img.shields.io/github/v/release/guovin/tv" />
|
||||
<a href="https://github.com/Guovin/iptv-api/releases/latest">
|
||||
<img src="https://img.shields.io/github/v/release/guovin/iptv-api" />
|
||||
</a>
|
||||
<a href="https://www.python.org/">
|
||||
<img src="https://img.shields.io/badge/python-%20%3E%3D%203.8-47c219" />
|
||||
<img src="https://img.shields.io/badge/python-%20%3D%203.13-47c219" />
|
||||
</a>
|
||||
<a href="https://github.com/Guovin/TV/releases/latest">
|
||||
<img src="https://img.shields.io/github/downloads/guovin/tv/total" />
|
||||
<a href="https://github.com/Guovin/iptv-api/releases/latest">
|
||||
<img src="https://img.shields.io/github/downloads/guovin/iptv-api/total" />
|
||||
</a>
|
||||
<a href="https://hub.docker.com/repository/docker/guovern/iptv-api">
|
||||
<img src="https://img.shields.io/docker/pulls/guovern/iptv-api?label=docker:iptv-api" />
|
||||
</a>
|
||||
<a href="https://hub.docker.com/repository/docker/guovern/tv-requests">
|
||||
<img src="https://img.shields.io/docker/pulls/guovern/tv-requests?label=docker:requests" />
|
||||
|
@ -77,46 +80,57 @@
|
|||
<a href="https://hub.docker.com/repository/docker/guovern/tv-driver">
|
||||
<img src="https://img.shields.io/docker/pulls/guovern/tv-driver?label=docker:driver" />
|
||||
</a>
|
||||
<a href="https://github.com/Guovin/TV/fork">
|
||||
<img src="https://img.shields.io/github/forks/guovin/tv" />
|
||||
<a href="https://github.com/Guovin/iptv-api/fork">
|
||||
<img src="https://img.shields.io/github/forks/guovin/iptv-api" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
[English](./README_en.md) | 中文
|
||||
|
||||
## ✅ 特点
|
||||
- [✅ 特点](#特点)
|
||||
- [🔗 最新结果](#最新结果)
|
||||
- [⚙️ 配置参数](./docs/config.md)
|
||||
- [🚀 快速上手](#快速上手)
|
||||
- [📖 详细教程](./docs/tutorial.md)
|
||||
- [🗓️ 更新日志](./CHANGELOG.md)
|
||||
- [💰️ 赞赏](#赞赏)
|
||||
- [👀 关注](#关注)
|
||||
- [📣 免责声明](#免责声明)
|
||||
- [⚖️ 许可证](#许可证)
|
||||
|
||||
## 特点
|
||||
|
||||
- ✅ 自定义模板,生成您想要的频道
|
||||
- ✅ 支持多种获取源方式:组播源、酒店源、订阅源、关键字搜索
|
||||
- ✅ 接口测速验效,响应时间、分辨率优先级,过滤无效接口
|
||||
- ✅ 偏好设置:IPv6、接口来源排序优先级与数量配置、接口白名单
|
||||
- ✅ 定时执行,北京时间每日 6:00 与 18:00 执行更新
|
||||
- ✅ 支持多种运行方式:工作流、命令行、GUI 软件、Docker(amd64/arm64)
|
||||
- ✅ 支持多种运行方式:工作流、命令行、GUI 软件、Docker(amd64/arm64/arm v7)
|
||||
- ✨ 更多功能请见[配置参数](./docs/config.md)
|
||||
|
||||
## 🔗 最新结果
|
||||
## 最新结果
|
||||
|
||||
- 接口源:
|
||||
|
||||
```bash
|
||||
https://ghproxy.net/raw.githubusercontent.com/Guovin/TV/gd/output/result.m3u
|
||||
https://ghproxy.net/raw.githubusercontent.com/Guovin/iptv-api/gd/output/result.m3u
|
||||
```
|
||||
|
||||
```bash
|
||||
https://ghproxy.net/raw.githubusercontent.com/Guovin/TV/gd/output/result.txt
|
||||
https://ghproxy.net/raw.githubusercontent.com/Guovin/iptv-api/gd/output/result.txt
|
||||
```
|
||||
|
||||
- 数据源:
|
||||
|
||||
```bash
|
||||
https://ghproxy.net/raw.githubusercontent.com/Guovin/TV/gd/source.json
|
||||
https://ghproxy.net/raw.githubusercontent.com/Guovin/iptv-api/gd/source.json
|
||||
```
|
||||
|
||||
## ⚙️ 配置
|
||||
## 配置
|
||||
|
||||
[配置参数](./docs/config.md)
|
||||
|
||||
## 🚀 快速上手
|
||||
## 快速上手
|
||||
|
||||
### 方式一:工作流
|
||||
|
||||
|
@ -129,16 +143,24 @@ pip install pipenv
|
|||
```
|
||||
|
||||
```python
|
||||
pipenv install
|
||||
pipenv install --dev
|
||||
```
|
||||
|
||||
启动更新:
|
||||
|
||||
```python
|
||||
pipenv run build
|
||||
pipenv run dev
|
||||
```
|
||||
|
||||
启动服务:
|
||||
|
||||
```python
|
||||
pipenv run service
|
||||
```
|
||||
|
||||
### 方式三:GUI 软件
|
||||
|
||||
1. 下载[更新工具软件](https://github.com/Guovin/TV/releases),打开软件,点击更新,即可完成更新
|
||||
1. 下载[IPTV-API 更新软件](https://github.com/Guovin/iptv-api/releases),打开软件,点击更新,即可完成更新
|
||||
|
||||
2. 或者在项目目录下运行以下命令,即可打开 GUI 软件:
|
||||
|
||||
|
@ -146,41 +168,39 @@ pipenv run build
|
|||
pipenv run ui
|
||||
```
|
||||
|
||||
<img src="./docs/images/ui.png" alt="更新工具软件" title="更新工具软件" style="height:600px" />
|
||||
<img src="./docs/images/ui.png" alt="IPTV-API更新软件" title="IPTV-API更新软件" style="height:600px" />
|
||||
|
||||
### 方式四:Docker
|
||||
|
||||
- driver:性能要求较高,更新速度较慢,稳定性、成功率高;修改配置 open_driver = False 可切换到 request 版本(推荐酒店源、组播源、关键字搜索使用此版本)
|
||||
- requests:轻量级,性能要求低,更新速度快,稳定性不确定(推荐订阅源使用此版本)
|
||||
|
||||
建议都试用一次,选择自己合适的版本
|
||||
- iptv-api(完整版本):性能要求较高,更新速度较慢,稳定性、成功率高;修改配置 open_driver = False 可切换到 Lite 版本运行模式(推荐酒店源、组播源、关键字搜索使用此版本)
|
||||
- iptv-api:lite(精简版本):轻量级,性能要求低,更新速度快,稳定性不确定(推荐订阅源使用此版本)
|
||||
|
||||
1. 拉取镜像:
|
||||
|
||||
- driver:
|
||||
- iptv-api:
|
||||
|
||||
```bash
|
||||
docker pull guovern/tv-driver:latest
|
||||
docker pull guovern/iptv-api:latest
|
||||
```
|
||||
|
||||
- requests:
|
||||
- iptv-api:lite:
|
||||
|
||||
```bash
|
||||
docker pull guovern/tv-requests:latest
|
||||
docker pull guovern/iptv-api:lite
|
||||
```
|
||||
|
||||
2. 运行容器:
|
||||
|
||||
- driver:
|
||||
- iptv-api:
|
||||
|
||||
```bash
|
||||
docker run -d -p 8000:8000 guovern/tv-driver
|
||||
docker run -d -p 8000:8000 guovern/iptv-api
|
||||
```
|
||||
|
||||
- requests:
|
||||
- iptv-api:lite:
|
||||
|
||||
```bash
|
||||
docker run -d -p 8000:8000 guovern/tv-requests
|
||||
docker run -d -p 8000:8000 guovern/iptv-api:lite
|
||||
```
|
||||
|
||||
卷挂载参数(可选):
|
||||
|
@ -188,29 +208,31 @@ docker run -d -p 8000:8000 guovern/tv-requests
|
|||
|
||||
以宿主机路径/etc/docker 为例:
|
||||
|
||||
- driver:
|
||||
- iptv-api:
|
||||
|
||||
```bash
|
||||
docker run -v /etc/docker/config:/tv-driver/config -v /etc/docker/output:/tv-driver/output -d -p 8000:8000 guovern/tv-driver
|
||||
docker run -v /etc/docker/config:/iptv-api/config -v /etc/docker/output:/iptv-api/output -d -p 8000:8000 guovern/iptv-api
|
||||
```
|
||||
|
||||
- requests:
|
||||
- iptv-api:lite:
|
||||
|
||||
```bash
|
||||
docker run -v /etc/docker/config:/tv-requests/config -v /etc/docker/output:/tv-requests/output -d -p 8000:8000 guovern/tv-requests
|
||||
docker run -v /etc/docker/config:/iptv-api-lite/config -v /etc/docker/output:/iptv-api-lite/output -d -p 8000:8000 guovern/iptv-api:lite
|
||||
```
|
||||
|
||||
3. 更新结果:
|
||||
|
||||
- 接口地址:ip:8000
|
||||
- 接口详情:ip:8000/result
|
||||
- M3u 接口:ip:8000/m3u
|
||||
- Txt 接口:ip:8000/txt
|
||||
- 接口内容:ip:8000/content
|
||||
- 测速日志:ip:8000/log
|
||||
|
||||
## 🗓️ 更新日志
|
||||
## 更新日志
|
||||
|
||||
[更新日志](./CHANGELOG.md)
|
||||
|
||||
## 💰️ 赞赏
|
||||
## 赞赏
|
||||
|
||||
<div>开发维护不易,请我喝杯咖啡☕️吧~</div>
|
||||
|
||||
|
@ -218,16 +240,16 @@ docker run -v /etc/docker/config:/tv-requests/config -v /etc/docker/output:/tv-r
|
|||
| ----------------------------------------- | ------------------------------------------- |
|
||||
| ![支付宝扫码](./static/images/alipay.jpg) | ![微信扫码](./static/images/appreciate.jpg) |
|
||||
|
||||
## 👀 关注
|
||||
## 关注
|
||||
|
||||
微信公众号搜索 Govin,或扫码,接收更新推送、学习更多使用技巧:
|
||||
|
||||
![微信公众号](./static/images/qrcode.jpg)
|
||||
|
||||
## 📣 免责声明
|
||||
## 免责声明
|
||||
|
||||
本项目仅供学习交流用途,接口数据均来源于网络,如有侵权,请联系删除
|
||||
|
||||
## ⚖️ 许可证
|
||||
## 许可证
|
||||
|
||||
[MIT](./LICENSE) License © 2024-PRESENT [Govin](https://github.com/guovin)
|
||||
|
|
108
README_en.md
108
README_en.md
|
@ -1,9 +1,9 @@
|
|||
<div align="center">
|
||||
<img src="./static/images/logo.png" alt="logo"/>
|
||||
<h1 align="center">IPTV live TV source update tool</h1>
|
||||
<h1 align="center">IPTV-API</h1>
|
||||
</div>
|
||||
|
||||
<div align="justify">Customize the channel menu, automatically obtain and update the latest live source interfaces based on the template channels, and generate available interface files after speed test verification.</div>
|
||||
<div align="center">Customize channels, automatically obtain live source interface, and generate usable results after speed test</div>
|
||||
<div align="justify">Default results include: 📺CCTV Channel, 💰CCTV Pay Channel, 📡Satellite TV Channel, 🏠Guangdong Channel, 🌊Hong Kong · Macao · Taiwan Channel, 🎬Movie Channel, 🎥Migu Live Streaming, 🏀Sports Channel, 🪁Animation channel, 🎮Game channel, 🎵Music channel, 🏛Classic Theater.</div>
|
||||
|
||||
<details>
|
||||
|
@ -62,14 +62,17 @@
|
|||
</details>
|
||||
<br>
|
||||
<p align="center">
|
||||
<a href="https://github.com/Guovin/TV/releases/latest">
|
||||
<img src="https://img.shields.io/github/v/release/guovin/tv" />
|
||||
<a href="https://github.com/Guovin/iptv-api/releases/latest">
|
||||
<img src="https://img.shields.io/github/v/release/guovin/iptv-api" />
|
||||
</a>
|
||||
<a href="https://www.python.org/">
|
||||
<img src="https://img.shields.io/badge/python-%20%3E%3D%203.8-47c219" />
|
||||
<img src="https://img.shields.io/badge/python-%20%3D%203.13-47c219" />
|
||||
</a>
|
||||
<a href="https://github.com/Guovin/TV/releases/latest">
|
||||
<img src="https://img.shields.io/github/downloads/guovin/tv/total" />
|
||||
<a href="https://github.com/Guovin/iptv-api/releases/latest">
|
||||
<img src="https://img.shields.io/github/downloads/guovin/iptv-api/total" />
|
||||
</a>
|
||||
<a href="https://hub.docker.com/repository/docker/guovern/iptv-api">
|
||||
<img src="https://img.shields.io/docker/pulls/guovern/iptv-api?label=docker:iptv-api" />
|
||||
</a>
|
||||
<a href="https://hub.docker.com/repository/docker/guovern/tv-requests">
|
||||
<img src="https://img.shields.io/docker/pulls/guovern/tv-requests?label=docker:requests" />
|
||||
|
@ -77,46 +80,57 @@
|
|||
<a href="https://hub.docker.com/repository/docker/guovern/tv-driver">
|
||||
<img src="https://img.shields.io/docker/pulls/guovern/tv-driver?label=docker:driver" />
|
||||
</a>
|
||||
<a href="https://github.com/Guovin/TV/fork">
|
||||
<img src="https://img.shields.io/github/forks/guovin/tv" />
|
||||
<a href="https://github.com/Guovin/iptv-api/fork">
|
||||
<img src="https://img.shields.io/github/forks/guovin/iptv-api" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
[中文](./README.md) | English
|
||||
|
||||
## ✅ Features
|
||||
- [✅ Features](#features)
|
||||
- [🔗 Latest results](#latest-results)
|
||||
- [⚙️ Config parameter](./docs/config_en.md)
|
||||
- [🚀 Quick Start](#quick-start)
|
||||
- [📖 Detailed Tutorial](./docs/tutorial_en.md)
|
||||
- [🗓️ Changelog](./CHANGELOG.md)
|
||||
- [💰️ Appreciate](#appreciate)
|
||||
- [👀 Follow](#follow)
|
||||
- [📣 Disclaimer](#disclaimer)
|
||||
- [⚖️ License](#license)
|
||||
|
||||
## Features
|
||||
|
||||
- ✅ Customize the template to generate the channel you want
|
||||
- ✅ Supports multiple source acquisition methods: multicast source, hotel source, subscription source, keyword search
|
||||
- ✅ Interface speed testing and verification, with priority on response time and resolution, filtering out ineffective interfaces
|
||||
- ✅ Preferences: IPv6, priority and quantity of interface source sorting, and interface whitelist
|
||||
- ✅ Scheduled execution at 6:00 AM and 18:00 PM Beijing time daily
|
||||
- ✅ Supports various execution methods: workflows, command line, GUI software, Docker(amd64/arm64)
|
||||
- ✅ Supports various execution methods: workflows, command line, GUI software, Docker(amd64/arm64/arm v7)
|
||||
- ✨ For more features, see [Config parameter](./docs/config_en.md)
|
||||
|
||||
## 🔗 Latest results
|
||||
## Latest results
|
||||
|
||||
- Interface source:
|
||||
|
||||
```bash
|
||||
https://ghproxy.net/raw.githubusercontent.com/Guovin/TV/gd/output/result.m3u
|
||||
https://ghproxy.net/raw.githubusercontent.com/Guovin/iptv-api/gd/output/result.m3u
|
||||
```
|
||||
|
||||
```bash
|
||||
https://ghproxy.net/raw.githubusercontent.com/Guovin/TV/gd/output/result.txt
|
||||
https://ghproxy.net/raw.githubusercontent.com/Guovin/iptv-api/gd/output/result.txt
|
||||
```
|
||||
|
||||
- Data source:
|
||||
|
||||
```bash
|
||||
https://ghproxy.net/raw.githubusercontent.com/Guovin/TV/gd/source.json
|
||||
https://ghproxy.net/raw.githubusercontent.com/Guovin/iptv-api/gd/source.json
|
||||
```
|
||||
|
||||
## ⚙️ Config
|
||||
## Config
|
||||
|
||||
[Config parameter](./docs/config_en.md)
|
||||
|
||||
## 🚀 Quick Start
|
||||
## Quick Start
|
||||
|
||||
### Method 1: Workflow
|
||||
|
||||
|
@ -129,16 +143,24 @@ pip install pipenv
|
|||
```
|
||||
|
||||
```python
|
||||
pipenv install
|
||||
pipenv install --dev
|
||||
```
|
||||
|
||||
Start update:
|
||||
|
||||
```python
|
||||
pipenv run build
|
||||
pipenv run dev
|
||||
```
|
||||
|
||||
Start service:
|
||||
|
||||
```python
|
||||
pipenv run service
|
||||
```
|
||||
|
||||
### Method 3: GUI Software
|
||||
|
||||
1. Download [Update tool software](https://github.com/Guovin/TV/releases), open the software, click update to complete the update
|
||||
1. Download [IPTV-API update software](https://github.com/Guovin/iptv-api/releases), open the software, click update to complete the update
|
||||
|
||||
2. Or run the following command in the project directory to open the GUI software:
|
||||
|
||||
|
@ -146,41 +168,41 @@ pipenv run build
|
|||
pipenv run ui
|
||||
```
|
||||
|
||||
<img src="./docs/images/ui.png" alt="Update tool software" title="Update tool software" style="height:600px" />
|
||||
<img src="./docs/images/ui.png" alt="IPTV-API update software" title="IPTV-API update software" style="height:600px" />
|
||||
|
||||
### Method 4: Docker
|
||||
|
||||
- driver: Higher performance requirements, slower update speed, high stability and success rate. Set open_driver = False to switch to the request version (recommended for hotel sources, multicast sources, and online searches)
|
||||
- requests: Lightweight, low performance requirements, fast update speed, stability uncertain (recommend using this version for the subscription source)
|
||||
- iptv-api (Full version): Higher performance requirements, slower update speed, high stability and success rate. Set open_driver = False to switch to the lite running mode (recommended for hotel sources, multicast sources, and online searches)
|
||||
- iptv-api:lite (Condensed version): Lightweight, low performance requirements, fast update speed, stability uncertain (recommend using this version for the subscription source)
|
||||
|
||||
It's recommended to try each one and choose the version that suits you
|
||||
|
||||
1. Pull the image:
|
||||
|
||||
- driver
|
||||
- iptv-api
|
||||
|
||||
```bash
|
||||
docker pull guovern/tv-driver:latest
|
||||
docker pull guovern/iptv-api:latest
|
||||
```
|
||||
|
||||
- requests
|
||||
- iptv-api:lite
|
||||
|
||||
```bash
|
||||
docker pull guovern/tv-requests:latest
|
||||
docker pull guovern/iptv-api:lite
|
||||
```
|
||||
|
||||
2. Run the container:
|
||||
|
||||
- driver
|
||||
- iptv-api
|
||||
|
||||
```bash
|
||||
docker run -d -p 8000:8000 guovern/tv-driver
|
||||
docker run -d -p 8000:8000 guovern/iptv-api
|
||||
```
|
||||
|
||||
- requests
|
||||
- iptv-api:lite
|
||||
|
||||
```bash
|
||||
docker run -d -p 8000:8000 guovern/tv-requests
|
||||
docker run -d -p 8000:8000 guovern/iptv-api:lite
|
||||
```
|
||||
|
||||
Volume Mount Parameter (Optional):
|
||||
|
@ -188,29 +210,31 @@ This allows synchronization of files between the host machine and the container.
|
|||
|
||||
Taking the host path /etc/docker as an example:
|
||||
|
||||
- driver:
|
||||
- iptv-api:
|
||||
|
||||
```bash
|
||||
docker run -v /etc/docker/config:/tv-driver/config -v /etc/docker/output:/tv-driver/output -d -p 8000:8000 guovern/tv-driver
|
||||
docker run -v /etc/docker/config:/iptv-api/config -v /etc/docker/output:/iptv-api/output -d -p 8000:8000 guovern/iptv-api
|
||||
```
|
||||
|
||||
- requests:
|
||||
- iptv-api:lite:
|
||||
|
||||
```bash
|
||||
docker run -v /etc/docker/config:/tv-requests/config -v /etc/docker/output:/tv-requests/output -d -p 8000:8000 guovern/tv-requests
|
||||
docker run -v /etc/docker/config:/iptv-api-lite/config -v /etc/docker/output:/iptv-api-lite/output -d -p 8000:8000 guovern/iptv-api:lite
|
||||
```
|
||||
|
||||
3. Update results:
|
||||
|
||||
- API address: ip:8000
|
||||
- API details: ip:8000/result
|
||||
- M3u api:ip:8000/m3u
|
||||
- Txt api:ip:8000/txt
|
||||
- API content: ip:8000/content
|
||||
- Speed test log: ip:8000/log
|
||||
|
||||
## 🗓️ Changelog
|
||||
## Changelog
|
||||
|
||||
[Changelog](./CHANGELOG.md)
|
||||
|
||||
## 💰️ Appreciate
|
||||
## Appreciate
|
||||
|
||||
<div>Development and maintenance are not easy, please buy me a coffee ~</div>
|
||||
|
||||
|
@ -218,16 +242,16 @@ docker run -v /etc/docker/config:/tv-requests/config -v /etc/docker/output:/tv-r
|
|||
| ------------------------------------- | ----------------------------------------- |
|
||||
| ![Alipay](./static/images/alipay.jpg) | ![Wechat](./static/images/appreciate.jpg) |
|
||||
|
||||
## 👀 Follow
|
||||
## Follow
|
||||
|
||||
Wechat public account search Govin, or scan code:
|
||||
Wechat public account search for Govin, or scan the code to receive updates and learn more tips:
|
||||
|
||||
![Wechat public account](./static/images/qrcode.jpg)
|
||||
|
||||
## 📣 Disclaimer
|
||||
## Disclaimer
|
||||
|
||||
This project is for learning and communication purposes only. All interface data comes from the internet. If there is any infringement, please contact us for removal.
|
||||
|
||||
## ⚖️ License
|
||||
## License
|
||||
|
||||
[MIT](./LICENSE) License © 2024-PRESENT [Govin](https://github.com/guovin)
|
||||
|
|
|
@ -9,7 +9,7 @@ online_search_page_num = 1
|
|||
urls_limit = 10
|
||||
open_keep_all = False
|
||||
open_sort = True
|
||||
sort_timeout = 10
|
||||
sort_timeout = 5
|
||||
open_ffmpeg = True
|
||||
open_filter_resolution = True
|
||||
min_resolution = 1920x1080
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
| urls_limit | 10 | 单个频道接口数量 |
|
||||
| open_keep_all | False | 保留所有检索结果,会保留非模板频道名称的结果,推荐手动维护时开启 |
|
||||
| open_sort | True | 开启排序功能(响应速度、日期、分辨率) |
|
||||
| sort_timeout | 10 | 单个接口测速超时时长,单位秒(s);数值越大测速所属时间越长,能提高获取接口数量,但质量会有所下降;数值越小测速所需时间越短,能获取低延时的接口,质量较好;调整此值能优化更新时间 |
|
||||
| sort_timeout | 5 | 单个接口测速超时时长,单位秒(s);数值越大测速所属时间越长,能提高获取接口数量,但质量会有所下降;数值越小测速所需时间越短,能获取低延时的接口,质量较好;调整此值能优化更新时间 |
|
||||
| open_ffmpeg | True | 开启使用 FFmpeg 进行测速,获取更准确的速度与分辨率信息,需要提前手动安装 |
|
||||
| open_m3u_result | True | 开启转换生成 m3u 文件类型结果链接,支持显示频道图标 |
|
||||
| open_filter_resolution | True | 开启分辨率过滤,低于最小分辨率(min_resolution)的接口将会被过滤 |
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
| urls_limit | 10 | Number of interfaces per channel |
|
||||
| open_keep_all | False | Retain all search results, retain results with non-template channel names, recommended to be turned on when manually maintaining |
|
||||
| open_sort | True | Enable the sorting function (response speed, date, resolution) |
|
||||
| sort_timeout | 10 | The timeout duration for speed testing of a single interface, in seconds (s). A larger value means a longer testing period, which can increase the number of interfaces obtained but may decrease their quality. A smaller value means a shorter testing time, which can obtain low-latency interfaces with better quality. Adjusting this value can optimize the update time. |
|
||||
| sort_timeout | 5 | The timeout duration for speed testing of a single interface, in seconds (s). A larger value means a longer testing period, which can increase the number of interfaces obtained but may decrease their quality. A smaller value means a shorter testing time, which can obtain low-latency interfaces with better quality. Adjusting this value can optimize the update time. |
|
||||
| open_ffmpeg | True | Enable speed testing using FFmpeg to obtain more accurate speed and resolution information. Manual installation is required in advance. |
|
||||
| open_m3u_result | True | Enable the conversion to generate m3u file type result links, supporting the display of channel icons |
|
||||
| open_filter_resolution | True | Enable resolution filtering, interfaces with resolution lower than the minimum resolution (min_resolution) will be filtered |
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
### 1. Star
|
||||
|
||||
打开 https://github.com/Guovin/TV ,点击 Star 收藏该项目(您的 Star 是我持续更新的动力)
|
||||
打开 https://github.com/Guovin/iptv-api ,点击 Star 收藏该项目(您的 Star 是我持续更新的动力)
|
||||
![Star](./images/star.png 'Star')
|
||||
|
||||
### 2. Watch
|
||||
|
@ -149,11 +149,11 @@ https://mirror.ghproxy.com/raw.githubusercontent.com/您的github用户名/仓
|
|||
|
||||
如果访问该链接能正常返回更新后的接口内容,说明您的直播源接口链接已经大功告成了!将该链接复制粘贴到 TVBox 等软件配置栏中即可使用~
|
||||
|
||||
- 注意:除了首次执行工作流需要您手动触发,后续执行(默认北京时间每日 6:00 和 18:00)将自动触发。如果您修改了模板或配置文件想立刻执行更新,可手动触发(2)中的 Run workflow 即可。
|
||||
- 注意:除了首次执行工作流需要您手动触发,后续执行(默认北京时间每日 6:00 与 18:00)将自动触发。如果您修改了模板或配置文件想立刻执行更新,可手动触发(2)中的 Run workflow 即可。
|
||||
|
||||
### 4.修改工作流更新频率(可选)
|
||||
|
||||
如果您想修改更新频率(默认北京时间每日 6:00 和 18:00),可修改 on:schedule:- cron 字段:
|
||||
如果您想修改更新频率(默认北京时间每日 6:00 与 18:00),可修改 on:schedule:- cron 字段:
|
||||
![.github/workflows/main.yml](./images/schedule-cron.png '.github/workflows/main.yml')
|
||||
如果您想 每 2 天执行更新可以这样修改:
|
||||
|
||||
|
@ -162,26 +162,41 @@ https://mirror.ghproxy.com/raw.githubusercontent.com/您的github用户名/仓
|
|||
- cron: '0 10 */2 * *'
|
||||
```
|
||||
|
||||
#### 1. 强烈不建议修改,因为短时间内的接口内容并无差异,过高的更新频率与高耗时运行的工作流都有可能被判定为资源滥用,导致仓库与账户被封禁的风险。
|
||||
#### 1. 强烈不建议修改更新频率过高,因为短时间内的接口内容并无差异,过高的更新频率与高耗时运行的工作流都有可能被判定为资源滥用,导致仓库与账户被封禁的风险。
|
||||
|
||||
#### 2. 请留意您的工作流运行时长,若发现执行时间过长,需要适当删减模板中频道数量、修改配置中的分页数量和接口数量,以达到合规的运行要求。
|
||||
|
||||
### 方式二:命令行
|
||||
|
||||
```python
|
||||
1. 安装 Python
|
||||
请至官方下载并安装 Python,安装时请选择将 Python 添加到系统环境变量 Path 中
|
||||
请至官方下载并安装 Python,安装时请选择将 Python 添加到系统环境变量 Path 中
|
||||
|
||||
2. 运行更新
|
||||
项目目录下打开终端 CMD 依次运行以下命令:
|
||||
项目目录下打开终端 CMD 依次运行以下命令:
|
||||
|
||||
```python
|
||||
pip install pipenv
|
||||
pipenv install
|
||||
pipenv run build
|
||||
```
|
||||
|
||||
```python
|
||||
pipenv install --dev
|
||||
```
|
||||
|
||||
启动更新:
|
||||
|
||||
```python
|
||||
pipenv run dev
|
||||
```
|
||||
|
||||
启动服务:
|
||||
|
||||
```python
|
||||
pipenv run service
|
||||
```
|
||||
|
||||
### 方式三:GUI 软件
|
||||
|
||||
1. 下载[更新工具软件](https://github.com/Guovin/TV/releases),打开软件,点击更新,即可完成更新
|
||||
1. 下载[IPTV-API 更新软件](https://github.com/Guovin/iptv-api/releases),打开软件,点击更新,即可完成更新
|
||||
|
||||
2. 或者在项目目录下运行以下命令,即可打开 GUI 软件:
|
||||
|
||||
|
@ -189,37 +204,65 @@ pipenv run build
|
|||
pipenv run ui
|
||||
```
|
||||
|
||||
![更新工具软件](./images/ui.png '更新工具软件')
|
||||
![IPTV-API 更新软件](./images/ui.png 'IPTV-API 更新软件')
|
||||
|
||||
### 方式四:Docker
|
||||
|
||||
- requests:轻量级,性能要求低,更新速度快,稳定性不确定(推荐订阅源使用此版本)
|
||||
- driver:性能要求较高,更新速度较慢,稳定性、成功率高;修改配置 open_driver = False 可切换到 request 版本(推荐酒店源、组播源、关键字搜索使用此版本)
|
||||
- iptv-api(完整版本):性能要求较高,更新速度较慢,稳定性、成功率高;修改配置 open_driver = False 可切换到 Lite 版本运行模式(推荐酒店源、组播源、关键字搜索使用此版本)
|
||||
- iptv-api:lite(精简版本):轻量级,性能要求低,更新速度快,稳定性不确定(推荐订阅源使用此版本)
|
||||
|
||||
1. 拉取镜像:
|
||||
|
||||
- iptv-api:
|
||||
|
||||
```bash
|
||||
1. 拉取镜像:
|
||||
requests:
|
||||
docker pull guovern/tv-requests:latest
|
||||
docker pull guovern/iptv-api:latest
|
||||
```
|
||||
|
||||
driver:
|
||||
docker pull guovern/tv-driver:latest
|
||||
- iptv-api:lite:
|
||||
|
||||
```bash
|
||||
docker pull guovern/iptv-api:lite
|
||||
```
|
||||
|
||||
2. 运行容器:
|
||||
docker run -d -p 8000:8000 guovern/tv-requests 或 tv-driver
|
||||
|
||||
- iptv-api:
|
||||
|
||||
```bash
|
||||
docker run -d -p 8000:8000 guovern/iptv-api
|
||||
```
|
||||
|
||||
- iptv-api:lite:
|
||||
|
||||
```bash
|
||||
docker run -d -p 8000:8000 guovern/iptv-api:lite
|
||||
```
|
||||
|
||||
卷挂载参数(可选):
|
||||
实现宿主机文件与容器文件同步,修改模板、配置、获取更新结果文件可直接在宿主机文件夹下操作
|
||||
|
||||
配置文件:
|
||||
-v 宿主机路径/config:/tv-requests/config 或 tv-driver/config
|
||||
以宿主机路径/etc/docker 为例:
|
||||
|
||||
结果文件:
|
||||
-v 宿主机路径/output:/tv-requests/output 或 tv-driver/output
|
||||
- iptv-api:
|
||||
|
||||
3. 查看更新结果:访问(域名:8000)
|
||||
```bash
|
||||
docker run -v /etc/docker/config:/iptv-api/config -v /etc/docker/output:/iptv-api/output -d -p 8000:8000 guovern/iptv-api
|
||||
```
|
||||
|
||||
#### 注:方式一至三更新完成后的结果文件链接:http://本地 ip:8000 或 http://localhost:8000
|
||||
- iptv-api:lite:
|
||||
|
||||
```bash
|
||||
docker run -v /etc/docker/config:/iptv-api-lite/config -v /etc/docker/output:/iptv-api-lite/output -d -p 8000:8000 guovern/iptv-api:lite
|
||||
```
|
||||
|
||||
3. 更新结果:
|
||||
|
||||
- 接口地址:ip:8000
|
||||
- M3u 接口:ip:8000/m3u
|
||||
- Txt 接口:ip:8000/txt
|
||||
- 接口内容:ip:8000/content
|
||||
- 测速日志:ip:8000/log
|
||||
|
||||
### 上传更新文件至仓库(可选)
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ Since this project will continue to iterate and improve, if you want to get the
|
|||
|
||||
### 1. Star
|
||||
|
||||
Go to https://github.com/Guovin/TV, click on Star to bookmark this project (Your Star is my motivation to keep updating).
|
||||
Go to https://github.com/Guovin/iptv-api, click on Star to bookmark this project (Your Star is my motivation to keep updating).
|
||||
![Star](./images/star.png 'Star')
|
||||
|
||||
### 2. Watch
|
||||
|
@ -146,11 +146,11 @@ https://mirror.ghproxy.com/raw.githubusercontent.com/your github username/reposi
|
|||
|
||||
If you can access this link and it returns the updated interface content, then your live source interface link has been successfully created! Simply copy and paste this link into software like TVBox in the configuration field to use~
|
||||
|
||||
- Note: Except for the first execution of the workflow, which requires you to manually trigger it, subsequent executions (default: daily at 6:00 am and 18:00 pm Beijing time) will be automatically triggered. If you have modified the template or configuration files and want to execute the update immediately, you can manually trigger (2) Run workflow.
|
||||
- Note: Except for the first execution of the workflow, which requires you to manually trigger it, subsequent executions (default: 6:00 AM and 18:00 PM Beijing time daily) will be automatically triggered. If you have modified the template or configuration files and want to execute the update immediately, you can manually trigger (2) Run workflow.
|
||||
|
||||
### 4.Modify Workflow Update Frequency(optional)
|
||||
|
||||
If you want to modify the update frequency (default: daily at 6:00 am and 18:00 pm Beijing time), you can modify the on:schedule:- cron field.
|
||||
If you want to modify the update frequency (default: 6:00 AM and 18:00 PM Beijing time daily), you can modify the on:schedule:- cron field.
|
||||
![.github/workflows/main.yml](./images/schedule-cron.png '.github/workflows/main.yml')
|
||||
If you want to perform updates every 2 days, you can modify it like this:
|
||||
|
||||
|
@ -159,26 +159,41 @@ If you want to perform updates every 2 days, you can modify it like this:
|
|||
- cron: '0 10 */2 * *'
|
||||
```
|
||||
|
||||
#### 1. It is strongly discouraged to make modifications, as there is no difference in the content of the interface in a short period of time. Both too frequent updates and high-consumption running workflows may be judged as resource abuse, leading to the risk of the repository and account being banned.
|
||||
#### 1. It is strongly not recommended to modify and update too frequently, because the interface content does not differ within a short period of time, and too high update frequency and time-consuming workflow may be judged as resource abuse, resulting in the risk of warehouse and account being blocked.
|
||||
|
||||
#### 2. Please pay attention to the runtime of your workflow. If you find that the execution time is too long, you need to appropriately reduce the number of channels in the template, modify the number of pages and interfaces in the configuration, in order to meet the compliant operation requirements.
|
||||
|
||||
### Method 2: Command Line
|
||||
|
||||
```python
|
||||
1. Install Python
|
||||
Please download and install Python from the official site. During installation, choose to add Python to the system's environment variables Path.
|
||||
Please download and install Python from the official site. During installation, choose to add Python to the system's environment variables Path.
|
||||
|
||||
2. Run Update
|
||||
Open a CMD terminal in the project directory and run the following commands in sequence:
|
||||
Open a CMD terminal in the project directory and run the following commands in sequence:
|
||||
|
||||
```python
|
||||
pip install pipenv
|
||||
pipenv install
|
||||
pipenv run build
|
||||
```
|
||||
|
||||
```python
|
||||
pipenv install --dev
|
||||
```
|
||||
|
||||
Start update:
|
||||
|
||||
```python
|
||||
pipenv run dev
|
||||
```
|
||||
|
||||
Start service:
|
||||
|
||||
```python
|
||||
pipenv run service
|
||||
```
|
||||
|
||||
### Method 3: GUI Software
|
||||
|
||||
1. Download the update tool software, open the software, click update to complete the update.
|
||||
1. Download [IPTV-API software](https://github.com/Guovin/iptv-api/releases), open the software, click update to complete the update.
|
||||
|
||||
2. Alternatively, run the following command in the project directory to open the GUI software:
|
||||
|
||||
|
@ -186,37 +201,67 @@ pipenv run build
|
|||
pipenv run ui
|
||||
```
|
||||
|
||||
![Update tool software](./images/ui.png 'Update tool software')
|
||||
![IPTV-API software](./images/ui.png 'IPTV-API software')
|
||||
|
||||
### Method 4: Docker
|
||||
|
||||
- requests: Lightweight, low performance requirements, fast update speed, stability uncertain (recommend this version for subscription sources)
|
||||
- driver: Higher performance requirements, slower update speed, high stability and success rate. Set open_driver = False to switch to the request version (recommended for hotel sources, multicast sources, and keyword search)
|
||||
- iptv-api (Full version): Higher performance requirements, slower update speed, high stability and success rate. Set open_driver = False to switch to the lite running mode (recommended for hotel sources, multicast sources, and online searches)
|
||||
- iptv-api:lite (Condensed version): Lightweight, low performance requirements, fast update speed, stability uncertain (recommend using this version for the subscription source)
|
||||
|
||||
It's recommended to try each one and choose the version that suits you
|
||||
|
||||
1. Pull the image:
|
||||
|
||||
- iptv-api
|
||||
|
||||
```bash
|
||||
1. Pull the image:
|
||||
For requests version:
|
||||
docker pull guovern/tv-requests:latest
|
||||
docker pull guovern/iptv-api:latest
|
||||
```
|
||||
|
||||
For driver version:
|
||||
docker pull guovern/tv-driver:latest
|
||||
- iptv-api:lite
|
||||
|
||||
```bash
|
||||
docker pull guovern/iptv-api:lite
|
||||
```
|
||||
|
||||
2. Run the container:
|
||||
docker run -d -p 8000:8000 guovern/tv-requests or driver
|
||||
|
||||
- iptv-api
|
||||
|
||||
```bash
|
||||
docker run -d -p 8000:8000 guovern/iptv-api
|
||||
```
|
||||
|
||||
- iptv-api:lite
|
||||
|
||||
```bash
|
||||
docker run -d -p 8000:8000 guovern/iptv-api:lite
|
||||
```
|
||||
|
||||
Volume Mount Parameter (Optional):
|
||||
This allows synchronization of files between the host machine and the container. Modifying templates, configurations, and retrieving updated result files can be directly operated in the host machine's folder.
|
||||
|
||||
config:
|
||||
-v <path>/config:/tv-requests/config or tv-driver/config
|
||||
Taking the host path /etc/docker as an example:
|
||||
|
||||
result:
|
||||
-v <path>/output:/tv-requests/output or tv-driver/output
|
||||
- iptv-api:
|
||||
|
||||
3. Check the update results: Visit (domain:8000)
|
||||
```bash
|
||||
docker run -v /etc/docker/config:/iptv-api/config -v /etc/docker/output:/iptv-api/output -d -p 8000:8000 guovern/iptv-api
|
||||
```
|
||||
|
||||
#### Note: Link to the result file after updates of methods one to three: http://local ip:8000 or http://localhost:8000
|
||||
- iptv-api:lite:
|
||||
|
||||
```bash
|
||||
docker run -v /etc/docker/config:/iptv-api-lite/config -v /etc/docker/output:/iptv-api-lite/output -d -p 8000:8000 guovern/iptv-api:lite
|
||||
```
|
||||
|
||||
3. Update results:
|
||||
|
||||
- API address: ip:8000
|
||||
- M3u api:ip:8000/m3u
|
||||
- Txt api:ip:8000/txt
|
||||
- API content: ip:8000/content
|
||||
- Speed test log: ip:8000/log
|
||||
|
||||
### Update the File to the Repository(optional)
|
||||
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
from selenium import webdriver
|
||||
from utils.config import config
|
||||
|
||||
if config.open_driver:
|
||||
try:
|
||||
from selenium import webdriver
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def setup_driver(proxy=None):
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
from driver.setup import setup_driver
|
||||
from utils.retry import (
|
||||
retry_func,
|
||||
locate_element_with_retry,
|
||||
|
@ -7,13 +6,21 @@ from utils.retry import (
|
|||
from time import sleep
|
||||
import re
|
||||
from bs4 import BeautifulSoup
|
||||
from selenium.webdriver.common.by import By
|
||||
from utils.config import config
|
||||
|
||||
if config.open_driver:
|
||||
try:
|
||||
from selenium.webdriver.common.by import By
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def get_soup_driver(url):
|
||||
"""
|
||||
Get the soup by driver
|
||||
"""
|
||||
from driver.setup import setup_driver
|
||||
|
||||
driver = setup_driver()
|
||||
retry_func(lambda: driver.get(url), name=url)
|
||||
sleep(1)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
for file in /tv_config/*; do
|
||||
for file in /iptv-api-config/*; do
|
||||
filename=$(basename "$file")
|
||||
target_file="$APP_WORKDIR/config/$filename"
|
||||
if [ ! -e "$target_file" ]; then
|
||||
|
@ -8,8 +8,10 @@ for file in /tv_config/*; do
|
|||
fi
|
||||
done
|
||||
|
||||
service cron start
|
||||
. /.venv/bin/activate
|
||||
|
||||
pipenv run python $APP_WORKDIR/main.py
|
||||
service cron start &
|
||||
|
||||
gunicorn -w 4 -b 0.0.0.0:8000 main:app
|
||||
python $APP_WORKDIR/main.py &
|
||||
|
||||
python -m gunicorn service.app:app -b 0.0.0.0:8000 --timeout=1000
|
82
main.py
82
main.py
|
@ -1,13 +1,12 @@
|
|||
import asyncio
|
||||
from utils.config import config
|
||||
import utils.constants as constants
|
||||
from service.app import run_service
|
||||
from utils.channel import (
|
||||
get_channel_items,
|
||||
append_total_data,
|
||||
process_sort_channel_list,
|
||||
write_channel_to_file,
|
||||
setup_logging,
|
||||
cleanup_logging,
|
||||
get_channel_data_cache_with_compare,
|
||||
format_channel_url_info,
|
||||
)
|
||||
|
@ -16,7 +15,6 @@ from utils.tools import (
|
|||
get_pbar_remaining,
|
||||
get_ip_address,
|
||||
convert_to_m3u,
|
||||
get_result_file_content,
|
||||
process_nested_dict,
|
||||
format_interval,
|
||||
check_ipv6_support,
|
||||
|
@ -27,46 +25,11 @@ from updates.multicast import get_channels_by_multicast
|
|||
from updates.hotel import get_channels_by_hotel
|
||||
from updates.fofa import get_channels_by_fofa
|
||||
from updates.online_search import get_channels_by_online_search
|
||||
import os
|
||||
from tqdm import tqdm
|
||||
from tqdm.asyncio import tqdm_asyncio
|
||||
from time import time
|
||||
from flask import Flask, render_template_string
|
||||
import sys
|
||||
import atexit
|
||||
import pickle
|
||||
import copy
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
atexit.register(cleanup_logging)
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def show_index():
|
||||
return get_result_file_content()
|
||||
|
||||
|
||||
@app.route("/result")
|
||||
def show_result():
|
||||
return get_result_file_content(show_result=True)
|
||||
|
||||
|
||||
@app.route("/log")
|
||||
def show_log():
|
||||
user_log_file = "output/" + (
|
||||
"user_result.log" if os.path.exists("config/user_config.ini") else "result.log"
|
||||
)
|
||||
if os.path.exists(user_log_file):
|
||||
with open(user_log_file, "r", encoding="utf-8") as file:
|
||||
content = file.read()
|
||||
else:
|
||||
content = constants.waiting_tip
|
||||
return render_template_string(
|
||||
"<head><link rel='icon' href='{{ url_for('static', filename='images/favicon.ico') }}' type='image/x-icon'></head><pre>{{ content }}</pre>",
|
||||
content=content,
|
||||
)
|
||||
|
||||
|
||||
class UpdateSource:
|
||||
|
||||
|
@ -140,7 +103,6 @@ class UpdateSource:
|
|||
async def main(self):
|
||||
try:
|
||||
if config.open_update:
|
||||
setup_logging()
|
||||
main_start_time = time()
|
||||
self.channel_items = get_channel_items()
|
||||
channel_names = [
|
||||
|
@ -173,7 +135,7 @@ class UpdateSource:
|
|||
0,
|
||||
)
|
||||
self.start_time = time()
|
||||
self.pbar = tqdm_asyncio(total=self.total, desc="Sorting")
|
||||
self.pbar = tqdm(total=self.total, desc="Sorting")
|
||||
self.channel_data = await process_sort_channel_list(
|
||||
self.channel_data,
|
||||
ipv6=ipv6_support,
|
||||
|
@ -191,24 +153,17 @@ class UpdateSource:
|
|||
)
|
||||
self.pbar.close()
|
||||
user_final_file = config.final_file
|
||||
update_file(user_final_file, "output/result_new.txt")
|
||||
update_file(user_final_file, constants.result_path)
|
||||
if config.open_use_old_result:
|
||||
if open_sort:
|
||||
get_channel_data_cache_with_compare(
|
||||
channel_data_cache, self.channel_data
|
||||
)
|
||||
with open(
|
||||
resource_path("output/result_cache.pkl", persistent=True), "wb"
|
||||
resource_path(constants.cache_path, persistent=True),
|
||||
"wb",
|
||||
) as file:
|
||||
pickle.dump(channel_data_cache, file)
|
||||
if open_sort:
|
||||
user_log_file = "output/" + (
|
||||
"user_result.log"
|
||||
if os.path.exists("config/user_config.ini")
|
||||
else "result.log"
|
||||
)
|
||||
update_file(user_log_file, "output/result_new.log", copy=True)
|
||||
cleanup_logging()
|
||||
convert_to_m3u()
|
||||
total_time = format_interval(time() - main_start_time)
|
||||
print(
|
||||
|
@ -228,6 +183,8 @@ class UpdateSource:
|
|||
True,
|
||||
url=f"{get_ip_address()}" if open_service else None,
|
||||
)
|
||||
if open_service:
|
||||
run_service()
|
||||
except asyncio.exceptions.CancelledError:
|
||||
print("Update cancelled!")
|
||||
|
||||
|
@ -247,31 +204,8 @@ class UpdateSource:
|
|||
self.pbar.close()
|
||||
|
||||
|
||||
def scheduled_task():
|
||||
if __name__ == "__main__":
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
update_source = UpdateSource()
|
||||
loop.run_until_complete(update_source.start())
|
||||
|
||||
|
||||
def run_service():
|
||||
try:
|
||||
if not os.environ.get("GITHUB_ACTIONS"):
|
||||
ip_address = get_ip_address()
|
||||
print(f"📄 Result detail: {ip_address}/result")
|
||||
print(f"📄 Log detail: {ip_address}/log")
|
||||
print(f"✅ You can use this url to watch IPTV 📺: {ip_address}")
|
||||
app.run(host="0.0.0.0", port=8000)
|
||||
except Exception as e:
|
||||
print(f"❌ Service start failed: {e}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) == 1 and config.open_service:
|
||||
loop = asyncio.new_event_loop()
|
||||
|
||||
async def run_service_async():
|
||||
loop.run_in_executor(None, run_service)
|
||||
|
||||
asyncio.run(run_service_async())
|
||||
scheduled_task()
|
||||
|
|
61
service/app.py
Normal file
61
service/app.py
Normal file
|
@ -0,0 +1,61 @@
|
|||
import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.path.dirname(sys.path[0]))
|
||||
from flask import Flask, render_template_string
|
||||
from utils.tools import get_result_file_content, get_ip_address, resource_path
|
||||
import utils.constants as constants
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def show_index():
|
||||
return get_result_file_content()
|
||||
|
||||
|
||||
@app.route("/txt")
|
||||
def show_txt():
|
||||
return get_result_file_content(file_type="txt")
|
||||
|
||||
|
||||
@app.route("/m3u")
|
||||
def show_m3u():
|
||||
return get_result_file_content(file_type="m3u")
|
||||
|
||||
|
||||
@app.route("/content")
|
||||
def show_content():
|
||||
return get_result_file_content(show_content=True)
|
||||
|
||||
|
||||
@app.route("/log")
|
||||
def show_log():
|
||||
log_path = resource_path(constants.sort_log_path)
|
||||
if os.path.exists(log_path):
|
||||
with open(log_path, "r", encoding="utf-8") as file:
|
||||
content = file.read()
|
||||
else:
|
||||
content = constants.waiting_tip
|
||||
return render_template_string(
|
||||
"<head><link rel='icon' href='{{ url_for('static', filename='images/favicon.ico') }}' type='image/x-icon'></head><pre>{{ content }}</pre>",
|
||||
content=content,
|
||||
)
|
||||
|
||||
|
||||
def run_service():
|
||||
try:
|
||||
if not os.environ.get("GITHUB_ACTIONS"):
|
||||
ip_address = get_ip_address()
|
||||
print(f"📄 Result content: {ip_address}/content")
|
||||
print(f"📄 Log content: {ip_address}/log")
|
||||
print(f"🚀 M3u api: {ip_address}/m3u")
|
||||
print(f"🚀 Txt api: {ip_address}/txt")
|
||||
print(f"✅ You can use this url to watch IPTV 📺: {ip_address}")
|
||||
app.run(host="0.0.0.0", port=8000)
|
||||
except Exception as e:
|
||||
print(f"❌ Service start failed: {e}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_service()
|
|
@ -44,14 +44,14 @@ class AboutUI:
|
|||
project_label.pack()
|
||||
project_link = tk.Label(
|
||||
project_row_column2,
|
||||
text="https://github.com/Guovin/TV",
|
||||
text="https://github.com/Guovin/iptv-api",
|
||||
fg="blue",
|
||||
cursor="hand2",
|
||||
)
|
||||
project_link.pack()
|
||||
project_link.bind(
|
||||
"<Button-1>",
|
||||
lambda e: webbrowser.open_new_tab("https://github.com/Guovin/TV"),
|
||||
lambda e: webbrowser.open_new_tab("https://github.com/Guovin/iptv-api"),
|
||||
)
|
||||
|
||||
disclaimer_label = tk.Label(
|
||||
|
|
|
@ -7,7 +7,7 @@ from tkinter import messagebox
|
|||
from PIL import Image, ImageTk
|
||||
from utils.config import config
|
||||
from utils.tools import resource_path
|
||||
from main import UpdateSource, run_service
|
||||
from main import UpdateSource
|
||||
import asyncio
|
||||
import threading
|
||||
import webbrowser
|
||||
|
@ -114,15 +114,10 @@ class TkinterUI:
|
|||
def on_run_update(self):
|
||||
loop = asyncio.new_event_loop()
|
||||
|
||||
async def run_service_async():
|
||||
loop.run_in_executor(None, run_service)
|
||||
|
||||
def run_loop():
|
||||
asyncio.set_event_loop(loop)
|
||||
loop.run_until_complete(self.run_update())
|
||||
|
||||
if config.open_service:
|
||||
asyncio.run(run_service_async())
|
||||
self.thread = threading.Thread(target=run_loop, daemon=True)
|
||||
self.thread.start()
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ exe = EXE(
|
|||
a.binaries,
|
||||
a.datas,
|
||||
[],
|
||||
name='iptv-update-tool',
|
||||
name='IPTV-API',
|
||||
debug=True,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
|
|
|
@ -3,7 +3,6 @@ from time import time
|
|||
from requests import get
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
import updates.fofa.fofa_map as fofa_map
|
||||
from driver.setup import setup_driver
|
||||
import re
|
||||
from utils.config import config
|
||||
import utils.constants as constants
|
||||
|
@ -87,6 +86,8 @@ async def get_channels_by_fofa(urls=None, multicast=False, callback=None):
|
|||
proxy = None
|
||||
open_proxy = config.open_proxy
|
||||
open_driver = config.open_driver
|
||||
if open_driver:
|
||||
from driver.setup import setup_driver
|
||||
open_sort = config.open_sort
|
||||
if open_proxy:
|
||||
test_url = fofa_urls[0][0]
|
||||
|
@ -214,7 +215,7 @@ def process_fofa_json_url(url, region, open_sort, hotel_name="酒店源"):
|
|||
total_url = (
|
||||
add_url_info(
|
||||
f"{url}{item_url}",
|
||||
f"{region}{hotel_name}|cache:{url}",
|
||||
f"{region}{hotel_name}-cache:{url}",
|
||||
)
|
||||
if open_sort
|
||||
else add_url_info(
|
||||
|
|
|
@ -13,7 +13,6 @@ from utils.retry import (
|
|||
retry_func,
|
||||
find_clickable_element_with_retry,
|
||||
)
|
||||
from selenium.webdriver.common.by import By
|
||||
from tqdm.asyncio import tqdm_asyncio
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
from requests_custom.utils import get_soup_requests, close_session
|
||||
|
@ -23,6 +22,12 @@ from updates.subscribe import get_channels_by_subscribe_urls
|
|||
from collections import defaultdict
|
||||
import updates.fofa.fofa_map as fofa_map
|
||||
|
||||
if config.open_driver:
|
||||
try:
|
||||
from selenium.webdriver.common.by import By
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
async def get_channels_by_hotel(callback=None):
|
||||
"""
|
||||
|
|
|
@ -18,7 +18,6 @@ from utils.retry import (
|
|||
retry_func,
|
||||
find_clickable_element_with_retry,
|
||||
)
|
||||
from selenium.webdriver.common.by import By
|
||||
from tqdm.asyncio import tqdm_asyncio
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
from requests_custom.utils import get_soup_requests, close_session
|
||||
|
@ -27,6 +26,12 @@ from urllib.parse import parse_qs
|
|||
from collections import defaultdict
|
||||
from .update_tmp import get_multicast_region_result_by_rtp_txt
|
||||
|
||||
if config.open_driver:
|
||||
try:
|
||||
from selenium.webdriver.common.by import By
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
async def get_channels_by_multicast(names, callback=None):
|
||||
"""
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
from asyncio import create_task, gather
|
||||
from utils.config import config
|
||||
import utils.constants as constants
|
||||
from utils.speed import get_speed
|
||||
from utils.channel import (
|
||||
format_channel_name,
|
||||
get_results_from_soup,
|
||||
|
@ -22,11 +20,16 @@ from utils.retry import (
|
|||
retry_func,
|
||||
find_clickable_element_with_retry,
|
||||
)
|
||||
from selenium.webdriver.common.by import By
|
||||
from tqdm.asyncio import tqdm_asyncio
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from requests_custom.utils import get_soup_requests, close_session
|
||||
|
||||
if config.open_driver:
|
||||
try:
|
||||
from selenium.webdriver.common.by import By
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
async def get_channels_by_online_search(names, callback=None):
|
||||
"""
|
||||
|
|
|
@ -2,7 +2,7 @@ from asyncio import Semaphore
|
|||
from tqdm import tqdm
|
||||
from tqdm.asyncio import tqdm_asyncio
|
||||
from utils.config import config
|
||||
from utils.speed import get_speed
|
||||
from utils.speed import get_speed_requests
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from driver.utils import get_soup_driver
|
||||
from requests_custom.utils import get_soup_requests, close_session
|
||||
|
@ -71,7 +71,7 @@ async def get_proxy_list_with_test(base_url, proxy_list):
|
|||
|
||||
async def get_speed_task(url, timeout, proxy):
|
||||
async with semaphore:
|
||||
return await get_speed(url, timeout=timeout, proxy=proxy)
|
||||
return await get_speed_requests(url, timeout=timeout, proxy=proxy)
|
||||
|
||||
response_times = await tqdm_asyncio.gather(
|
||||
*(get_speed_task(base_url, timeout=30, proxy=url) for url in proxy_list),
|
||||
|
|
172
utils/channel.py
172
utils/channel.py
|
@ -8,54 +8,23 @@ from utils.tools import (
|
|||
remove_cache_info,
|
||||
resource_path,
|
||||
write_content_into_txt,
|
||||
get_logger,
|
||||
)
|
||||
from utils.speed import (
|
||||
get_speed,
|
||||
sort_urls_by_speed_and_resolution,
|
||||
is_ffmpeg_installed,
|
||||
speed_cache,
|
||||
)
|
||||
import os
|
||||
from collections import defaultdict
|
||||
import re
|
||||
from bs4 import NavigableString
|
||||
import logging
|
||||
from logging.handlers import RotatingFileHandler
|
||||
from opencc import OpenCC
|
||||
import asyncio
|
||||
import base64
|
||||
import pickle
|
||||
import copy
|
||||
import datetime
|
||||
|
||||
handler = None
|
||||
|
||||
|
||||
def setup_logging():
|
||||
"""
|
||||
Setup logging
|
||||
"""
|
||||
global handler
|
||||
if not os.path.exists(constants.output_dir):
|
||||
os.makedirs(constants.output_dir)
|
||||
handler = RotatingFileHandler(constants.log_path, encoding="utf-8")
|
||||
logging.basicConfig(
|
||||
handlers=[handler],
|
||||
format="%(message)s",
|
||||
level=logging.INFO,
|
||||
)
|
||||
|
||||
|
||||
def cleanup_logging():
|
||||
"""
|
||||
Cleanup logging
|
||||
"""
|
||||
global handler
|
||||
if handler:
|
||||
for handler in logging.root.handlers[:]:
|
||||
handler.close()
|
||||
logging.root.removeHandler(handler)
|
||||
if os.path.exists(constants.log_path):
|
||||
os.remove(constants.log_path)
|
||||
import asyncio
|
||||
from logging import INFO
|
||||
|
||||
|
||||
def get_name_url(content, pattern, multiline=False, check_url=True):
|
||||
|
@ -117,7 +86,7 @@ def get_channel_items():
|
|||
)
|
||||
|
||||
if config.open_use_old_result:
|
||||
result_cache_path = resource_path("output/result_cache.pkl")
|
||||
result_cache_path = resource_path(constants.cache_path)
|
||||
if os.path.exists(result_cache_path):
|
||||
with open(result_cache_path, "rb") as file:
|
||||
old_result = pickle.load(file)
|
||||
|
@ -258,7 +227,7 @@ def get_channel_multicast_result(result, search_result):
|
|||
(
|
||||
add_url_info(
|
||||
f"http://{url}/rtp/{ip}",
|
||||
f"{result_region}{result_type}{multicast_name}|cache:{url}",
|
||||
f"{result_region}{result_type}{multicast_name}-cache:{url}",
|
||||
)
|
||||
if config.open_sort
|
||||
else add_url_info(
|
||||
|
@ -463,9 +432,7 @@ def init_info_data(data, cate, name):
|
|||
data[cate][name] = []
|
||||
|
||||
|
||||
def append_data_to_info_data(
|
||||
info_data, cate, name, data, origin=None, check=True, insert=False
|
||||
):
|
||||
def append_data_to_info_data(info_data, cate, name, data, origin=None, check=True):
|
||||
"""
|
||||
Append channel data to total info data
|
||||
"""
|
||||
|
@ -486,14 +453,7 @@ def append_data_to_info_data(
|
|||
or (not check)
|
||||
or (check and check_url_by_patterns(pure_url))
|
||||
):
|
||||
if insert:
|
||||
info_data[cate][name].insert(
|
||||
0, (url, date, resolution, url_origin)
|
||||
)
|
||||
else:
|
||||
info_data[cate][name].append(
|
||||
(url, date, resolution, url_origin)
|
||||
)
|
||||
info_data[cate][name].append((url, date, resolution, url_origin))
|
||||
urls.append(pure_url)
|
||||
except:
|
||||
continue
|
||||
|
@ -585,115 +545,45 @@ def append_total_data(
|
|||
)
|
||||
|
||||
|
||||
async def sort_channel_list(
|
||||
cate,
|
||||
name,
|
||||
info_list,
|
||||
semaphore,
|
||||
ffmpeg=False,
|
||||
ipv6_proxy=None,
|
||||
callback=None,
|
||||
):
|
||||
"""
|
||||
Sort the channel list
|
||||
"""
|
||||
async with semaphore:
|
||||
data = []
|
||||
try:
|
||||
if info_list:
|
||||
sorted_data = await sort_urls_by_speed_and_resolution(
|
||||
info_list, ffmpeg=ffmpeg, ipv6_proxy=ipv6_proxy, callback=callback
|
||||
)
|
||||
if sorted_data:
|
||||
for (url, date, resolution, origin), response_time in sorted_data:
|
||||
logging.info(
|
||||
f"Name: {name}, URL: {url}, Date: {date}, Resolution: {resolution}, Response Time: {response_time} ms"
|
||||
)
|
||||
data.append((url, date, resolution, origin))
|
||||
except Exception as e:
|
||||
logging.error(f"Error: {e}")
|
||||
finally:
|
||||
return {"cate": cate, "name": name, "data": data}
|
||||
|
||||
|
||||
async def process_sort_channel_list(data, ipv6=False, callback=None):
|
||||
"""
|
||||
Processs the sort channel list
|
||||
"""
|
||||
ipv6_proxy = None if (not config.open_ipv6 or ipv6) else constants.ipv6_proxy
|
||||
ffmpeg_installed = is_ffmpeg_installed()
|
||||
if config.open_ffmpeg and not ffmpeg_installed:
|
||||
print("FFmpeg is not installed, using requests for sorting.")
|
||||
is_ffmpeg = config.open_ffmpeg and ffmpeg_installed
|
||||
semaphore = asyncio.Semaphore(5)
|
||||
need_sort_data = copy.deepcopy(data)
|
||||
process_nested_dict(need_sort_data, seen=set(), flag=r"cache:(.*)", force_str="!")
|
||||
result = {}
|
||||
semaphore = asyncio.Semaphore(10)
|
||||
|
||||
async def limited_get_speed(info, ipv6_proxy, callback):
|
||||
async with semaphore:
|
||||
return await get_speed(info[0], ipv6_proxy=ipv6_proxy, callback=callback)
|
||||
|
||||
tasks = [
|
||||
asyncio.create_task(
|
||||
sort_channel_list(
|
||||
cate,
|
||||
name,
|
||||
info_list,
|
||||
semaphore,
|
||||
ffmpeg=is_ffmpeg,
|
||||
limited_get_speed(
|
||||
info,
|
||||
ipv6_proxy=ipv6_proxy,
|
||||
callback=callback,
|
||||
)
|
||||
)
|
||||
for cate, channel_obj in need_sort_data.items()
|
||||
for name, info_list in channel_obj.items()
|
||||
for channel_obj in need_sort_data.values()
|
||||
for info_list in channel_obj.values()
|
||||
for info in info_list
|
||||
]
|
||||
sort_results = await asyncio.gather(*tasks)
|
||||
sort_data = {}
|
||||
for result in sort_results:
|
||||
if result:
|
||||
cate, name, result_data = result["cate"], result["name"], result["data"]
|
||||
append_data_to_info_data(sort_data, cate, name, result_data, check=False)
|
||||
await asyncio.gather(*tasks)
|
||||
logger = get_logger(constants.sort_log_path, level=INFO, init=True)
|
||||
for cate, obj in data.items():
|
||||
for name, info_list in obj.items():
|
||||
sort_info_list = sort_data.get(cate, {}).get(name, [])
|
||||
sort_urls = {
|
||||
remove_cache_info(sort_url[0])
|
||||
for sort_url in sort_info_list
|
||||
if sort_url and sort_url[0]
|
||||
}
|
||||
for url, date, resolution, origin in info_list:
|
||||
if "$" in url:
|
||||
info = url.partition("$")[2]
|
||||
if info and info.startswith("!"):
|
||||
append_data_to_info_data(
|
||||
sort_data,
|
||||
cate,
|
||||
name,
|
||||
[(url, date, resolution, origin)],
|
||||
check=False,
|
||||
insert=True,
|
||||
)
|
||||
continue
|
||||
matcher = re.search(r"cache:(.*)", info)
|
||||
if matcher:
|
||||
cache_key = matcher.group(1)
|
||||
if not cache_key:
|
||||
continue
|
||||
url = remove_cache_info(url)
|
||||
if url in sort_urls or cache_key not in speed_cache:
|
||||
continue
|
||||
cache = speed_cache[cache_key]
|
||||
if not cache:
|
||||
continue
|
||||
response_time, resolution = cache
|
||||
if response_time and response_time != float("inf"):
|
||||
append_data_to_info_data(
|
||||
sort_data,
|
||||
cate,
|
||||
name,
|
||||
[(url, date, resolution, origin)],
|
||||
check=False,
|
||||
)
|
||||
logging.info(
|
||||
f"Name: {name}, URL: {url}, Date: {date}, Resolution: {resolution}, Response Time: {response_time} ms"
|
||||
)
|
||||
return sort_data
|
||||
info_list = sort_urls_by_speed_and_resolution(name, info_list, logger)
|
||||
append_data_to_info_data(
|
||||
result,
|
||||
cate,
|
||||
name,
|
||||
info_list,
|
||||
check=False,
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
def write_channel_to_file(data, ipv6=False, callback=None):
|
||||
|
|
|
@ -270,7 +270,9 @@ class ConfigManager:
|
|||
|
||||
@property
|
||||
def open_driver(self):
|
||||
return self.config.getboolean("Settings", "open_driver", fallback=True)
|
||||
return not os.environ.get("LITE") and self.config.getboolean(
|
||||
"Settings", "open_driver", fallback=True
|
||||
)
|
||||
|
||||
@property
|
||||
def hotel_page_num(self):
|
||||
|
|
|
@ -2,9 +2,13 @@ import os
|
|||
|
||||
output_dir = "output"
|
||||
|
||||
log_file = "result_new.log"
|
||||
result_path = os.path.join(output_dir, "result_new.txt")
|
||||
|
||||
log_path = os.path.join(output_dir, log_file)
|
||||
cache_path = os.path.join(output_dir, "cache.pkl")
|
||||
|
||||
sort_log_path = os.path.join(output_dir, "sort.log")
|
||||
|
||||
log_path = os.path.join(output_dir, "log.log")
|
||||
|
||||
url_pattern = r"((https?):\/\/)?(\[[0-9a-fA-F:]+\]|([\w-]+\.)+[\w-]+)(:[0-9]{1,5})?(\/[^\s]*)?(\$[^\s]+)?"
|
||||
|
||||
|
@ -95,4 +99,4 @@ foodie_url = "http://www.foodieguide.com/iptvsearch/"
|
|||
|
||||
foodie_hotel_url = "http://www.foodieguide.com/iptvsearch/hoteliptv.php"
|
||||
|
||||
waiting_tip = "🔍️正在更新,请耐心等待更新完成..."
|
||||
waiting_tip = "🔍️未找到结果文件,若已启动更新,请耐心等待更新完成..."
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
from time import sleep
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from selenium.common.exceptions import TimeoutException
|
||||
from utils.config import config
|
||||
|
||||
if config.open_driver:
|
||||
try:
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from selenium.common.exceptions import TimeoutException
|
||||
except:
|
||||
pass
|
||||
|
||||
max_retries = 2
|
||||
|
||||
|
||||
|
|
169
utils/speed.py
169
utils/speed.py
|
@ -3,13 +3,62 @@ from time import time
|
|||
import asyncio
|
||||
import re
|
||||
from utils.config import config
|
||||
from utils.tools import is_ipv6, add_url_info, remove_cache_info, get_resolution_value
|
||||
import utils.constants as constants
|
||||
from utils.tools import is_ipv6, remove_cache_info, get_resolution_value, get_logger
|
||||
import subprocess
|
||||
import yt_dlp
|
||||
from concurrent.futures import ProcessPoolExecutor
|
||||
import functools
|
||||
|
||||
logger = get_logger(constants.log_path)
|
||||
|
||||
|
||||
async def get_speed(url, timeout=config.sort_timeout, proxy=None):
|
||||
def get_info_yt_dlp(url, timeout=config.sort_timeout):
|
||||
"""
|
||||
Get the speed of the url
|
||||
Get the url info by yt_dlp
|
||||
"""
|
||||
ydl_opts = {
|
||||
"socket_timeout": timeout,
|
||||
"skip_download": True,
|
||||
"quiet": True,
|
||||
"no_warnings": True,
|
||||
"format": "best",
|
||||
"logger": logger,
|
||||
}
|
||||
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
||||
return ydl.sanitize_info(ydl.extract_info(url, download=False))
|
||||
|
||||
|
||||
async def get_speed_yt_dlp(url, timeout=config.sort_timeout):
|
||||
"""
|
||||
Get the speed of the url by yt_dlp
|
||||
"""
|
||||
try:
|
||||
async with asyncio.timeout(timeout + 2):
|
||||
start_time = time()
|
||||
loop = asyncio.get_running_loop()
|
||||
with ProcessPoolExecutor() as exc:
|
||||
info = await loop.run_in_executor(
|
||||
exc, functools.partial(get_info_yt_dlp, url, timeout)
|
||||
)
|
||||
fps = (
|
||||
int(round((time() - start_time) * 1000))
|
||||
if len(info)
|
||||
else float("inf")
|
||||
)
|
||||
resolution = (
|
||||
f"{info['width']}x{info['height']}"
|
||||
if "width" in info and "height" in info
|
||||
else None
|
||||
)
|
||||
return (fps, resolution)
|
||||
except:
|
||||
return (float("inf"), None)
|
||||
|
||||
|
||||
async def get_speed_requests(url, timeout=config.sort_timeout, proxy=None):
|
||||
"""
|
||||
Get the speed of the url by requests
|
||||
"""
|
||||
async with ClientSession(
|
||||
connector=TCPConnector(verify_ssl=False), trust_env=True
|
||||
|
@ -113,15 +162,11 @@ async def check_stream_speed(url_info):
|
|||
speed_cache = {}
|
||||
|
||||
|
||||
async def get_speed_by_info(
|
||||
url_info, ffmpeg, semaphore, ipv6_proxy=None, callback=None
|
||||
):
|
||||
async def get_speed(url, ipv6_proxy=None, callback=None):
|
||||
"""
|
||||
Get the info with speed
|
||||
Get the speed of the url
|
||||
"""
|
||||
async with semaphore:
|
||||
url, _, resolution, _ = url_info
|
||||
url_info = list(url_info)
|
||||
try:
|
||||
cache_key = None
|
||||
url_is_ipv6 = is_ipv6(url)
|
||||
if "$" in url:
|
||||
|
@ -129,69 +174,59 @@ async def get_speed_by_info(
|
|||
matcher = re.search(r"cache:(.*)", cache_info)
|
||||
if matcher:
|
||||
cache_key = matcher.group(1)
|
||||
url_show_info = remove_cache_info(cache_info)
|
||||
url_info[0] = url
|
||||
if cache_key in speed_cache:
|
||||
speed = speed_cache[cache_key][0]
|
||||
url_info[2] = speed_cache[cache_key][1]
|
||||
if speed != float("inf"):
|
||||
if url_show_info:
|
||||
url_info[0] = add_url_info(url, url_show_info)
|
||||
return (tuple(url_info), speed)
|
||||
else:
|
||||
return float("inf")
|
||||
try:
|
||||
if ipv6_proxy and url_is_ipv6:
|
||||
url_speed = 0
|
||||
speed = (url_info, url_speed)
|
||||
elif ffmpeg:
|
||||
speed = await check_stream_speed(url_info)
|
||||
url_speed = speed[1] if speed != float("inf") else float("inf")
|
||||
if url_speed == float("inf"):
|
||||
url_speed = await get_speed(url)
|
||||
resolution = speed[0][2] if speed != float("inf") else None
|
||||
else:
|
||||
url_speed = await get_speed(url)
|
||||
speed = (
|
||||
(url_info, url_speed) if url_speed != float("inf") else float("inf")
|
||||
)
|
||||
if cache_key and cache_key not in speed_cache:
|
||||
speed_cache[cache_key] = (url_speed, resolution)
|
||||
if url_show_info:
|
||||
speed[0][0] = add_url_info(speed[0][0], url_show_info)
|
||||
speed = (tuple(speed[0]), speed[1])
|
||||
return speed
|
||||
except:
|
||||
return float("inf")
|
||||
finally:
|
||||
if callback:
|
||||
callback()
|
||||
return speed_cache[cache_key][0]
|
||||
if ipv6_proxy and url_is_ipv6:
|
||||
speed = 0
|
||||
else:
|
||||
speed = await get_speed_yt_dlp(url)
|
||||
if cache_key and cache_key not in speed_cache:
|
||||
speed_cache[cache_key] = speed
|
||||
return speed
|
||||
except:
|
||||
return float("inf")
|
||||
finally:
|
||||
if callback:
|
||||
callback()
|
||||
|
||||
|
||||
async def sort_urls_by_speed_and_resolution(
|
||||
data, ffmpeg=False, ipv6_proxy=None, callback=None
|
||||
):
|
||||
def sort_urls_by_speed_and_resolution(name, data, logger=None):
|
||||
"""
|
||||
Sort by speed and resolution
|
||||
"""
|
||||
semaphore = asyncio.Semaphore(20)
|
||||
response = await asyncio.gather(
|
||||
*(
|
||||
get_speed_by_info(
|
||||
url_info, ffmpeg, semaphore, ipv6_proxy=ipv6_proxy, callback=callback
|
||||
)
|
||||
for url_info in data
|
||||
)
|
||||
)
|
||||
valid_response = [res for res in response if res != float("inf")]
|
||||
filter_data = []
|
||||
for url, date, resolution, origin in data:
|
||||
if origin == "important":
|
||||
filter_data.append((url, date, resolution, origin))
|
||||
continue
|
||||
cache_key_match = re.search(r"cache:(.*)", url.partition("$")[2])
|
||||
cache_key = cache_key_match.group(1) if cache_key_match else None
|
||||
if cache_key and cache_key in speed_cache:
|
||||
cache = speed_cache[cache_key]
|
||||
if cache:
|
||||
response_time, cache_resolution = cache
|
||||
resolution = cache_resolution or resolution
|
||||
if response_time != float("inf"):
|
||||
url = remove_cache_info(url)
|
||||
try:
|
||||
if logger:
|
||||
logger.info(
|
||||
f"Name: {name}, URL: {url}, Date: {date}, Resolution: {resolution}, Response Time: {response_time} ms"
|
||||
)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
filter_data.append((url, date, resolution, origin))
|
||||
|
||||
def combined_key(item):
|
||||
(_, _, resolution, _), response_time = item
|
||||
resolution_value = get_resolution_value(resolution) if resolution else 0
|
||||
return (
|
||||
-(config.response_time_weight * response_time)
|
||||
+ config.resolution_weight * resolution_value
|
||||
)
|
||||
_, _, resolution, origin = item
|
||||
if origin == "important":
|
||||
return -float("inf")
|
||||
else:
|
||||
resolution_value = get_resolution_value(resolution) if resolution else 0
|
||||
return (
|
||||
config.response_time_weight * response_time
|
||||
- config.resolution_weight * resolution_value
|
||||
)
|
||||
|
||||
sorted_res = sorted(valid_response, key=combined_key, reverse=True)
|
||||
return sorted_res
|
||||
filter_data.sort(key=combined_key)
|
||||
return filter_data
|
||||
|
|
|
@ -3,7 +3,6 @@ import datetime
|
|||
import os
|
||||
import urllib.parse
|
||||
import ipaddress
|
||||
from urllib.parse import urlparse
|
||||
import socket
|
||||
from utils.config import config
|
||||
import utils.constants as constants
|
||||
|
@ -13,6 +12,23 @@ from flask import render_template_string, send_file
|
|||
import shutil
|
||||
import requests
|
||||
import sys
|
||||
import logging
|
||||
from logging.handlers import RotatingFileHandler
|
||||
|
||||
|
||||
def get_logger(path, level=logging.ERROR, init=False):
|
||||
"""
|
||||
get the logger
|
||||
"""
|
||||
if not os.path.exists(constants.output_dir):
|
||||
os.makedirs(constants.output_dir)
|
||||
if init and os.path.exists(path):
|
||||
os.remove(path)
|
||||
handler = RotatingFileHandler(path, encoding="utf-8")
|
||||
logger = logging.getLogger(path)
|
||||
logger.addHandler(handler)
|
||||
logger.setLevel(level)
|
||||
return logger
|
||||
|
||||
|
||||
def format_interval(t):
|
||||
|
@ -376,20 +392,26 @@ def convert_to_m3u():
|
|||
m3u_file_path = os.path.splitext(user_final_file)[0] + ".m3u"
|
||||
with open(m3u_file_path, "w", encoding="utf-8") as m3u_file:
|
||||
m3u_file.write(m3u_output)
|
||||
print(f"✅ Result m3u file generated at: {m3u_file_path}")
|
||||
print(f"✅ M3U result file generated at: {m3u_file_path}")
|
||||
|
||||
|
||||
def get_result_file_content(show_result=False):
|
||||
def get_result_file_content(show_content=False, file_type=None):
|
||||
"""
|
||||
Get the content of the result file
|
||||
"""
|
||||
user_final_file = resource_path(config.final_file)
|
||||
if os.path.exists(user_final_file):
|
||||
result_file = (
|
||||
os.path.splitext(user_final_file)[0] + f".{file_type}"
|
||||
if file_type
|
||||
else user_final_file
|
||||
)
|
||||
if os.path.exists(result_file):
|
||||
if config.open_m3u_result:
|
||||
user_final_file = os.path.splitext(user_final_file)[0] + ".m3u"
|
||||
if show_result == False:
|
||||
return send_file(user_final_file, as_attachment=True)
|
||||
with open(user_final_file, "r", encoding="utf-8") as file:
|
||||
if file_type == "m3u" or not file_type:
|
||||
result_file = os.path.splitext(user_final_file)[0] + ".m3u"
|
||||
if file_type != "txt" and show_content == False:
|
||||
return send_file(result_file, as_attachment=True)
|
||||
with open(result_file, "r", encoding="utf-8") as file:
|
||||
content = file.read()
|
||||
else:
|
||||
content = constants.waiting_tip
|
||||
|
@ -452,7 +474,7 @@ def add_url_info(url, info):
|
|||
Add url info to the URL
|
||||
"""
|
||||
if info:
|
||||
separator = "|" if "$" in url else "$"
|
||||
separator = "-" if "$" in url else "$"
|
||||
url += f"{separator}{info}"
|
||||
return url
|
||||
|
||||
|
@ -469,7 +491,7 @@ def remove_cache_info(str):
|
|||
"""
|
||||
Remove the cache info from the string
|
||||
"""
|
||||
return re.sub(r"cache:.*|\|cache:.*", "", str)
|
||||
return re.sub(r"[^a-zA-Z\u4e00-\u9fa5\$]?cache:.*", "", str)
|
||||
|
||||
|
||||
def resource_path(relative_path, persistent=False):
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"version": "1.5.2",
|
||||
"name": "IPTV电视直播源更新工具"
|
||||
"version": "1.5.4",
|
||||
"name": "IPTV-API"
|
||||
}
|
Loading…
Reference in a new issue