diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9369987e7b..a0a36944a7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,60 @@
# 更新日志(Changelog)
+## v1.5.6
+
+### 2024/12/16
+
+- ❤️ 推荐关注微信公众号(Govin),订阅更新通知与使用技巧等文章推送,还可进行答疑和交流讨论
+- ⚠️ 本次更新涉及配置变更,以最新 `config/config.ini` 为准,工作流用户需复制最新配置至`user_config.ini`
+- ✨ 新增白名单列表功能,支持自定义接口和订阅源关键字白名单,文件位于`config/whitelist.txt`,工作流用户为了避免冲突覆盖,建议文件重命名添加
+ `user_`前缀(#584,#599)
+- ✨ 新增黑名单列表功能,支持接口关键字黑名单,文件位于`config/blacklist.txt`,工作流用户为了避免冲突覆盖,建议文件重命名添加
+ `user_`前缀
+- ✨ 新增订阅源列表功能,文件位于`config/subscribe.txt`,工作流用户为了避免冲突覆盖,建议文件重命名添加`user_`前缀
+- ✨ 新增支持获取接口速率、最低速率过滤(`open_filter_speed`、`min_speed`)
+- ✨ 新增支持修改Docker服务端口环境变量(`APP_PORT`)(#619)
+- ✨ 新增jsdelivr代理地址,支持TLSv1.1 和 TLSv1.2 协议(#639)
+- ✨ 新增离线数据和网络数据查询开关(`open_use_cache`, `open_request`)
+- ✨ 新增控制是否使用离线数据和网络数据查询(`open_use_cache`、`open_request`)
+- ✨ 新增支持跳过检查是否支持ipv6(`ipv6_support`)
+- ✨ 调整GUI界面布局,新增测速设置页面,跳转编辑白/黑名单、订阅源列表文本
+- 🐛 修复部分m3u8接口测速导致任务超时(#621)
+- 🐛 修复GUI日志线程占用问题(#655)
+- 🐛 补充显示更新时间配置文档(#622)
+- 🪄 优化接口测速方法,移除`yt-dlp`(#621)
+- 🗑️ 移除配置:`open_ffmpeg`、`subscribe_urls`、`resolution_weight`、`response_time_weight`、`url_keywords_blacklist`
+
+
+ English
+
+- ❤️ Recommend following the WeChat public account (Govin) to subscribe to update notifications and articles on usage
+ tips, as well as for Q&A and discussion.
+- ⚠️ This update involves configuration changes. Refer to the latest `config/config.ini`. Workflow users need to copy
+ the latest configuration to `user_config.ini`.
+- ✨ Added whitelist feature, supporting custom interface and subscription source keyword whitelists. The file is located
+ at `config/whitelist.txt`. To avoid conflict, workflow users are advised to rename the file with a `user_` prefix (
+ #584, #599).
+- ✨ Added blacklist feature, supporting interface keyword blacklists. The file is located at `config/blacklist.txt`. To
+ avoid conflict, workflow users are advised to rename the file with a `user_` prefix.
+- ✨ Added subscription source list feature. The file is located at `config/subscribe.txt`. To avoid conflict, workflow
+ users are advised to rename the file with a `user_` prefix.
+- ✨ Added support for fetching interface speed and minimum speed filtering (`open_filter_speed`, `min_speed`).
+- ✨ Added support for modifying Docker server port environment variable (`APP_PORT`) (#619).
+- ✨ Added jsdelivr proxy address, supporting TLSv1.1 and TLSv1.2 protocols (#639).
+- ✨ Added switches for offline data and network data queries (`open_use_cache`, `open_request`).
+- ✨ Added control for whether to use offline data and network data queries (`open_use_cache`, `open_request`).
+- ✨ Added support for skipping the check for IPv6 support (`ipv6_support`).
+- ✨ Adjusted GUI layout, added speed test settings page, and links to edit whitelist/blacklist and subscription source
+ list text files.
+- 🐛 Fixed issue where some m3u8 interface speed tests caused task timeouts (#621).
+- 🐛 Fixed GUI log thread occupation issue (#655).
+- 🐛 Added display of update time in configuration documentation (#622).
+- 🪄 Optimized interface speed test method, removed `yt-dlp` (#621).
+- 🗑️ Removed configurations: `open_ffmpeg`, `subscribe_urls`, `resolution_weight`, `response_time_weight`,
+ `url_keywords_blacklist`.
+
+
+
## v1.5.5
### 2024/12/2
diff --git a/README.md b/README.md
index c3b1ce625a..508ae522ee 100644
--- a/README.md
+++ b/README.md
@@ -110,16 +110,6 @@
- 接口源:
-```bash
-https://ghp.ci/raw.githubusercontent.com/Guovin/iptv-api/gd/output/result.m3u
-```
-
-```bash
-https://ghp.ci/raw.githubusercontent.com/Guovin/iptv-api/gd/output/result.txt
-```
-
-或
-
```bash
https://cdn.jsdelivr.net/gh/Guovin/iptv-api@gd/output/result.m3u
```
@@ -130,12 +120,6 @@ https://cdn.jsdelivr.net/gh/Guovin/iptv-api@gd/output/result.txt
- 数据源:
-```bash
-https://ghp.ci/raw.githubusercontent.com/Guovin/iptv-api/gd/source.json
-```
-
-或
-
```bash
https://cdn.jsdelivr.net/gh/Guovin/iptv-api@gd/source.json
```
@@ -144,9 +128,8 @@ https://cdn.jsdelivr.net/gh/Guovin/iptv-api@gd/source.json
| 配置项 | 描述 | 默认值 |
|:-----------------------|:------------------------------------------------------------------------------------------|:----------------------------------------|
-| open_driver | 开启浏览器运行,若更新无数据可开启此模式,较消耗性能 | True |
+| open_driver | 开启浏览器运行,若更新无数据可开启此模式,较消耗性能 | False |
| open_empty_category | 开启无结果频道分类,自动归类至底部 | False |
-| open_ffmpeg | 开启使用 FFmpeg 进行测速,获取更准确的速度与分辨率信息,需要提前手动安装 | True |
| open_filter_resolution | 开启分辨率过滤,低于最小分辨率(min_resolution)的接口将会被过滤 | True |
| open_filter_speed | 开启速率过滤,低于最小速率(min_speed)的接口将会被过滤 | True |
| open_hotel | 开启酒店源功能,关闭后所有酒店源工作模式都将关闭 | True |
@@ -159,22 +142,22 @@ https://cdn.jsdelivr.net/gh/Guovin/iptv-api@gd/source.json
| open_multicast_fofa | 开启 FOFA 组播源工作模式 | True |
| open_online_search | 开启关键字搜索源功能 | False |
| open_proxy | 开启代理,自动获取免费可用代理,若更新无数据可开启此模式 | False |
-| open_request | 开启查询请求,数据来源于网络 | False |
+| open_request | 开启查询请求,数据来源于网络(仅针对酒店源与组播源) | False |
| open_service | 开启页面服务,用于控制是否启动结果页面服务;如果使用青龙等平台部署,有专门设定的定时任务,需要更新完成后停止运行,可以关闭该功能 | True |
| open_sort | 开启排序功能(响应速度、日期、分辨率) | True |
| open_subscribe | 开启订阅源功能 | False |
| open_update | 开启更新,用于控制是否更新接口,若关闭则所有工作模式(获取接口和测速)均停止 | True |
| open_update_time | 开启显示更新时间 | True |
| open_url_info | 开启显示接口说明信息,用于控制是否显示分辨率、接口协议类型等信息,为$符号后的内容,播放软件使用该信息对接口进行描述 | True |
-| open_use_cache | 开启使用本地缓存数据,适用于查询请求失败场景 | True |
+| open_use_cache | 开启使用本地缓存数据,适用于查询请求失败场景(仅针对酒店源与组播源) | True |
| open_use_old_result | 开启使用历史更新结果(包含模板与结果文件的接口),合并至本次更新中 | True |
-| delay_weight | 响应时间权重值(所有权重值总和应为 1) | 0.25 |
| final_file | 生成结果文件路径 | output/result.txt |
| hotel_num | 结果中偏好的酒店源接口数量 | 4 |
| hotel_page_num | 酒店地区获取分页数量 | 1 |
| hotel_region_list | 酒店源地区列表,"全部"表示所有地区 | 全部 |
| ipv4_num | 结果中偏好的 IPv4 接口数量 | 5 |
| ipv6_num | 结果中偏好的 IPv6 接口数量 | 5 |
+| ipv6_support | 强制认为当前网络支持IPv6,跳过检测 | False |
| ipv_type | 生成结果中接口的协议类型,可选值:ipv4、ipv6、全部、all | 全部 |
| ipv_type_prefer | 接口协议类型偏好,优先将该类型的接口排在结果前面,可选值:IPv4、IPv6、自动、auto | 自动 |
| min_resolution | 接口最小分辨率,需要开启 open_filter_resolution 才能生效 | 1920x1080 |
@@ -187,12 +170,9 @@ https://cdn.jsdelivr.net/gh/Guovin/iptv-api@gd/source.json
| origin_type_prefer | 结果偏好的接口来源,结果优先按该顺序进行排序,hotel:酒店源,multicast:组播源,subscribe:订阅源,online_search:关键字搜索 | hotel,multicast,subscribe,online_search |
| recent_days | 获取最近时间范围内更新的接口(单位天),适当减小可避免出现匹配问题 | 30 |
| request_timeout | 查询请求超时时长,单位秒(s),用于控制查询接口文本链接的超时时长以及重试时长,调整此值能优化更新时间 | 10 |
-| resolution_weight | 分辨率权重值 (所有权重值总和应为 1) | 0.25 |
| sort_timeout | 单个接口测速超时时长,单位秒(s);数值越大测速所属时间越长,能提高获取接口数量,但质量会有所下降;数值越小测速所需时间越短,能获取低延时的接口,质量较好;调整此值能优化更新时间 | 10 |
| source_file | 模板文件路径 | config/demo.txt |
-| speed_weight | 速率权重值(所有权重值总和应为 1) | 0.5 |
| subscribe_num | 结果中偏好的订阅源接口数量 | 3 |
-| url_keywords_blacklist | 接口关键字黑名单,用于过滤含特定字符的接口 | |
| urls_limit | 单个频道接口数量 | 10 |
## 快速上手
diff --git a/README_en.md b/README_en.md
index 8cc38408d8..81b0ba6600 100644
--- a/README_en.md
+++ b/README_en.md
@@ -110,16 +110,6 @@
- Interface source:
-```bash
-https://ghp.ci/raw.githubusercontent.com/Guovin/iptv-api/gd/output/result.m3u
-```
-
-```bash
-https://ghp.ci/raw.githubusercontent.com/Guovin/iptv-api/gd/output/result.txt
-```
-
-or
-
```bash
https://cdn.jsdelivr.net/gh/Guovin/iptv-api@gd/output/result.m3u
```
@@ -130,12 +120,6 @@ https://cdn.jsdelivr.net/gh/Guovin/iptv-api@gd/output/result.txt
- Data source:
-```bash
-https://ghp.ci/raw.githubusercontent.com/Guovin/iptv-api/gd/source.json
-```
-
-or
-
```bash
https://cdn.jsdelivr.net/gh/Guovin/iptv-api@gd/source.json
```
@@ -144,9 +128,8 @@ https://cdn.jsdelivr.net/gh/Guovin/iptv-api@gd/source.json
| Configuration Item | Description | Default Value |
|:-----------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------|
-| open_driver | Enable browser execution, If there are no updates, this mode can be enabled, which consumes more performance | True |
+| open_driver | Enable browser execution, If there are no updates, this mode can be enabled, which consumes more performance | False |
| open_empty_category | Enable the No Results Channel Category, which will automatically categorize channels without results to the bottom | False |
-| open_ffmpeg | Enable speed testing using FFmpeg to obtain more accurate speed and resolution information. Manual installation is required in advance. | True |
| open_filter_resolution | Enable resolution filtering, interfaces with resolution lower than the minimum resolution (min_resolution) will be filtered | True |
| open_filter_speed | Enable speed filtering, interfaces with speed lower than the minimum speed (min_speed) will be filtered | True |
| open_hotel | Enable the hotel source function, after closing it all hotel source working modes will be disabled | True |
@@ -159,22 +142,22 @@ https://cdn.jsdelivr.net/gh/Guovin/iptv-api@gd/source.json
| open_multicast_fofa | Enable FOFA multicast source work mode | True |
| open_online_search | Enable keyword search source feature | False |
| open_proxy | Enable proxy, automatically obtains free available proxies, If there are no updates, this mode can be enabled | False |
-| open_request | Enable query request, the data is obtained from the network | False |
+| open_request | Enable query request, the data is obtained from the network (only for hotel sources and multicast sources) | False |
| open_service | Enable page service, used to control whether to start the result page service; if deployed on platforms like Qinglong with dedicated scheduled tasks, the function can be turned off after updates are completed and the task is stopped | True |
| open_sort | Enable the sorting function (response speed, date, resolution) | True |
| open_subscribe | Enable subscription source feature | True |
| open_update | Enable updates, if disabled then only the result page service is run | True |
| open_update_time | Enable show update time | True |
| open_url_info | Enable display of API description information, used to control whether to show resolution, API protocol type, etc., the content after the $ symbol, playback software uses this information to describe the API | True |
-| open_use_cache | Enable the use of local cache data, applicable to the query request failure scenario | True |
+| open_use_cache | Enable the use of local cache data, applicable to the query request failure scenario (only for hotel sources and multicast sources) | True |
| open_use_old_result | Enable the use of historical update results (including the interface for template and result files) and merge them into the current update | True |
-| delay_weight | Response time weight value (the sum of all weight values should be 1) | 0.25 |
| final_file | Generated result file path | output/result.txt |
| hotel_num | The number of preferred hotel source interfaces in the results | 4 |
| hotel_page_num | Number of pages to retrieve for hotel regions | 1 |
| hotel_region_list | List of hotel source regions, 'all' indicates all regions | all |
| ipv4_num | The preferred number of IPv4 interfaces in the result | 5 |
| ipv6_num | The preferred number of IPv6 interfaces in the result | 5 |
+| ipv6_support | It is forced to consider that the current network supports IPv6 and skip the check | False |
| ipv_type | The protocol type of interface in the generated result, optional values: ipv4, ipv6, all | all |
| ipv_type_prefer | Interface protocol type preference, prioritize interfaces of this type in the results, optional values: IPv4, IPv6, auto | auto |
| min_resolution | Minimum interface resolution, requires enabling open_filter_resolution to take effect | 1920x1080 |
@@ -187,12 +170,9 @@ https://cdn.jsdelivr.net/gh/Guovin/iptv-api@gd/source.json
| origin_type_prefer | Result preference for the source of the interface, results are prioritized in this order: hotel: hotel source, multicast: multicast source, subscribe: subscription source, online_search: keyword search | hotel, multicast, subscribe, online_search |
| recent_days | Retrieve interfaces updated within a recent time range (in days), reducing appropriately can avoid matching issues | 30 |
| request_timeout | Query request timeout duration, in seconds (s), used to control the timeout and retry duration for querying interface text links. Adjusting this value can optimize update time. | 10 |
-| resolution_weight | Resolution weight value (the sum of all weight values should be 1) | 0.25 |
| sort_timeout | 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. | 10 |
| source_file | Template file path | config/demo.txt |
-| speed_weight | Speed weight value (the sum of all weight values should be 1) | 0.5 |
| subscribe_num | The number of preferred subscribe source interfaces in the results | 3 |
-| url_keywords_blacklist | Interface keyword blacklist, used to filter out interfaces containing specific characters | |
| urls_limit | Number of interfaces per channel | 10 |
## Quick Start
diff --git a/config/blacklist.txt b/config/blacklist.txt
new file mode 100644
index 0000000000..d5e79b2441
--- /dev/null
+++ b/config/blacklist.txt
@@ -0,0 +1,11 @@
+# 这是接口黑名单列表,符合关键字的接口将被拦截,一个关键字一行
+# This is the interface blacklist list, the interface matching the keyword will be blocked, one keyword line
+epg.pw
+skype.serv00.net
+iptv.yjxfz.com
+live-hls-web-ajb.getaj.net
+live.goodiptv.club
+hc73k3dhwo5gfkt.wcetv.com
+stream1.freetv.fun
+zw9999.cnstream.top
+zsntlqj.xicp.net
diff --git a/config/config.ini b/config/config.ini
index 6be19b1807..b71ba738b4 100644
--- a/config/config.ini
+++ b/config/config.ini
@@ -1,51 +1,47 @@
[Settings]
-open_service = True
-open_update = True
-open_use_old_result = True
-open_use_cache = True
-open_request = False
-source_file = config/demo.txt
-final_file = output/result.txt
-open_online_search = False
-online_search_page_num = 1
-urls_limit = 10
-open_keep_all = False
-open_sort = True
-sort_timeout = 10
-open_ffmpeg = True
-open_filter_speed = True
-min_speed = 0.2
+open_driver = False
+open_empty_category = False
open_filter_resolution = True
-min_resolution = 1920x1080
-delay_weight = 0.25
-speed_weight = 0.5
-resolution_weight = 0.25
-recent_days = 30
-ipv_type = 全部
-ipv_type_prefer = 自动
-ipv4_num = 5
-ipv6_num = 5
+open_filter_speed = True
+open_hotel = True
+open_hotel_foodie = True
+open_hotel_fofa = True
+open_keep_all = False
open_m3u_result = True
-url_keywords_blacklist = epg.pw,skype.serv00.net,iptv.yjxfz.com,live-hls-web-ajb.getaj.net,live.goodiptv.club,hc73k3dhwo5gfkt.wcetv.com,stream1.freetv.fun,zw9999.cnstream.top,zsntlqj.xicp.net
-open_subscribe = True
open_multicast = True
open_multicast_foodie = True
open_multicast_fofa = True
-multicast_region_list = 全部
-multicast_page_num = 1
+open_online_search = False
open_proxy = False
-open_driver = False
-open_hotel = True
-open_hotel_foodie = True
-open_hotel_fofa = True
-hotel_region_list = 全部
-hotel_page_num = 1
+open_request = False
+open_service = True
+open_sort = True
+open_subscribe = True
+open_update = True
open_update_time = True
-request_timeout = 10
-origin_type_prefer = hotel,multicast,subscribe,online_search
+open_url_info = True
+open_use_cache = True
+open_use_old_result = True
+final_file = output/result.txt
hotel_num = 4
+hotel_page_num = 1
+hotel_region_list = 全部
+ipv4_num = 5
+ipv6_num = 5
+ipv6_support = False
+ipv_type = 全部
+ipv_type_prefer = 自动
+min_resolution = 1920x1080
+min_speed = 0.2
multicast_num = 3
-subscribe_num = 3
+multicast_page_num = 1
+multicast_region_list = 全部
online_search_num = 0
-open_url_info = True
-open_empty_category = False
\ No newline at end of file
+online_search_page_num = 1
+origin_type_prefer = hotel,multicast,subscribe,online_search
+recent_days = 30
+request_timeout = 10
+sort_timeout = 10
+source_file = config/demo.txt
+subscribe_num = 3
+urls_limit = 10
\ No newline at end of file
diff --git a/config/subscribe.txt b/config/subscribe.txt
index 81ddce1127..755b6da565 100644
--- a/config/subscribe.txt
+++ b/config/subscribe.txt
@@ -1,19 +1,18 @@
# 这是订阅源列表,每行一个订阅地址
# This is a list of subscription sources, with one subscription address per line
-
https://iptv.b2og.com/txt/fmml_ipv6.txt
-https://ghp.ci/raw.githubusercontent.com/suxuang/myIPTV/main/ipv6.m3u
+https://cdn.jsdelivr.net/gh/suxuang/myIPTV@main/ipv6.m3u
https://live.zbds.top/tv/iptv6.txt
https://live.zbds.top/tv/iptv4.txt
https://live.fanmingming.com/tv/m3u/ipv6.m3u
-https://ghp.ci/https://raw.githubusercontent.com/joevess/IPTV/main/home.m3u8
+https://cdn.jsdelivr.net/gh/joevess/IPTV@main/home.m3u8
https://aktv.top/live.txt
http://175.178.251.183:6689/live.txt
-https://ghp.ci/https://raw.githubusercontent.com/kimwang1978/collect-tv-txt/main/merged_output.txt
+https://cdn.jsdelivr.net/gh/kimwang1978/collect-tv-txt@main/merged_output.txt
https://m3u.ibert.me/txt/fmml_dv6.txt
https://m3u.ibert.me/txt/o_cn.txt
https://m3u.ibert.me/txt/j_iptv.txt
-https://ghp.ci/https://raw.githubusercontent.com/xzw832/cmys/main/S_CCTV.txt
-https://ghp.ci/https://raw.githubusercontent.com/xzw832/cmys/main/S_weishi.txt
-https://ghp.ci//https://raw.githubusercontent.com/asdjkl6/tv/tv/.m3u/整套直播源/测试/整套直播源/l.txt
-https://ghp.ci//https://raw.githubusercontent.com/asdjkl6/tv/tv/.m3u/整套直播源/测试/整套直播源/kk.txt
+https://cdn.jsdelivr.net/gh/xzw832/cmys@main/S_CCTV.txt
+https://cdn.jsdelivr.net/gh/xzw832/cmys@main/S_weishi.txt
+https://cdn.jsdelivr.net/gh/asdjkl6/tv@tv/.m3u/整套直播源/测试/整套直播源/l.txt
+https://cdn.jsdelivr.net/gh/asdjkl6/tv@tv/.m3u/整套直播源/测试/整套直播源/kk.txt
diff --git a/docs/config.md b/docs/config.md
index 7a38a5b6f7..fbb3767978 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -1,8 +1,7 @@
| 配置项 | 描述 | 默认值 |
|:-----------------------|:------------------------------------------------------------------------------------------|:----------------------------------------|
-| open_driver | 开启浏览器运行,若更新无数据可开启此模式,较消耗性能 | True |
+| open_driver | 开启浏览器运行,若更新无数据可开启此模式,较消耗性能 | False |
| open_empty_category | 开启无结果频道分类,自动归类至底部 | False |
-| open_ffmpeg | 开启使用 FFmpeg 进行测速,获取更准确的速度与分辨率信息,需要提前手动安装 | True |
| open_filter_resolution | 开启分辨率过滤,低于最小分辨率(min_resolution)的接口将会被过滤 | True |
| open_filter_speed | 开启速率过滤,低于最小速率(min_speed)的接口将会被过滤 | True |
| open_hotel | 开启酒店源功能,关闭后所有酒店源工作模式都将关闭 | True |
@@ -15,22 +14,22 @@
| open_multicast_fofa | 开启 FOFA 组播源工作模式 | True |
| open_online_search | 开启关键字搜索源功能 | False |
| open_proxy | 开启代理,自动获取免费可用代理,若更新无数据可开启此模式 | False |
-| open_request | 开启查询请求,数据来源于网络 | False |
+| open_request | 开启查询请求,数据来源于网络(仅针对酒店源与组播源) | False |
| open_service | 开启页面服务,用于控制是否启动结果页面服务;如果使用青龙等平台部署,有专门设定的定时任务,需要更新完成后停止运行,可以关闭该功能 | True |
| open_sort | 开启排序功能(响应速度、日期、分辨率) | True |
| open_subscribe | 开启订阅源功能 | False |
| open_update | 开启更新,用于控制是否更新接口,若关闭则所有工作模式(获取接口和测速)均停止 | True |
| open_update_time | 开启显示更新时间 | True |
| open_url_info | 开启显示接口说明信息,用于控制是否显示分辨率、接口协议类型等信息,为$符号后的内容,播放软件使用该信息对接口进行描述 | True |
-| open_use_cache | 开启使用本地缓存数据,适用于查询请求失败场景 | True |
+| open_use_cache | 开启使用本地缓存数据,适用于查询请求失败场景(仅针对酒店源与组播源) | True |
| open_use_old_result | 开启使用历史更新结果(包含模板与结果文件的接口),合并至本次更新中 | True |
-| delay_weight | 响应时间权重值(所有权重值总和应为 1) | 0.25 |
| final_file | 生成结果文件路径 | output/result.txt |
| hotel_num | 结果中偏好的酒店源接口数量 | 4 |
| hotel_page_num | 酒店地区获取分页数量 | 1 |
| hotel_region_list | 酒店源地区列表,"全部"表示所有地区 | 全部 |
| ipv4_num | 结果中偏好的 IPv4 接口数量 | 5 |
| ipv6_num | 结果中偏好的 IPv6 接口数量 | 5 |
+| ipv6_support | 强制认为当前网络支持IPv6,跳过检测 | False |
| ipv_type | 生成结果中接口的协议类型,可选值:ipv4、ipv6、全部、all | 全部 |
| ipv_type_prefer | 接口协议类型偏好,优先将该类型的接口排在结果前面,可选值:IPv4、IPv6、自动、auto | 自动 |
| min_resolution | 接口最小分辨率,需要开启 open_filter_resolution 才能生效 | 1920x1080 |
@@ -43,10 +42,7 @@
| origin_type_prefer | 结果偏好的接口来源,结果优先按该顺序进行排序,hotel:酒店源,multicast:组播源,subscribe:订阅源,online_search:关键字搜索 | hotel,multicast,subscribe,online_search |
| recent_days | 获取最近时间范围内更新的接口(单位天),适当减小可避免出现匹配问题 | 30 |
| request_timeout | 查询请求超时时长,单位秒(s),用于控制查询接口文本链接的超时时长以及重试时长,调整此值能优化更新时间 | 10 |
-| resolution_weight | 分辨率权重值 (所有权重值总和应为 1) | 0.25 |
| sort_timeout | 单个接口测速超时时长,单位秒(s);数值越大测速所属时间越长,能提高获取接口数量,但质量会有所下降;数值越小测速所需时间越短,能获取低延时的接口,质量较好;调整此值能优化更新时间 | 10 |
| source_file | 模板文件路径 | config/demo.txt |
-| speed_weight | 速率权重值(所有权重值总和应为 1) | 0.5 |
| subscribe_num | 结果中偏好的订阅源接口数量 | 3 |
-| url_keywords_blacklist | 接口关键字黑名单,用于过滤含特定字符的接口 | |
| urls_limit | 单个频道接口数量 | 10 |
\ No newline at end of file
diff --git a/docs/config_en.md b/docs/config_en.md
index dca62473f4..be2a6c17d7 100644
--- a/docs/config_en.md
+++ b/docs/config_en.md
@@ -1,8 +1,7 @@
| Configuration Item | Description | Default Value |
|:-----------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------|
-| open_driver | Enable browser execution, If there are no updates, this mode can be enabled, which consumes more performance | True |
+| open_driver | Enable browser execution, If there are no updates, this mode can be enabled, which consumes more performance | False |
| open_empty_category | Enable the No Results Channel Category, which will automatically categorize channels without results to the bottom | False |
-| open_ffmpeg | Enable speed testing using FFmpeg to obtain more accurate speed and resolution information. Manual installation is required in advance. | True |
| open_filter_resolution | Enable resolution filtering, interfaces with resolution lower than the minimum resolution (min_resolution) will be filtered | True |
| open_filter_speed | Enable speed filtering, interfaces with speed lower than the minimum speed (min_speed) will be filtered | True |
| open_hotel | Enable the hotel source function, after closing it all hotel source working modes will be disabled | True |
@@ -15,22 +14,22 @@
| open_multicast_fofa | Enable FOFA multicast source work mode | True |
| open_online_search | Enable keyword search source feature | False |
| open_proxy | Enable proxy, automatically obtains free available proxies, If there are no updates, this mode can be enabled | False |
-| open_request | Enable query request, the data is obtained from the network | False |
+| open_request | Enable query request, the data is obtained from the network (only for hotel sources and multicast sources) | False |
| open_service | Enable page service, used to control whether to start the result page service; if deployed on platforms like Qinglong with dedicated scheduled tasks, the function can be turned off after updates are completed and the task is stopped | True |
| open_sort | Enable the sorting function (response speed, date, resolution) | True |
| open_subscribe | Enable subscription source feature | True |
| open_update | Enable updates, if disabled then only the result page service is run | True |
| open_update_time | Enable show update time | True |
| open_url_info | Enable display of API description information, used to control whether to show resolution, API protocol type, etc., the content after the $ symbol, playback software uses this information to describe the API | True |
-| open_use_cache | Enable the use of local cache data, applicable to the query request failure scenario | True |
+| open_use_cache | Enable the use of local cache data, applicable to the query request failure scenario (only for hotel sources and multicast sources) | True |
| open_use_old_result | Enable the use of historical update results (including the interface for template and result files) and merge them into the current update | True |
-| delay_weight | Response time weight value (the sum of all weight values should be 1) | 0.25 |
| final_file | Generated result file path | output/result.txt |
| hotel_num | The number of preferred hotel source interfaces in the results | 4 |
| hotel_page_num | Number of pages to retrieve for hotel regions | 1 |
| hotel_region_list | List of hotel source regions, 'all' indicates all regions | all |
| ipv4_num | The preferred number of IPv4 interfaces in the result | 5 |
| ipv6_num | The preferred number of IPv6 interfaces in the result | 5 |
+| ipv6_support | It is forced to consider that the current network supports IPv6 and skip the check | False |
| ipv_type | The protocol type of interface in the generated result, optional values: ipv4, ipv6, all | all |
| ipv_type_prefer | Interface protocol type preference, prioritize interfaces of this type in the results, optional values: IPv4, IPv6, auto | auto |
| min_resolution | Minimum interface resolution, requires enabling open_filter_resolution to take effect | 1920x1080 |
@@ -43,10 +42,7 @@
| origin_type_prefer | Result preference for the source of the interface, results are prioritized in this order: hotel: hotel source, multicast: multicast source, subscribe: subscription source, online_search: keyword search | hotel, multicast, subscribe, online_search |
| recent_days | Retrieve interfaces updated within a recent time range (in days), reducing appropriately can avoid matching issues | 30 |
| request_timeout | Query request timeout duration, in seconds (s), used to control the timeout and retry duration for querying interface text links. Adjusting this value can optimize update time. | 10 |
-| resolution_weight | Resolution weight value (the sum of all weight values should be 1) | 0.25 |
| sort_timeout | 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. | 10 |
| source_file | Template file path | config/demo.txt |
-| speed_weight | Speed weight value (the sum of all weight values should be 1) | 0.5 |
| subscribe_num | The number of preferred subscribe source interfaces in the results | 3 |
-| url_keywords_blacklist | Interface keyword blacklist, used to filter out interfaces containing specific characters | |
| urls_limit | Number of interfaces per channel | 10 |
\ No newline at end of file
diff --git a/docs/tutorial.md b/docs/tutorial.md
index e8106b96ea..eda1fee020 100644
--- a/docs/tutorial.md
+++ b/docs/tutorial.md
@@ -150,7 +150,7 @@
如果一切正常,稍等片刻后就可以看到该条工作流已经执行成功(绿色勾图标)
![Workflow执行成功](./images/workflow-success.png 'Workflow执行成功')
此时您可以访问代理文件链接,查看最新结果有没有同步即可:
-https://ghp.ci/raw.githubusercontent.com/您的github用户名/仓库名称(对应上述Fork创建时的TV)/master/output/user_result.txt
+https://cdn.jsdelivr.net/gh/您的github用户名/仓库名称(对应上述Fork创建时的TV)@master/output/user_result.txt
![用户名与仓库名称](./images/rep-info.png '用户名与仓库名称')
如果访问该链接能正常返回更新后的接口内容,说明您的直播源接口链接已经大功告成了!将该链接复制粘贴到 TVBox
@@ -282,5 +282,5 @@ docker run -v /etc/docker/config:/iptv-api-lite/config -v /etc/docker/output:/ip
### 上传更新文件至仓库(可选)
如果您没有自己的域名地址,接口更新完成后,将 user_result.txt 上传至个人仓库,即可使用
-https://ghp.ci/raw.githubusercontent.com/您的github用户名/仓库名称(对应上述Fork创建时的TV)/master/output/user_result.txt
+https://cdn.jsdelivr.net/gh/您的github用户名/仓库名称(对应上述Fork创建时的TV)@master/output/user_result.txt
![用户名与仓库名称](./images/rep-info.png '用户名与仓库名称')
diff --git a/docs/tutorial_en.md b/docs/tutorial_en.md
index e96d296d8f..31323d2a89 100644
--- a/docs/tutorial_en.md
+++ b/docs/tutorial_en.md
@@ -159,8 +159,8 @@ mark).
![Workflow executed successfully](./images/workflow-success.png 'Workflow executed successfully')
At this point, you can visit the proxy file link to see if the latest results have been synchronized:
-https://ghp.ci/raw.githubusercontent.com/your github username/repository name (corresponding to the TV created when
-forking)/master/user_result.txt
+https://cdn.jsdelivr.net/gh/your github username/repository name (corresponding to the TV created when
+forking)@master/user_result.txt
![Username and Repository Name](./images/rep-info.png 'Username and Repository Name')
If you can access this link and it returns the updated interface content, then your live source interface link has been
@@ -303,6 +303,6 @@ Port environment variables:
If you do not have your own domain address, after the interface update is completed, upload user_result.txt to your
personal repository to use it.
-https://ghp.ci/raw.githubusercontent.com/your github username/repository name (corresponding to the TV created when
-forking)/master/output/user_result.txt
+https://cdn.jsdelivr.net/gh/github username/repository name (corresponding to the TV created when
+forking)@master/output/user_result.txt
![Username and Repository Name](./images/rep-info.png 'Username and Repository Name')
diff --git a/main.py b/main.py
index 477a63a127..cb43e5e4fa 100644
--- a/main.py
+++ b/main.py
@@ -129,7 +129,7 @@ async def main(self):
self.online_search_result,
)
channel_data_cache = copy.deepcopy(self.channel_data)
- ipv6_support = check_ipv6_support()
+ ipv6_support = config.ipv6_support or check_ipv6_support()
open_sort = config.open_sort
if open_sort:
urls_total = self.get_urls_len()
diff --git a/tkinter_ui/default.py b/tkinter_ui/default.py
index 768463b222..f6c0ed3475 100644
--- a/tkinter_ui/default.py
+++ b/tkinter_ui/default.py
@@ -1,9 +1,9 @@
import os
import tkinter as tk
from tkinter import filedialog
-from tkinter import scrolledtext
from tkinter import ttk
+import utils.constants as constants
from utils.config import config
@@ -13,43 +13,6 @@ def init_ui(self, root):
"""
Init default UI
"""
- frame_default_open_update = tk.Frame(root)
- frame_default_open_update.pack(fill=tk.X)
- frame_default_open_update_column1 = tk.Frame(frame_default_open_update)
- frame_default_open_update_column1.pack(side=tk.LEFT, fill=tk.Y)
- frame_default_open_update_column2 = tk.Frame(frame_default_open_update)
- frame_default_open_update_column2.pack(side=tk.RIGHT, fill=tk.Y)
-
- self.open_update_label = tk.Label(
- frame_default_open_update_column1, text="开启更新:", width=8
- )
- self.open_update_label.pack(side=tk.LEFT, padx=4, pady=8)
- self.open_update_var = tk.BooleanVar(value=config.open_update)
- self.open_update_checkbutton = ttk.Checkbutton(
- frame_default_open_update_column1,
- variable=self.open_update_var,
- onvalue=True,
- offvalue=False,
- command=self.update_open_update,
- text="(关闭则只运行结果页面服务)",
- )
- self.open_update_checkbutton.pack(side=tk.LEFT, padx=4, pady=8)
-
- self.open_use_old_result_label = tk.Label(
- frame_default_open_update_column2, text="使用历史结果:", width=12
- )
- self.open_use_old_result_label.pack(side=tk.LEFT, padx=4, pady=8)
- self.open_use_old_result_var = tk.BooleanVar(value=config.open_use_old_result)
- self.open_use_old_result_checkbutton = ttk.Checkbutton(
- frame_default_open_update_column2,
- variable=self.open_use_old_result_var,
- onvalue=True,
- offvalue=False,
- command=self.update_open_use_old_result,
- text="(保留上次更新可用结果)",
- )
- self.open_use_old_result_checkbutton.pack(side=tk.LEFT, padx=4, pady=8)
-
frame_default_source_file = tk.Frame(root)
frame_default_source_file.pack(fill=tk.X)
frame_default_source_file_column1 = tk.Frame(frame_default_source_file)
@@ -94,6 +57,76 @@ def init_ui(self, root):
)
self.final_file_button.pack(side=tk.LEFT, padx=4, pady=0)
+ frame_default_open_update = tk.Frame(root)
+ frame_default_open_update.pack(fill=tk.X)
+ frame_default_open_update_column1 = tk.Frame(frame_default_open_update)
+ frame_default_open_update_column1.pack(side=tk.LEFT, fill=tk.Y)
+ frame_default_open_update_column2 = tk.Frame(frame_default_open_update)
+ frame_default_open_update_column2.pack(side=tk.RIGHT, fill=tk.Y)
+
+ self.open_update_label = tk.Label(
+ frame_default_open_update_column1, text="开启更新:", width=12
+ )
+ self.open_update_label.pack(side=tk.LEFT, padx=4, pady=8)
+ self.open_update_var = tk.BooleanVar(value=config.open_update)
+ self.open_update_checkbutton = ttk.Checkbutton(
+ frame_default_open_update_column1,
+ variable=self.open_update_var,
+ onvalue=True,
+ offvalue=False,
+ command=self.update_open_update
+ )
+ self.open_update_checkbutton.pack(side=tk.LEFT, padx=4, pady=8)
+
+ self.open_service_label = tk.Label(
+ frame_default_open_update_column2, text="开启服务:", width=8
+ )
+ self.open_service_label.pack(side=tk.LEFT, padx=4, pady=8)
+ self.open_service_var = tk.BooleanVar(value=config.open_service)
+ self.open_service_checkbutton = ttk.Checkbutton(
+ frame_default_open_update_column2,
+ variable=self.open_service_var,
+ onvalue=True,
+ offvalue=False,
+ command=self.update_open_service
+ )
+ self.open_service_checkbutton.pack(side=tk.LEFT, padx=4, pady=8)
+
+ frame_default_open_cache = tk.Frame(root)
+ frame_default_open_cache.pack(fill=tk.X)
+ frame_default_open_cache_column1 = tk.Frame(frame_default_open_cache)
+ frame_default_open_cache_column1.pack(side=tk.LEFT, fill=tk.Y)
+ frame_default_open_cache_column2 = tk.Frame(frame_default_open_cache)
+ frame_default_open_cache_column2.pack(side=tk.RIGHT, fill=tk.Y)
+
+ self.open_use_old_result_label = tk.Label(
+ frame_default_open_cache_column1, text="使用历史结果:", width=12
+ )
+ self.open_use_old_result_label.pack(side=tk.LEFT, padx=4, pady=8)
+ self.open_use_old_result_var = tk.BooleanVar(value=config.open_use_old_result)
+ self.open_use_old_result_checkbutton = ttk.Checkbutton(
+ frame_default_open_cache_column1,
+ variable=self.open_use_old_result_var,
+ onvalue=True,
+ offvalue=False,
+ command=self.update_open_use_old_result,
+ )
+ self.open_use_old_result_checkbutton.pack(side=tk.LEFT, padx=4, pady=8)
+
+ self.open_use_cache_label = tk.Label(
+ frame_default_open_cache_column2, text="使用离线数据:", width=12
+ )
+ self.open_use_cache_label.pack(side=tk.LEFT, padx=4, pady=8)
+ self.open_use_cache_var = tk.BooleanVar(value=config.open_use_cache)
+ self.open_use_cache_checkbutton = ttk.Checkbutton(
+ frame_default_open_cache_column2,
+ variable=self.open_use_cache_var,
+ onvalue=True,
+ offvalue=False,
+ command=self.update_open_use_cache,
+ )
+ self.open_use_cache_checkbutton.pack(side=tk.LEFT, padx=4, pady=8)
+
frame_default_mode = tk.Frame(root)
frame_default_mode.pack(fill=tk.X)
frame_default_mode_params_column1 = tk.Frame(frame_default_mode)
@@ -101,35 +134,28 @@ def init_ui(self, root):
frame_default_mode_params_column2 = tk.Frame(frame_default_mode)
frame_default_mode_params_column2.pack(side=tk.RIGHT, fill=tk.Y)
- self.open_driver_label = tk.Label(
- frame_default_mode_params_column1, text="浏览器模式:", width=12
+ self.open_request_label = tk.Label(
+ frame_default_mode_params_column1, text="开启网络请求:", width=12
)
- self.open_driver_label.pack(side=tk.LEFT, padx=4, pady=8)
- self.open_driver_var = tk.BooleanVar(value=config.open_driver)
- self.open_driver_checkbutton = ttk.Checkbutton(
+ self.open_request_label.pack(side=tk.LEFT, padx=4, pady=8)
+ self.open_request_var = tk.BooleanVar(value=config.open_request)
+ self.open_request_checkbutton = ttk.Checkbutton(
frame_default_mode_params_column1,
- variable=self.open_driver_var,
+ variable=self.open_request_var,
onvalue=True,
offvalue=False,
- command=self.update_open_driver,
- text="(若获取更新异常请开启)",
+ command=self.update_open_request
)
- self.open_driver_checkbutton.pack(side=tk.LEFT, padx=4, pady=8)
+ self.open_request_checkbutton.pack(side=tk.LEFT, padx=4, pady=8)
- self.open_proxy_label = tk.Label(
- frame_default_mode_params_column2, text="开启代理:", width=12
+ self.request_timeout_label = tk.Label(
+ frame_default_mode_params_column2, text="请求超时(s):", width=12
)
- self.open_proxy_label.pack(side=tk.LEFT, padx=4, pady=8)
- self.open_proxy_var = tk.BooleanVar(value=config.open_proxy)
- self.open_proxy_checkbutton = ttk.Checkbutton(
- frame_default_mode_params_column2,
- variable=self.open_proxy_var,
- onvalue=True,
- offvalue=False,
- command=self.update_open_proxy,
- text="(通过代理进行更新)",
- )
- self.open_proxy_checkbutton.pack(side=tk.LEFT, padx=4, pady=8)
+ self.request_timeout_label.pack(side=tk.LEFT, padx=4, pady=8)
+ self.request_timeout_entry = tk.Entry(frame_default_mode_params_column2, width=8)
+ self.request_timeout_entry.pack(side=tk.LEFT, padx=4, pady=8)
+ self.request_timeout_entry.insert(0, config.request_timeout)
+ self.request_timeout_entry.bind("", self.update_request_timeout)
frame_default_channel = tk.Frame(root)
frame_default_channel.pack(fill=tk.X)
@@ -162,166 +188,74 @@ def init_ui(self, root):
self.ipv_type_combo.current(2)
self.ipv_type_combo.bind("<>", self.update_ipv_type)
- frame_default_open_keep_all = tk.Frame(root)
- frame_default_open_keep_all.pack(fill=tk.X)
+ frame_proxy_mode = tk.Frame(root)
+ frame_proxy_mode.pack(fill=tk.X)
+ frame_proxy_mode_params_column1 = tk.Frame(frame_proxy_mode)
+ frame_proxy_mode_params_column1.pack(side=tk.LEFT, fill=tk.Y)
+ frame_proxy_mode_params_column2 = tk.Frame(frame_proxy_mode)
+ frame_proxy_mode_params_column2.pack(side=tk.RIGHT, fill=tk.Y)
+
+ self.open_proxy_label = tk.Label(
+ frame_proxy_mode_params_column1, text="开启代理查询:", width=12
+ )
+ self.open_proxy_label.pack(side=tk.LEFT, padx=4, pady=8)
+ self.open_proxy_var = tk.BooleanVar(value=config.open_proxy)
+ self.open_proxy_checkbutton = ttk.Checkbutton(
+ frame_proxy_mode_params_column1,
+ variable=self.open_proxy_var,
+ onvalue=True,
+ offvalue=False,
+ command=self.update_open_proxy
+ )
+ self.open_proxy_checkbutton.pack(side=tk.LEFT, padx=4, pady=8)
self.open_keep_all_label = tk.Label(
- frame_default_open_keep_all, text="保留模式:", width=12
+ frame_proxy_mode_params_column2, text="完整记录:", width=12
)
self.open_keep_all_label.pack(side=tk.LEFT, padx=4, pady=8)
self.open_keep_all_var = tk.BooleanVar(value=config.open_keep_all)
self.open_keep_all_checkbutton = ttk.Checkbutton(
- frame_default_open_keep_all,
+ frame_proxy_mode_params_column2,
variable=self.open_keep_all_var,
onvalue=True,
offvalue=False,
- command=self.update_open_keep_all,
- text="(保留所有查询记录)",
+ command=self.update_open_keep_all
)
self.open_keep_all_checkbutton.pack(side=tk.LEFT, padx=4, pady=8)
- frame_default_sort = tk.Frame(root)
- frame_default_sort.pack(fill=tk.X)
- frame_default_sort_column1 = tk.Frame(frame_default_sort)
- frame_default_sort_column1.pack(side=tk.LEFT, fill=tk.Y)
- frame_default_sort_column2 = tk.Frame(frame_default_sort)
- frame_default_sort_column2.pack(side=tk.RIGHT, fill=tk.Y)
-
- self.open_sort_label = tk.Label(
- frame_default_sort_column1, text="测速排序:", width=12
- )
- self.open_sort_label.pack(side=tk.LEFT, padx=4, pady=8)
- self.open_sort_var = tk.BooleanVar(value=config.open_sort)
- self.open_sort_checkbutton = ttk.Checkbutton(
- frame_default_sort_column1,
- variable=self.open_sort_var,
- onvalue=True,
- offvalue=False,
- command=self.update_open_sort,
- )
- self.open_sort_checkbutton.pack(side=tk.LEFT, padx=4, pady=8)
-
- self.sort_timeout_label = tk.Label(
- frame_default_sort_column2, text="测速超时:", width=12
- )
- self.sort_timeout_label.pack(side=tk.LEFT, padx=4, pady=8)
- self.sort_timeout_entry = tk.Entry(frame_default_sort_column2, width=8)
- self.sort_timeout_entry.pack(side=tk.LEFT, padx=4, pady=8)
- self.sort_timeout_entry.insert(0, config.sort_timeout)
- self.sort_timeout_entry.bind("", self.update_sort_timeout)
-
- frame_default_sort_mode = tk.Frame(root)
- frame_default_sort_mode.pack(fill=tk.X)
- frame_default_sort_mode_column1 = tk.Frame(frame_default_sort_mode)
- frame_default_sort_mode_column1.pack(side=tk.LEFT, fill=tk.Y)
- frame_default_sort_mode_column2 = tk.Frame(frame_default_sort_mode)
- frame_default_sort_mode_column2.pack(side=tk.RIGHT, fill=tk.Y)
-
- self.open_ffmpeg_label = tk.Label(
- frame_default_sort_mode_column1, text="FFmpeg测速:", width=12
- )
- self.open_ffmpeg_label.pack(side=tk.LEFT, padx=4, pady=8)
- self.open_ffmpeg_var = tk.BooleanVar(value=config.open_ffmpeg)
- self.open_ffmpeg_checkbutton = ttk.Checkbutton(
- frame_default_sort_mode_column1,
- variable=self.open_ffmpeg_var,
- onvalue=True,
- offvalue=False,
- command=self.update_open_ffmpeg,
- text="(需要手动安装)",
- )
- self.open_ffmpeg_checkbutton.pack(side=tk.LEFT, padx=4, pady=8)
-
+ frame_m3u = tk.Frame(root)
+ frame_m3u.pack(fill=tk.X)
+ frame_proxy_m3u_column1 = tk.Frame(frame_m3u)
+ frame_proxy_m3u_column1.pack(side=tk.LEFT, fill=tk.Y)
+ frame_proxy_m3u_column2 = tk.Frame(frame_m3u)
+ frame_proxy_m3u_column2.pack(side=tk.RIGHT, fill=tk.Y)
self.open_m3u_result_label = tk.Label(
- frame_default_sort_mode_column2, text="M3U转换:", width=12
+ frame_proxy_m3u_column1, text="M3U转换:", width=12
)
self.open_m3u_result_label.pack(side=tk.LEFT, padx=4, pady=8)
self.open_m3u_result_var = tk.BooleanVar(value=config.open_m3u_result)
self.open_m3u_result_checkbutton = ttk.Checkbutton(
- frame_default_sort_mode_column2,
+ frame_proxy_m3u_column1,
variable=self.open_m3u_result_var,
onvalue=True,
offvalue=False,
- command=self.update_open_m3u_result,
- text="(开启频道图标)",
+ command=self.update_open_m3u_result
)
self.open_m3u_result_checkbutton.pack(side=tk.LEFT, padx=4, pady=8)
- frame_default_resolution_params = tk.Frame(root)
- frame_default_resolution_params.pack(fill=tk.X)
- frame_default_resolution_params_column1 = tk.Frame(
- frame_default_resolution_params
- )
- frame_default_resolution_params_column1.pack(side=tk.LEFT, fill=tk.Y)
- frame_default_resolution_params_column2 = tk.Frame(
- frame_default_resolution_params
- )
- frame_default_resolution_params_column2.pack(side=tk.RIGHT, fill=tk.Y)
-
- self.open_filter_resolution_label = tk.Label(
- frame_default_resolution_params_column1, text="分辨率过滤:", width=12
- )
- self.open_filter_resolution_label.pack(side=tk.LEFT, padx=4, pady=8)
- self.open_filter_resolution_var = tk.BooleanVar(
- value=config.open_filter_resolution
+ self.open_driver_label = tk.Label(
+ frame_proxy_m3u_column2, text="浏览器模式:", width=12
)
- self.open_filter_resolution_checkbutton = ttk.Checkbutton(
- frame_default_resolution_params_column1,
- variable=self.open_filter_resolution_var,
+ self.open_driver_label.pack(side=tk.LEFT, padx=4, pady=8)
+ self.open_driver_var = tk.BooleanVar(value=config.open_driver)
+ self.open_driver_checkbutton = ttk.Checkbutton(
+ frame_proxy_m3u_column2,
+ variable=self.open_driver_var,
onvalue=True,
offvalue=False,
- command=self.update_open_filter_resolution,
- text="(低于最小分辨率将被过滤)",
- )
- self.open_filter_resolution_checkbutton.pack(side=tk.LEFT, padx=4, pady=8)
-
- self.min_resolution_label = tk.Label(
- frame_default_resolution_params_column2, text="最小分辨率:", width=12
- )
- self.min_resolution_label.pack(side=tk.LEFT, padx=4, pady=8)
- self.min_resolution_entry = tk.Entry(
- frame_default_resolution_params_column2, width=10
- )
- self.min_resolution_entry.pack(side=tk.LEFT, padx=4, pady=8)
- self.min_resolution_entry.insert(0, config.min_resolution)
- self.min_resolution_entry.bind("", self.update_min_resolution)
-
- frame_default_sort_params = tk.Frame(root)
- frame_default_sort_params.pack(fill=tk.X)
- frame_default_sort_params_column1 = tk.Frame(frame_default_sort_params)
- frame_default_sort_params_column1.pack(side=tk.LEFT, fill=tk.Y)
- frame_default_sort_params_column2 = tk.Frame(frame_default_sort_params)
- frame_default_sort_params_column2.pack(side=tk.RIGHT, fill=tk.Y)
-
- self.delay_weight_label = tk.Label(
- frame_default_sort_params_column1, text="响应时间权重:", width=12
- )
- self.delay_weight_label.pack(side=tk.LEFT, padx=4, pady=8)
- self.delay_weight_scale = tk.Scale(
- frame_default_sort_params_column1,
- from_=0,
- to=1,
- orient=tk.HORIZONTAL,
- resolution=0.1,
- command=self.update_delay_weight,
- )
- self.delay_weight_scale.pack(side=tk.LEFT, padx=4, pady=8)
- self.delay_weight_scale.set(config.delay_weight)
-
- self.resolution_weight_label = tk.Label(
- frame_default_sort_params_column2, text="分辨率权重:", width=12
- )
- self.resolution_weight_label.pack(side=tk.LEFT, padx=4, pady=8)
- self.resolution_weight_scale = tk.Scale(
- frame_default_sort_params_column2,
- from_=0,
- to=1,
- orient=tk.HORIZONTAL,
- resolution=0.1,
- command=self.update_resolution_weight,
- )
- self.resolution_weight_scale.pack(side=tk.LEFT, padx=4, pady=8)
- self.resolution_weight_scale.set(config.resolution_weight)
+ command=self.update_open_driver
+ )
+ self.open_driver_checkbutton.pack(side=tk.LEFT, padx=4, pady=8)
frame_default_open_update_info = tk.Frame(root)
frame_default_open_update_info.pack(fill=tk.X)
@@ -364,14 +298,22 @@ def init_ui(self, root):
frame_default_open_empty_category = tk.Frame(root)
frame_default_open_empty_category.pack(fill=tk.X)
+ frame_default_open_empty_category_column1 = tk.Frame(
+ frame_default_open_empty_category
+ )
+ frame_default_open_empty_category_column1.pack(side=tk.LEFT, fill=tk.Y)
+ frame_default_open_empty_category_column2 = tk.Frame(
+ frame_default_open_empty_category
+ )
+ frame_default_open_empty_category_column2.pack(side=tk.RIGHT, fill=tk.Y)
self.open_empty_category_label = tk.Label(
- frame_default_open_empty_category, text="显示无结果分类:", width=12
+ frame_default_open_empty_category_column1, text="显示无结果分类:", width=12
)
self.open_empty_category_label.pack(side=tk.LEFT, padx=4, pady=8)
self.open_empty_category_var = tk.BooleanVar(value=config.open_empty_category)
self.open_empty_category_checkbutton = ttk.Checkbutton(
- frame_default_open_empty_category,
+ frame_default_open_empty_category_column1,
variable=self.open_empty_category_var,
onvalue=True,
offvalue=False,
@@ -379,34 +321,64 @@ def init_ui(self, root):
)
self.open_empty_category_checkbutton.pack(side=tk.LEFT, padx=4, pady=8)
- frame_default_url_keywords_blacklist = tk.Frame(root)
- frame_default_url_keywords_blacklist.pack(fill=tk.X)
-
- self.url_keywords_blacklist_label = tk.Label(
- frame_default_url_keywords_blacklist, text="关键字黑名单:", width=12
+ self.ipv6_support_label = tk.Label(
+ frame_default_open_empty_category_column2, text="跳过IPv6检测:", width=12
)
- self.url_keywords_blacklist_label.pack(side=tk.LEFT, padx=4, pady=8)
- self.url_keywords_blacklist_text = scrolledtext.ScrolledText(
- frame_default_url_keywords_blacklist, height=5
+ self.ipv6_support_label.pack(side=tk.LEFT, padx=4, pady=8)
+ self.ipv6_support_var = tk.BooleanVar(value=config.ipv6_support)
+ self.ipv6_support_checkbutton = ttk.Checkbutton(
+ frame_default_open_empty_category_column2,
+ variable=self.ipv6_support_var,
+ onvalue=True,
+ offvalue=False,
+ command=self.update_ipv6_support,
)
- self.url_keywords_blacklist_text.pack(
- side=tk.LEFT, padx=4, pady=8, expand=True, fill=tk.BOTH
+ self.ipv6_support_checkbutton.pack(side=tk.LEFT, padx=4, pady=8)
+
+ frame_default_url_keywords = tk.Frame(root)
+ frame_default_url_keywords.pack(fill=tk.X)
+ frame_default_url_keywords_column1 = tk.Frame(frame_default_url_keywords)
+ frame_default_url_keywords_column1.pack(side=tk.LEFT, fill=tk.Y)
+ frame_default_url_keywords_column2 = tk.Frame(frame_default_url_keywords)
+ frame_default_url_keywords_column2.pack(side=tk.RIGHT, fill=tk.Y)
+
+ self.url_keywords_whitelist_label = tk.Label(
+ frame_default_url_keywords_column1, text="白名单:", width=12
)
- self.url_keywords_blacklist_text.insert(
- tk.END, ",".join(config.url_keywords_blacklist)
+ self.url_keywords_whitelist_label.pack(side=tk.LEFT, padx=4, pady=8)
+ self.whitelist_file_button = tk.ttk.Button(
+ frame_default_url_keywords_column1,
+ text="编辑",
+ command=self.edit_whitelist_file,
)
- self.url_keywords_blacklist_text.bind(
- "", self.update_url_keywords_blacklist
+ self.whitelist_file_button.pack(side=tk.LEFT, padx=4, pady=0)
+ self.url_keywords_blacklist_label = tk.Label(
+ frame_default_url_keywords_column2, text="黑名单:", width=12
)
+ self.url_keywords_blacklist_label.pack(side=tk.LEFT, padx=4, pady=8)
+ self.blacklist_file_button = tk.ttk.Button(
+ frame_default_url_keywords_column2,
+ text="编辑",
+ command=self.edit_blacklist_file,
+ )
+ self.blacklist_file_button.pack(side=tk.LEFT, padx=4, pady=0)
def update_open_update(self):
config.set("Settings", "open_update", str(self.open_update_var.get()))
+ def update_open_service(self):
+ config.set("Settings", "open_service", str(self.open_update_var.get()))
+
def update_open_use_old_result(self):
config.set(
"Settings", "open_use_old_result", str(self.open_use_old_result_var.get())
)
+ def update_open_use_cache(self):
+ config.set(
+ "Settings", "open_use_cache", str(self.open_use_cache_var.get())
+ )
+
def select_source_file(self):
filepath = filedialog.askopenfilename(
initialdir=os.getcwd(), title="选择模板文件", filetypes=[("txt", "*.txt")]
@@ -425,6 +397,9 @@ def select_final_file(self):
self.final_file_entry.insert(0, filepath)
config.set("Settings", "final_file", filepath)
+ def update_open_request(self):
+ config.set("Settings", "open_requests", str(self.open_request_var.get()))
+
def update_open_driver(self):
config.set("Settings", "open_driver", str(self.open_driver_var.get()))
@@ -434,45 +409,15 @@ def update_open_proxy(self):
def update_open_keep_all(self):
config.set("Settings", "open_keep_all", str(self.open_keep_all_var.get()))
- def update_open_sort(self):
- config.set("Settings", "open_sort", str(self.open_sort_var.get()))
-
- def update_sort_timeout(self):
- config.set("Settings", "sort_timeout", self.sort_timeout_entry.get())
-
- def update_open_ffmpeg(self):
- config.set("Settings", "open_ffmpeg", str(self.open_ffmpeg_var.get()))
-
def update_open_m3u_result(self):
config.set("Settings", "open_m3u_result", str(self.open_m3u_result_var.get()))
- def update_open_filter_resolution(self):
- config.set(
- "Settings",
- "open_filter_resolution",
- str(self.open_filter_resolution_var.get()),
- )
-
- def update_min_resolution(self, event):
- config.set("Settings", "min_resolution", self.min_resolution_entry.get())
+ def update_request_timeout(self, event):
+ config.set("Settings", "request_timeout", self.request_timeout_entry.get())
def update_urls_limit(self, event):
config.set("Settings", "urls_limit", self.urls_limit_entry.get())
- def update_delay_weight(self, event):
- weight1 = self.delay_weight_scale.get()
- weight2 = 1 - weight1
- self.resolution_weight_scale.set(weight2)
- config.set("Settings", "delay_weight", str(weight1))
- config.set("Settings", "resolution_weight", str(weight2))
-
- def update_resolution_weight(self, event):
- weight1 = self.resolution_weight_scale.get()
- weight2 = 1 - weight1
- self.delay_weight_scale.set(weight2)
- config.set("Settings", "resolution_weight", str(weight1))
- config.set("Settings", "delay_weight", str(weight2))
-
def update_open_update_time(self):
config.set("Settings", "open_update_time", str(self.open_update_time_var.get()))
@@ -484,27 +429,29 @@ def update_open_empty_category(self):
"Settings", "open_empty_category", str(self.open_empty_category_var.get())
)
+ def update_ipv6_support(self):
+ config.set(
+ "Settings", "ipv6_support", str(self.ipv6_support_var.get())
+ )
+
def update_ipv_type(self, event):
config.set("Settings", "ipv_type", self.ipv_type_combo.get())
- def update_url_keywords_blacklist(self, event):
- config.set(
- "Settings",
- "url_keywords_blacklist",
- self.url_keywords_blacklist_text.get(1.0, tk.END),
- )
+ def edit_whitelist_file(self):
+ if os.path.exists(constants.whitelist_path):
+ os.system(f'notepad.exe {constants.whitelist_path}')
- def update_url_keywords_blacklist(self, event):
- config.set(
- "Settings",
- "url_keywords_blacklist",
- self.url_keywords_blacklist_text.get(1.0, tk.END),
- )
+ def edit_blacklist_file(self):
+ if os.path.exists(constants.blacklist_path):
+ os.system(f'notepad.exe {constants.blacklist_path}')
def change_entry_state(self, state):
for entry in [
"open_update_checkbutton",
+ "open_service_checkbutton",
"open_use_old_result_checkbutton",
+ "open_use_cache_checkbutton",
+ "open_request_checkbutton",
"open_driver_checkbutton",
"open_proxy_checkbutton",
"source_file_entry",
@@ -512,19 +459,14 @@ def change_entry_state(self, state):
"final_file_entry",
"final_file_button",
"open_keep_all_checkbutton",
- "open_sort_checkbutton",
- "sort_timeout_entry",
- "open_ffmpeg_checkbutton",
"open_m3u_result_checkbutton",
- "open_filter_resolution_checkbutton",
- "min_resolution_entry",
"urls_limit_entry",
- "delay_weight_scale",
- "resolution_weight_scale",
"open_update_time_checkbutton",
"open_url_info_checkbutton",
"open_empty_category_checkbutton",
"ipv_type_combo",
- "url_keywords_blacklist_text",
+ "ipv6_support_checkbutton",
+ "whitelist_file_button",
+ "blacklist_file_button",
]:
getattr(self, entry).config(state=state)
diff --git a/tkinter_ui/speed.py b/tkinter_ui/speed.py
new file mode 100644
index 0000000000..d163963469
--- /dev/null
+++ b/tkinter_ui/speed.py
@@ -0,0 +1,167 @@
+import tkinter as tk
+from tkinter import ttk
+
+from utils.config import config
+
+
+class SpeedUI:
+ def init_ui(self, root=None):
+ """
+ Init speed UI
+ """
+ frame_default_sort = tk.Frame(root)
+ frame_default_sort.pack(fill=tk.X)
+ frame_default_sort_column1 = tk.Frame(frame_default_sort)
+ frame_default_sort_column1.pack(side=tk.LEFT, fill=tk.Y)
+ frame_default_sort_column2 = tk.Frame(frame_default_sort)
+ frame_default_sort_column2.pack(side=tk.RIGHT, fill=tk.Y)
+
+ self.open_sort_label = tk.Label(
+ frame_default_sort_column1, text="测速排序:", width=12
+ )
+ self.open_sort_label.pack(side=tk.LEFT, padx=4, pady=8)
+ self.open_sort_var = tk.BooleanVar(value=config.open_sort)
+ self.open_sort_checkbutton = ttk.Checkbutton(
+ frame_default_sort_column1,
+ variable=self.open_sort_var,
+ onvalue=True,
+ offvalue=False,
+ command=self.update_open_sort,
+ )
+ self.open_sort_checkbutton.pack(side=tk.LEFT, padx=4, pady=8)
+
+ self.sort_timeout_label = tk.Label(
+ frame_default_sort_column2, text="响应超时(s):", width=12
+ )
+ self.sort_timeout_label.pack(side=tk.LEFT, padx=4, pady=8)
+ self.sort_timeout_entry = tk.Entry(frame_default_sort_column2, width=10)
+ self.sort_timeout_entry.pack(side=tk.LEFT, padx=4, pady=8)
+ self.sort_timeout_entry.insert(0, config.sort_timeout)
+ self.sort_timeout_entry.bind("", self.update_sort_timeout)
+
+ frame_default_sort_mode = tk.Frame(root)
+ frame_default_sort_mode.pack(fill=tk.X)
+ frame_default_sort_mode_column1 = tk.Frame(frame_default_sort_mode)
+ frame_default_sort_mode_column1.pack(side=tk.LEFT, fill=tk.Y)
+ frame_default_sort_mode_column2 = tk.Frame(frame_default_sort_mode)
+ frame_default_sort_mode_column2.pack(side=tk.RIGHT, fill=tk.Y)
+
+ frame_default_speed_params = tk.Frame(root)
+ frame_default_speed_params.pack(fill=tk.X)
+ frame_default_speed_params_column1 = tk.Frame(
+ frame_default_speed_params
+ )
+ frame_default_speed_params_column1.pack(side=tk.LEFT, fill=tk.Y)
+ frame_default_speed_params_column2 = tk.Frame(
+ frame_default_speed_params
+ )
+ frame_default_speed_params_column2.pack(side=tk.RIGHT, fill=tk.Y)
+
+ self.open_filter_speed_label = tk.Label(
+ frame_default_speed_params_column1, text="速率过滤:", width=12
+ )
+ self.open_filter_speed_label.pack(side=tk.LEFT, padx=4, pady=8)
+ self.open_filter_speed_var = tk.BooleanVar(
+ value=config.open_filter_speed
+ )
+ self.open_filter_speed_checkbutton = ttk.Checkbutton(
+ frame_default_speed_params_column1,
+ variable=self.open_filter_speed_var,
+ onvalue=True,
+ offvalue=False,
+ command=self.update_open_filter_speed
+ )
+ self.open_filter_speed_checkbutton.pack(side=tk.LEFT, padx=4, pady=8)
+
+ self.min_speed_label = tk.Label(
+ frame_default_speed_params_column2, text="最小速率(M/s):", width=12
+ )
+ self.min_speed_label.pack(side=tk.LEFT, padx=4, pady=8)
+ self.min_speed_entry = tk.Entry(
+ frame_default_speed_params_column2, width=10
+ )
+ self.min_speed_entry.pack(side=tk.LEFT, padx=4, pady=8)
+ self.min_speed_entry.insert(0, config.min_speed)
+ self.min_speed_entry.bind("", self.update_min_speed)
+
+ frame_default_resolution_params = tk.Frame(root)
+ frame_default_resolution_params.pack(fill=tk.X)
+ frame_default_resolution_params_column1 = tk.Frame(
+ frame_default_resolution_params
+ )
+ frame_default_resolution_params_column1.pack(side=tk.LEFT, fill=tk.Y)
+ frame_default_resolution_params_column2 = tk.Frame(
+ frame_default_resolution_params
+ )
+ frame_default_resolution_params_column2.pack(side=tk.RIGHT, fill=tk.Y)
+
+ self.open_filter_resolution_label = tk.Label(
+ frame_default_resolution_params_column1, text="分辨率过滤:", width=12
+ )
+ self.open_filter_resolution_label.pack(side=tk.LEFT, padx=4, pady=8)
+ self.open_filter_resolution_var = tk.BooleanVar(
+ value=config.open_filter_resolution
+ )
+ self.open_filter_resolution_checkbutton = ttk.Checkbutton(
+ frame_default_resolution_params_column1,
+ variable=self.open_filter_resolution_var,
+ onvalue=True,
+ offvalue=False,
+ command=self.update_open_filter_resolution
+ )
+ self.open_filter_resolution_checkbutton.pack(side=tk.LEFT, padx=4, pady=8)
+
+ self.min_resolution_label = tk.Label(
+ frame_default_resolution_params_column2, text="最小分辨率:", width=12
+ )
+ self.min_resolution_label.pack(side=tk.LEFT, padx=4, pady=8)
+ self.min_resolution_entry = tk.Entry(
+ frame_default_resolution_params_column2, width=10
+ )
+ self.min_resolution_entry.pack(side=tk.LEFT, padx=4, pady=8)
+ self.min_resolution_entry.insert(0, config.min_resolution)
+ self.min_resolution_entry.bind("", self.update_min_resolution)
+
+ frame_default_sort_params = tk.Frame(root)
+ frame_default_sort_params.pack(fill=tk.X)
+ frame_default_sort_params_column1 = tk.Frame(frame_default_sort_params)
+ frame_default_sort_params_column1.pack(side=tk.LEFT, fill=tk.Y)
+ frame_default_sort_params_column2 = tk.Frame(frame_default_sort_params)
+ frame_default_sort_params_column2.pack(side=tk.RIGHT, fill=tk.Y)
+
+ def update_open_sort(self):
+ config.set("Settings", "open_sort", str(self.open_sort_var.get()))
+
+ def update_sort_timeout(self):
+ config.set("Settings", "sort_timeout", self.sort_timeout_entry.get())
+
+ def update_open_filter_speed(self):
+ config.set(
+ "Settings",
+ "open_filter_speed",
+ str(self.open_filter_speed_var.get()),
+ )
+
+ def update_min_speed(self, event):
+ config.set("Settings", "min_speed", self.min_speed_entry.get())
+
+ def update_open_filter_resolution(self):
+ config.set(
+ "Settings",
+ "open_filter_resolution",
+ str(self.open_filter_resolution_var.get()),
+ )
+
+ def update_min_resolution(self, event):
+ config.set("Settings", "min_resolution", self.min_resolution_entry.get())
+
+ def change_entry_state(self, state):
+ for entry in [
+ "open_sort_checkbutton",
+ "sort_timeout_entry",
+ "open_filter_speed_checkbutton",
+ "min_speed_entry",
+ "open_filter_resolution_checkbutton",
+ "min_resolution_entry"
+ ]:
+ getattr(self, entry).config(state=state)
diff --git a/tkinter_ui/subscribe.py b/tkinter_ui/subscribe.py
index 323c857a91..3c2fda914a 100644
--- a/tkinter_ui/subscribe.py
+++ b/tkinter_ui/subscribe.py
@@ -1,10 +1,10 @@
+import os
+import os.path
import tkinter as tk
-from tkinter import scrolledtext
from tkinter import ttk
import utils.constants as constants
from utils.config import config
-from utils.tools import get_urls_from_file
class SubscribeUI:
@@ -31,33 +31,32 @@ def init_ui(self, root):
frame_subscribe_subscribe_urls = tk.Frame(root)
frame_subscribe_subscribe_urls.pack(fill=tk.X)
+ frame_subscribe_urls_column1 = tk.Frame(frame_subscribe_subscribe_urls)
+ frame_subscribe_urls_column1.pack(side=tk.LEFT, fill=tk.Y)
+ frame_subscribe_urls_column2 = tk.Frame(frame_subscribe_subscribe_urls)
+ frame_subscribe_urls_column2.pack(side=tk.LEFT, fill=tk.Y)
self.subscribe_urls_label = tk.Label(
- frame_subscribe_subscribe_urls, text="订阅源:", width=9
+ frame_subscribe_urls_column1, text="订阅源:", width=9
)
self.subscribe_urls_label.pack(side=tk.LEFT, padx=4, pady=8)
- self.subscribe_urls_text = scrolledtext.ScrolledText(
- frame_subscribe_subscribe_urls, height=40
+ self.subscribe_file_button = tk.ttk.Button(
+ frame_subscribe_urls_column2,
+ text="编辑",
+ command=self.edit_subscribe_file,
)
- self.subscribe_urls_text.pack(
- side=tk.LEFT, padx=4, pady=8, expand=True, fill=tk.BOTH
- )
- self.subscribe_urls_text.insert(tk.END, ",".join(get_urls_from_file(constants.subscribe_path)))
- self.subscribe_urls_text.bind("", self.update_subscribe_urls)
+ self.subscribe_file_button.pack(side=tk.LEFT, padx=4, pady=0)
def update_open_subscribe(self):
config.set("Settings", "open_subscribe", str(self.open_subscribe_var.get()))
- def update_subscribe_urls(self, event):
- config.set(
- "Settings",
- "subscribe_urls",
- self.subscribe_urls_text.get(1.0, tk.END),
- )
+ def edit_subscribe_file(self):
+ if os.path.exists(constants.subscribe_path):
+ os.system(f'notepad.exe {constants.subscribe_path}')
def change_entry_state(self, state):
for entry in [
"open_subscribe_checkbutton",
- "subscribe_urls_text",
+ "subscribe_file_button",
]:
getattr(self, entry).config(state=state)
diff --git a/tkinter_ui/tkinter_ui.py b/tkinter_ui/tkinter_ui.py
index 013ed82f78..5c728c0366 100644
--- a/tkinter_ui/tkinter_ui.py
+++ b/tkinter_ui/tkinter_ui.py
@@ -13,6 +13,7 @@
import webbrowser
from about import AboutUI
from default import DefaultUI
+from speed import SpeedUI
from prefer import PreferUI
from multicast import MulticastUI
from hotel import HotelUI
@@ -30,6 +31,7 @@ def __init__(self, root):
self.version = info.get("version", "")
self.about_ui = AboutUI()
self.default_ui = DefaultUI()
+ self.speed_ui = SpeedUI()
self.prefer_ui = PreferUI()
self.multicast_ui = MulticastUI()
self.hotel_ui = HotelUI()
@@ -44,39 +46,40 @@ def view_result_link_callback(self, event):
def save_config(self):
config_values = {
- "open_update": self.default_ui.open_update_var.get(),
- "open_use_old_result": self.default_ui.open_use_old_result_var.get(),
- "source_file": self.default_ui.source_file_entry.get(),
- "final_file": self.default_ui.final_file_entry.get(),
- "urls_limit": self.default_ui.urls_limit_entry.get(),
"open_driver": self.default_ui.open_driver_var.get(),
- "open_proxy": self.default_ui.open_proxy_var.get(),
+ "open_filter_resolution": self.speed_ui.open_filter_resolution_var.get(),
+ "open_hotel": self.hotel_ui.open_hotel_var.get(),
+ "open_hotel_foodie": self.hotel_ui.open_hotel_foodie_var.get(),
+ "open_hotel_fofa": self.hotel_ui.open_hotel_fofa_var.get(),
"open_keep_all": self.default_ui.open_keep_all_var.get(),
- "open_sort": self.default_ui.open_sort_var.get(),
- "open_filter_resolution": self.default_ui.open_filter_resolution_var.get(),
- "min_resolution": self.default_ui.min_resolution_entry.get(),
- "delay_weight": self.default_ui.delay_weight_scale.get(),
- "resolution_weight": self.default_ui.resolution_weight_scale.get(),
- "ipv_type": self.default_ui.ipv_type_combo.get(),
- "url_keywords_blacklist": self.default_ui.url_keywords_blacklist_text.get(
- 1.0, tk.END
- ),
- "open_subscribe": self.subscribe_ui.open_subscribe_var.get(),
- "subscribe_urls": self.subscribe_ui.subscribe_urls_text.get(1.0, tk.END),
"open_multicast": self.multicast_ui.open_multicast_var.get(),
"open_multicast_foodie": self.multicast_ui.open_multicast_foodie_var.get(),
"open_multicast_fofa": self.multicast_ui.open_multicast_fofa_var.get(),
- "multicast_region_list": self.multicast_ui.region_list_combo.get(),
- "multicast_page_num": self.multicast_ui.page_num_entry.get(),
- "open_hotel": self.hotel_ui.open_hotel_var.get(),
- "open_hotel_foodie": self.hotel_ui.open_hotel_foodie_var.get(),
- "open_hotel_fofa": self.hotel_ui.open_hotel_fofa_var.get(),
+ "open_online_search": self.online_search_ui.open_online_search_var.get(),
+ "open_proxy": self.default_ui.open_proxy_var.get(),
+ "open_request": self.default_ui.open_request_var.get(),
+ "open_service": self.default_ui.open_service_var.get(),
+ "open_sort": self.speed_ui.open_sort_var.get(),
+ "open_subscribe": self.subscribe_ui.open_subscribe_var.get(),
+ "open_update": self.default_ui.open_update_var.get(),
+ "open_update_time": self.default_ui.open_update_time_var.get(),
+ "open_url_info": self.default_ui.open_url_info_var.get(),
+ "open_use_cache": self.default_ui.open_use_cache_var.get(),
+ "open_use_old_result": self.default_ui.open_use_old_result_var.get(),
+ "final_file": self.default_ui.final_file_entry.get(),
"hotel_region_list": self.hotel_ui.region_list_combo.get(),
"hotel_page_num": self.hotel_ui.page_num_entry.get(),
- "open_online_search": self.online_search_ui.open_online_search_var.get(),
+ "ipv_type": self.default_ui.ipv_type_combo.get(),
+ "ipv6_support": self.default_ui.ipv6_support_var.get(),
+ "min_resolution": self.speed_ui.min_resolution_entry.get(),
+ "multicast_region_list": self.multicast_ui.region_list_combo.get(),
+ "multicast_page_num": self.multicast_ui.page_num_entry.get(),
"online_search_page_num": self.online_search_ui.page_num_entry.get(),
"recent_days": self.online_search_ui.recent_days_entry.get(),
- "open_update_time": self.default_ui.open_update_time_var.get(),
+ "request_timeout": self.default_ui.request_timeout_entry.get(),
+ "sort_timeout": self.speed_ui.sort_timeout_entry.get(),
+ "source_file": self.default_ui.source_file_entry.get(),
+ "urls_limit": self.default_ui.urls_limit_entry.get(),
}
for key, value in config_values.items():
@@ -86,6 +89,7 @@ def save_config(self):
def change_state(self, state):
self.default_ui.change_entry_state(state=state)
+ self.speed_ui.change_entry_state(state=state)
self.prefer_ui.change_entry_state(state=state)
self.multicast_ui.change_entry_state(state=state)
self.hotel_ui.change_entry_state(state=state)
@@ -152,6 +156,7 @@ def init_UI(self):
notebook.pack(fill="both", padx=10, pady=5)
frame_default = tk.ttk.Frame(notebook)
+ frame_speed = tk.ttk.Frame(notebook)
frame_prefer = tk.ttk.Frame(notebook)
frame_hotel = tk.ttk.Frame(notebook)
frame_multicast = tk.ttk.Frame(notebook)
@@ -162,6 +167,10 @@ def init_UI(self):
resource_path("static/images/settings_icon.png")
).resize((16, 16))
settings_icon = ImageTk.PhotoImage(settings_icon_source)
+ speed_icon_source = Image.open(
+ resource_path("static/images/speed_icon.png")
+ ).resize((16, 16))
+ speed_icon = ImageTk.PhotoImage(speed_icon_source)
prefer_icon_source = Image.open(
resource_path("static/images/prefer_icon.png")
).resize((16, 16))
@@ -186,6 +195,7 @@ def init_UI(self):
notebook.add(
frame_default, text="通用设置", image=settings_icon, compound=tk.LEFT
)
+ notebook.add(frame_speed, text="测速设置", image=speed_icon, compound=tk.LEFT)
notebook.add(frame_prefer, text="偏好设置", image=prefer_icon, compound=tk.LEFT)
notebook.add(frame_hotel, text="酒店源", image=hotel_icon, compound=tk.LEFT)
notebook.add(
@@ -202,6 +212,7 @@ def init_UI(self):
)
notebook.settings_icon = settings_icon
+ notebook.speed_icon = speed_icon
notebook.prefer_icon = prefer_icon
notebook.hotel_icon = hotel_icon
notebook.multicast_icon = multicast_icon
@@ -209,6 +220,7 @@ def init_UI(self):
notebook.online_search_icon = online_search_icon
self.default_ui.init_ui(frame_default)
+ self.speed_ui.init_ui(frame_speed)
self.prefer_ui.init_ui(frame_prefer)
self.multicast_ui.init_ui(frame_multicast)
self.hotel_ui.init_ui(frame_hotel)
@@ -254,8 +266,8 @@ def init_UI(self):
def get_root_location(root):
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
- width = 550
- height = 750
+ width = 500
+ height = 600
x = (screen_width / 2) - (width / 2)
y = (screen_height / 2) - (height / 2)
return (width, height, x, y)
diff --git a/tkinter_ui/tkinter_ui.spec b/tkinter_ui/tkinter_ui.spec
index 29d406c943..4afc916e5c 100644
--- a/tkinter_ui/tkinter_ui.spec
+++ b/tkinter_ui/tkinter_ui.spec
@@ -1,13 +1,14 @@
# -*- mode: python ; coding: utf-8 -*-
a = Analysis(
- ['tkinter_ui.py', 'about.py', 'default.py', 'prefer.py', 'multicast.py', 'hotel.py', 'subscribe.py', 'online_search.py'],
+ ['tkinter_ui.py', 'about.py', 'default.py', 'speed.py', 'prefer.py', 'multicast.py', 'hotel.py', 'subscribe.py', 'online_search.py'],
pathex=[],
binaries=[],
datas=[
('../config/config.ini', 'config'),
('../config/demo.txt', 'config'),
('../config/whitelist.txt', 'config'),
+ ('../config/subscribe.txt', 'config'),
('../config/rtp', 'config/rtp'),
('../updates/hotel/cache.pkl', 'updates/hotel'),
('../updates/multicast/multicast_map.json', 'updates/multicast'),
@@ -17,6 +18,7 @@ a = Analysis(
('../static/images/favicon.ico', 'static/images'),
('../static/images/alipay.jpg', 'static/images'),
('../static/images/settings_icon.png', 'static/images'),
+ ('../static/images/speed_icon.png', 'static/images'),
('../static/images/prefer_icon.png', 'static/images'),
('../static/images/hotel_icon.png', 'static/images'),
('../static/images/multicast_icon.png', 'static/images'),
@@ -24,6 +26,7 @@ a = Analysis(
('../static/images/online_search_icon.png', 'static/images'),
('about.py', '.'),
('default.py', '.'),
+ ('speed.py', '.'),
('prefer.py', '.'),
('multicast.py', '.'),
('hotel.py', '.'),
diff --git a/updates/online_search/request.py b/updates/online_search/request.py
index 0992c9e4b6..b971afe549 100644
--- a/updates/online_search/request.py
+++ b/updates/online_search/request.py
@@ -1,28 +1,30 @@
-from utils.config import config
+from concurrent.futures import ThreadPoolExecutor
+from time import time
+
+from tqdm.asyncio import tqdm_asyncio
+
import utils.constants as constants
+from driver.setup import setup_driver
+from driver.utils import search_submit
+from requests_custom.utils import get_soup_requests, close_session
+from updates.proxy import get_proxy, get_proxy_next
from utils.channel import (
format_channel_name,
get_results_from_soup,
get_results_from_soup_requests,
)
+from utils.config import config
+from utils.retry import (
+ retry_func,
+ find_clickable_element_with_retry,
+)
from utils.tools import (
- check_url_by_patterns,
get_pbar_remaining,
get_soup,
format_url_with_cache,
add_url_info,
+ get_urls_from_file
)
-from updates.proxy import get_proxy, get_proxy_next
-from time import time
-from driver.setup import setup_driver
-from driver.utils import search_submit
-from utils.retry import (
- retry_func,
- find_clickable_element_with_retry,
-)
-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:
@@ -47,6 +49,8 @@ async def get_channels_by_online_search(names, callback=None):
proxy = await get_proxy(pageUrl, best=True, with_test=True)
start_time = time()
online_search_name = constants.origin_map["online_search"]
+ whitelist = get_urls_from_file(constants.whitelist_path)
+ blacklist = get_urls_from_file(constants.blacklist_path)
def process_channel_by_online_search(name):
nonlocal proxy
@@ -136,7 +140,7 @@ def process_channel_by_online_search(name):
driver,
(
By.XPATH,
- f'//a[contains(@href, "={page+1}") and contains(@href, "{name}")]',
+ f'//a[contains(@href, "={page + 1}") and contains(@href, "{name}")]',
),
retries=1,
)
@@ -151,7 +155,7 @@ def process_channel_by_online_search(name):
continue
for result in results:
url, date, resolution = result
- if url and check_url_by_patterns(url):
+ if url:
url = add_url_info(url, online_search_name)
url = format_url_with_cache(url)
info_list.append((url, date, resolution))
diff --git a/utils/channel.py b/utils/channel.py
index 5178fc4caa..76fdbd3673 100644
--- a/utils/channel.py
+++ b/utils/channel.py
@@ -19,7 +19,8 @@
)
from utils.tools import (
get_name_url,
- check_url_by_patterns,
+ check_url_by_keywords,
+ check_url_ipv_type,
get_total_urls,
process_nested_dict,
add_url_info,
@@ -219,6 +220,8 @@ def get_channel_multicast_result(result, search_result):
"""
info_result = {}
multicast_name = constants.origin_map["multicast"]
+ whitelist = get_urls_from_file(constants.whitelist_path)
+ blacklist = get_urls_from_file(constants.blacklist_path)
for name, result_obj in result.items():
info_list = [
(
@@ -242,7 +245,10 @@ def get_channel_multicast_result(result, search_result):
if result_type in search_result[result_region]
for ip in get_multicast_ip_list(result_type_urls) or []
for url, date, resolution in search_result[result_region][result_type]
- if check_url_by_patterns(f"http://{url}/rtp/{ip}")
+ if (whitelist and check_url_by_keywords(f"http://{url}/rtp/{ip}", whitelist)) or
+ (
+ check_url_ipv_type(f"http://{url}/rtp/{ip}") and not check_url_by_keywords(
+ f"http://{url}/rtp/{ip}", blacklist))
]
info_result[name] = info_list
return info_result
@@ -430,7 +436,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):
+def append_data_to_info_data(info_data, cate, name, data, origin=None, check=True, whitelist=None, blacklist=None):
"""
Append channel data to total info data
"""
@@ -446,10 +452,13 @@ def append_data_to_info_data(info_data, cate, name, data, origin=None, check=Tru
pure_url = url.partition("$")[0]
if pure_url in urls:
continue
+ if whitelist and check_url_by_keywords(url, whitelist):
+ url_origin = "important"
if (
url_origin == "important"
or (not check)
- or (check and check_url_by_patterns(pure_url))
+ or (
+ check and check_url_ipv_type(pure_url) and not check_url_by_keywords(url, blacklist))
):
info_data[cate][name].append((url, date, resolution, url_origin))
urls.append(pure_url)
@@ -464,7 +473,7 @@ def get_origin_method_name(method):
return "hotel" if method.startswith("hotel_") else method
-def append_old_data_to_info_data(info_data, cate, name, data):
+def append_old_data_to_info_data(info_data, cate, name, data, whitelist=None, blacklist=None):
"""
Append history channel data to total info data
"""
@@ -473,6 +482,8 @@ def append_old_data_to_info_data(info_data, cate, name, data):
cate,
name,
data,
+ whitelist=whitelist,
+ blacklist=blacklist
)
print("History:", len(data), end=", ")
@@ -497,11 +508,13 @@ def append_total_data(
("subscribe", subscribe_result),
("online_search", online_search_result),
]
+ whitelist = get_urls_from_file(constants.whitelist_path)
+ blacklist = get_urls_from_file(constants.blacklist_path)
for cate, channel_obj in items:
for name, old_info_list in channel_obj.items():
print(f"{name}:", end=" ")
if config.open_use_old_result and old_info_list:
- append_old_data_to_info_data(data, cate, name, old_info_list)
+ append_old_data_to_info_data(data, cate, name, old_info_list, whitelist=whitelist, blacklist=blacklist)
for method, result in total_result:
if config.open_method[method]:
origin_method = get_origin_method_name(method)
@@ -509,7 +522,7 @@ def append_total_data(
continue
name_results = get_channel_results_by_name(name, result)
append_data_to_info_data(
- data, cate, name, name_results, origin=origin_method
+ data, cate, name, name_results, origin=origin_method, whitelist=whitelist, blacklist=blacklist
)
print(f"{method.capitalize()}:", len(name_results), end=", ")
print(
@@ -534,7 +547,7 @@ def append_total_data(
data, extra_cate, name, old_info_list
)
append_data_to_info_data(
- data, extra_cate, name, urls, origin=origin_method
+ data, extra_cate, name, urls, origin=origin_method, whitelist=whitelist, blacklist=blacklist
)
print(name, f"{method.capitalize()}:", len(urls), end=", ")
print(
@@ -549,10 +562,7 @@ async def process_sort_channel_list(data, ipv6=False, callback=None):
"""
ipv6_proxy = None if (not config.open_ipv6 or ipv6) else constants.ipv6_proxy
need_sort_data = copy.deepcopy(data)
- whitelist_urls = get_urls_from_file(constants.whitelist_path)
- if whitelist_urls:
- print(f"Found {len(whitelist_urls)} whitelist urls")
- process_nested_dict(need_sort_data, seen=set(whitelist_urls), flag=r"cache:(.*)", force_str="!")
+ process_nested_dict(need_sort_data, seen=set(), flag=r"cache:(.*)", force_str="!")
result = {}
semaphore = asyncio.Semaphore(5)
@@ -576,7 +586,7 @@ async def limited_get_speed(info, ipv6_proxy, callback):
logger = get_logger(constants.sort_log_path, level=INFO, init=True)
for cate, obj in data.items():
for name, info_list in obj.items():
- info_list = sort_urls(name, info_list, logger=logger, whitelist=whitelist_urls)
+ info_list = sort_urls(name, info_list, logger=logger)
append_data_to_info_data(
result,
cate,
diff --git a/utils/config.py b/utils/config.py
index 93c22202b1..4fb878df66 100644
--- a/utils/config.py
+++ b/utils/config.py
@@ -98,6 +98,10 @@ def ipv4_num(self):
def ipv6_num(self):
return self.config.getint("Settings", "ipv6_num", fallback=15)
+ @property
+ def ipv6_support(self):
+ return self.config.getboolean("Settings", "ipv6_support", fallback=False)
+
@property
def ipv_limit(self):
return {
@@ -166,16 +170,6 @@ def open_url_info(self):
def recent_days(self):
return self.config.getint("Settings", "recent_days", fallback=30)
- @property
- def url_keywords_blacklist(self):
- return [
- keyword.strip()
- for keyword in self.config.get(
- "Settings", "url_keywords_blacklist", fallback=""
- ).split(",")
- if keyword.strip()
- ]
-
@property
def source_file(self):
return self.config.get("Settings", "source_file", fallback="config/demo.txt")
@@ -247,10 +241,6 @@ def open_use_old_result(self):
def open_sort(self):
return self.config.getboolean("Settings", "open_sort", fallback=True)
- @property
- def open_ffmpeg(self):
- return self.config.getboolean("Settings", "open_ffmpeg", fallback=True)
-
@property
def open_update_time(self):
return self.config.getboolean("Settings", "open_update_time", fallback=True)
@@ -305,18 +295,6 @@ def multicast_page_num(self):
def online_search_page_num(self):
return config.getint("Settings", "online_search_page_num", fallback=1)
- @property
- def delay_weight(self):
- return self.config.getfloat("Settings", "delay_weight", fallback=0.5)
-
- @property
- def speed_weight(self):
- return self.config.getfloat("Settings", "speed_weight", fallback=0.5)
-
- @property
- def resolution_weight(self):
- return self.config.getfloat("Settings", "resolution_weight", fallback=0.5)
-
@property
def open_empty_category(self):
return self.config.getboolean("Settings", "open_empty_category", fallback=True)
diff --git a/utils/constants.py b/utils/constants.py
index bf6463dde6..73df30397d 100644
--- a/utils/constants.py
+++ b/utils/constants.py
@@ -6,6 +6,8 @@
whitelist_path = os.path.join(config_path, "whitelist.txt")
+blacklist_path = os.path.join(config_path, "blacklist.txt")
+
subscribe_path = os.path.join(config_path, "subscribe.txt")
result_path = os.path.join(output_path, "result_new.txt")
diff --git a/utils/speed.py b/utils/speed.py
index 91a8550bdd..5896735eb4 100644
--- a/utils/speed.py
+++ b/utils/speed.py
@@ -8,7 +8,7 @@
from aiohttp import ClientSession, TCPConnector
from utils.config import config
-from utils.tools import is_ipv6, remove_cache_info, get_resolution_value
+from utils.tools import is_ipv6, remove_cache_info
async def get_speed_with_download(url: str, timeout: int = config.sort_timeout) -> dict[str, float | None]:
@@ -207,14 +207,12 @@ async def get_speed(url, ipv6_proxy=None, callback=None):
callback()
-def sort_urls(name, data, logger=None, whitelist=None):
+def sort_urls(name, data, logger=None):
"""
Sort the urls with info
"""
filter_data = []
for url, date, resolution, origin in data:
- if whitelist and url in whitelist:
- origin = "important"
result = {
"url": remove_cache_info(url),
"date": date,
@@ -249,15 +247,11 @@ def sort_urls(name, data, logger=None, whitelist=None):
filter_data.append(result)
def combined_key(item):
- speed, delay, resolution, origin = item["speed"], item["delay"], item["resolution"], item["origin"]
+ speed, origin = item["speed"], item["origin"]
if origin == "important":
return float("inf")
else:
- return (
- config.speed_weight * (speed * 1024 if speed is not None else float("-inf"))
- - config.delay_weight * (delay if delay is not None else float("inf"))
- + config.resolution_weight * (get_resolution_value(resolution) if resolution else 0)
- )
+ return speed if speed is not None else float("-inf")
filter_data.sort(key=combined_key, reverse=True)
return [
diff --git a/utils/tools.py b/utils/tools.py
index 8b38b4fedc..203876d64d 100644
--- a/utils/tools.py
+++ b/utils/tools.py
@@ -291,27 +291,14 @@ def check_url_ipv_type(url):
)
-def check_by_url_keywords_blacklist(url):
+def check_url_by_keywords(url, keywords=None):
"""
- Check by URL blacklist keywords
+ Check by URL keywords
"""
- return not any(keyword in url for keyword in config.url_keywords_blacklist)
-
-
-def check_url_by_patterns(url):
- """
- Check the url by patterns
- """
- return check_url_ipv_type(url) and check_by_url_keywords_blacklist(url)
-
-
-def filter_urls_by_patterns(urls):
- """
- Filter urls by patterns
- """
- urls = [url for url in urls if check_url_ipv_type(url)]
- urls = [url for url in urls if check_by_url_keywords_blacklist(url)]
- return urls
+ if not keywords:
+ return True
+ else:
+ return any(keyword in url for keyword in keywords)
def merge_objects(*objects):
@@ -562,6 +549,9 @@ def get_urls_from_file(path: str) -> list:
if os.path.exists(real_path):
with open(real_path, "r", encoding="utf-8") as f:
for line in f:
+ line = line.strip()
+ if line.startswith("#"):
+ continue
match = re.search(url_pattern, line)
if match:
urls.append(match.group().strip())
@@ -578,6 +568,9 @@ def get_name_urls_from_file(path: str) -> dict[str, list]:
if os.path.exists(real_path):
with open(real_path, "r", encoding="utf-8") as f:
for line in f:
+ line = line.strip()
+ if line.startswith("#"):
+ continue
name_url = get_name_url(line, pattern=txt_pattern)
if name_url and name_url[0]:
name = name_url[0]["name"]
diff --git a/version.json b/version.json
index 92f3eb0151..802a1121fb 100644
--- a/version.json
+++ b/version.json
@@ -1,4 +1,4 @@
{
- "version": "1.5.5",
+ "version": "1.5.6",
"name": "IPTV-API"
}
\ No newline at end of file