Merge branch 'dev'
This commit is contained in:
commit
e52016dadc
@ -6,7 +6,6 @@ tkinter_ui
|
||||
**/*.md
|
||||
**/*.jpg
|
||||
**/*.png
|
||||
version.json
|
||||
.git
|
||||
.github
|
||||
.gitignore
|
||||
|
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@ -33,7 +33,7 @@ jobs:
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: IPTV
|
||||
name: IPTV-API
|
||||
path: dist
|
||||
|
||||
- name: Get version from version.json
|
||||
@ -76,6 +76,6 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: dist/IPTV.exe
|
||||
asset_name: IPTV.exe
|
||||
asset_path: dist/IPTV-API.exe
|
||||
asset_name: IPTV-API.exe
|
||||
asset_content_type: application/octet-stream
|
||||
|
48
CHANGELOG.md
48
CHANGELOG.md
@ -1,5 +1,53 @@
|
||||
# 更新日志(Changelog)
|
||||
|
||||
## v1.5.9
|
||||
|
||||
### 2025/1/8
|
||||
|
||||
- ❤️ 2025年第一次更新,祝大家新年快乐,万事如意
|
||||
- ✨ 公众号详细教程文章已发布,欢迎关注`Govin`公众号获取
|
||||
- ✨ 新增支持`rtmp`协议接口(#780)
|
||||
- ✨ 新增支持修改更新时间位置(`update_time_position`)(#755)
|
||||
- ✨ 新增支持修改时区(`time_zone`)(#759)
|
||||
- ✨ 更新组播源与酒店源离线数据,增加`广东移动组播RTP`(#773)
|
||||
- ✨ 更新Github CDN代理地址(#796)
|
||||
- ✨ GUI使用Github工作流基于源码自动构建并发布,唯一下载途径是[Release](https://github.com/Guovin/iptv-api/releases)
|
||||
,若安全软件有误报,请添加信任
|
||||
- ✨ 增加版本信息打印输出
|
||||
- ✨ 更新部分教程文档图片
|
||||
- 🐛 修复m3u更新时间logo显示问题(#794)
|
||||
- 🐛 修复测速阶段出现`cookie illegal key`问题(#728,#787)
|
||||
- 🐛 修复白名单接口排序与接口信息命名问题(#765)
|
||||
- 🐛 修复组播源更新结果异常问题
|
||||
- 🐛 修复写入结果目录为空问题
|
||||
- 🪄 调整接口状态码判断,只处理`200`状态码(#779)
|
||||
- 🪄 调整默认不显示接口信息,兼容更多播放器
|
||||
|
||||
<details>
|
||||
<summary>English</summary>
|
||||
|
||||
- ❤️ First update of 2025, wishing everyone a Happy New Year and all the best
|
||||
- ✨ Detailed tutorial articles have been published on the `Govin` public account, welcome to follow for more information
|
||||
- ✨ Added support for `rtmp` protocol interface (#780)
|
||||
- ✨ Added support for modifying update time position (`update_time_position`) (#755)
|
||||
- ✨ Added support for modifying time zone (`time_zone`) (#759)
|
||||
- ✨ Updated offline data for multicast sources and hotel sources, added `Guangdong Mobile Multicast RTP` (#773)
|
||||
- ✨ Updated GitHub CDN proxy address (#796)
|
||||
- ✨ GUI is automatically built and released based on the source code using GitHub workflows, the only download method
|
||||
is [Release](https://github.com/Guovin/iptv-api/releases). If there are false positives from security software, please
|
||||
add it to the trust list
|
||||
- ✨ Added version information print output
|
||||
- ✨ Updated some tutorial document images
|
||||
- 🐛 Fixed m3u update time logo display issue (#794)
|
||||
- 🐛 Fixed `cookie illegal key` issue during speed test phase (#728, #787)
|
||||
- 🐛 Fixed whitelist interface sorting and interface information naming issue (#765)
|
||||
- 🐛 Fixed abnormal results issue for multicast source updates
|
||||
- 🐛 Fixed empty result directory issue
|
||||
- 🪄 Adjusted interface status code judgment to only process `200` status code (#779)
|
||||
- 🪄 Adjusted to hide interface information by default, compatible with more players
|
||||
|
||||
</details>
|
||||
|
||||
## v1.5.8
|
||||
|
||||
### 2024/12/30
|
||||
|
2
Pipfile
2
Pipfile
@ -25,6 +25,7 @@ flask = "*"
|
||||
opencc-python-reimplemented = "*"
|
||||
pillow = "*"
|
||||
m3u8 = "*"
|
||||
pytz = "*"
|
||||
|
||||
[packages]
|
||||
requests = "*"
|
||||
@ -37,6 +38,7 @@ opencc-python-reimplemented = "*"
|
||||
gunicorn = "*"
|
||||
pillow = "*"
|
||||
m3u8 = "*"
|
||||
pytz = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.13"
|
||||
|
18
Pipfile.lock
generated
18
Pipfile.lock
generated
@ -1,7 +1,7 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "7ccc0e57a8b15b60d41f3ca958d5da9a7391392e60967d053377d60a93998b1b"
|
||||
"sha256": "919159f0f1a8d07db897c47ac59358ec36f164cf6698d1b21fdeef94a72340dc"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
@ -780,6 +780,14 @@
|
||||
"markers": "python_version >= '3.9'",
|
||||
"version": "==0.2.1"
|
||||
},
|
||||
"pytz": {
|
||||
"hashes": [
|
||||
"sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a",
|
||||
"sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"
|
||||
],
|
||||
"index": "aliyun",
|
||||
"version": "==2024.2"
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760",
|
||||
@ -1815,6 +1823,14 @@
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.7.1"
|
||||
},
|
||||
"pytz": {
|
||||
"hashes": [
|
||||
"sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a",
|
||||
"sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"
|
||||
],
|
||||
"index": "aliyun",
|
||||
"version": "==2024.2"
|
||||
},
|
||||
"pywin32-ctypes": {
|
||||
"hashes": [
|
||||
"sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8",
|
||||
|
@ -164,7 +164,7 @@ https://cdn.jsdelivr.net/gh/Guovin/iptv-api@gd/source.json
|
||||
| open_subscribe | 开启订阅源功能 | False |
|
||||
| open_update | 开启更新,用于控制是否更新接口,若关闭则所有工作模式(获取接口和测速)均停止 | True |
|
||||
| open_update_time | 开启显示更新时间 | True |
|
||||
| open_url_info | 开启显示接口说明信息,用于控制是否显示接口来源、分辨率、协议类型等信息,为$符号后的内容,播放软件使用该信息对接口进行描述,若部分播放器(如PotPlayer)不支持解析导致无法播放可关闭 | True |
|
||||
| open_url_info | 开启显示接口说明信息,用于控制是否显示接口来源、分辨率、协议类型等信息,为$符号后的内容,播放软件使用该信息对接口进行描述,若部分播放器(如PotPlayer)不支持解析导致无法播放可关闭 | False |
|
||||
| open_use_cache | 开启使用本地缓存数据,适用于查询请求失败场景(仅针对酒店源与组播源) | True |
|
||||
| open_use_old_result | 开启使用历史更新结果(包含模板与结果文件的接口),合并至本次更新中 | True |
|
||||
| app_port | 页面服务端口,用于控制页面服务的端口号 | 8000 |
|
||||
@ -190,7 +190,9 @@ https://cdn.jsdelivr.net/gh/Guovin/iptv-api@gd/source.json
|
||||
| sort_timeout | 单个接口测速超时时长,单位秒(s);数值越大测速所属时间越长,能提高获取接口数量,但质量会有所下降;数值越小测速所需时间越短,能获取低延时的接口,质量较好;调整此值能优化更新时间 | 10 |
|
||||
| source_file | 模板文件路径 | config/demo.txt |
|
||||
| subscribe_num | 结果中偏好的订阅源接口数量 | 10 |
|
||||
| time_zone | 时区,可用于控制更新时间显示的时区,可选值:Asia/Shanghai 或其它时区编码 | Asia/Shanghai |
|
||||
| urls_limit | 单个频道接口数量 | 10 |
|
||||
| update_time_position | 更新时间显示位置,需要开启 open_update_time 才能生效,可选值:top、bottom,top: 显示于结果顶部,bottom: 显示于结果底部 | top |
|
||||
|
||||
## 快速上手
|
||||
|
||||
|
@ -164,7 +164,7 @@ https://cdn.jsdelivr.net/gh/Guovin/iptv-api@gd/source.json
|
||||
| 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 to display interface description information, used to control whether to display interface source, resolution, protocol type and other information, the content after the $ symbol, the playback software uses this information to describe the interface, if some players (such as PotPlayer) do not support parsing and cannot play, you can turn it off | True |
|
||||
| open_url_info | Enable to display interface description information, used to control whether to display interface source, resolution, protocol type and other information, the content after the $ symbol, the playback software uses this information to describe the interface, if some players (such as PotPlayer) do not support parsing and cannot play, you can turn it off | False |
|
||||
| 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 |
|
||||
| app_port | Page service port, used to control the port number of the page service | 8000 |
|
||||
@ -190,7 +190,9 @@ https://cdn.jsdelivr.net/gh/Guovin/iptv-api@gd/source.json
|
||||
| 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 |
|
||||
| subscribe_num | The number of preferred subscribe source interfaces in the results | 10 |
|
||||
| time_zone | Time zone, can be used to control the time zone displayed by the update time, optional values: Asia/Shanghai or other time zone codes | Asia/Shanghai |
|
||||
| urls_limit | Number of interfaces per channel | 10 |
|
||||
| update_time_position | Update time display position, need to enable open_update_time to take effect, optional values: top, bottom, top: display at the top of the result, bottom: display at the bottom of the result | top |
|
||||
|
||||
## Quick Start
|
||||
|
||||
|
@ -42,7 +42,7 @@ open_update = True
|
||||
# 开启显示更新时间; 可选值: True, False | Enable display update time; Optional values: True, False
|
||||
open_update_time = True
|
||||
# 开启显示接口说明信息,用于控制是否显示接口来源、分辨率、协议类型等信息,为$符号后的内容,播放软件使用该信息对接口进行描述,若部分播放器(如PotPlayer)不支持解析导致无法播放可关闭; 可选值: True, False | Enable to display interface description information, used to control whether to display interface source, resolution, protocol type and other information, the content after the $ symbol, the playback software uses this information to describe the interface, if some players (such as PotPlayer) do not support parsing and cannot play, you can turn it off; Optional values: True, False
|
||||
open_url_info = True
|
||||
open_url_info = False
|
||||
# 开启使用本地缓存数据,适用于查询请求失败场景(仅针对酒店源与组播源); 可选值: True, False | Enable to use local cached data, suitable for query request failure scenarios (only for hotel source and multicast source); Optional values: True, False
|
||||
open_use_cache = True
|
||||
# 开启使用历史更新结果(包含模板与结果文件的接口),合并至本次更新中; 可选值: True, False | Enable to use historical update results (including interfaces of templates and result files), merged into this update; Optional values: True, False
|
||||
@ -93,5 +93,9 @@ sort_timeout = 10
|
||||
source_file = config/demo.txt
|
||||
# 结果中偏好的订阅源接口数量 | Preferred number of subscription source interfaces in the result
|
||||
subscribe_num = 10
|
||||
# 时区,可用于控制更新时间显示的时区,可选值:Asia/Shanghai 或其它时区编码 | Time zone, can be used to control the time zone displayed by the update time, optional values: Asia/Shanghai or other time zone codes
|
||||
time_zone = Asia/Shanghai
|
||||
# 单个频道接口数量 | Number of interfaces per channel
|
||||
urls_limit = 10
|
||||
urls_limit = 10
|
||||
# 更新时间显示位置,需要开启 open_update_time 才能生效,可选值:top、bottom,top: 显示于结果顶部,bottom: 显示于结果底部 | Update time display position, need to enable open_update_time to take effect, optional values: top, bottom, top: display at the top of the result, bottom: display at the bottom of the result
|
||||
update_time_position = top
|
@ -20,7 +20,7 @@
|
||||
| open_subscribe | 开启订阅源功能 | False |
|
||||
| open_update | 开启更新,用于控制是否更新接口,若关闭则所有工作模式(获取接口和测速)均停止 | True |
|
||||
| open_update_time | 开启显示更新时间 | True |
|
||||
| open_url_info | 开启显示接口说明信息,用于控制是否显示接口来源、分辨率、协议类型等信息,为$符号后的内容,播放软件使用该信息对接口进行描述,若部分播放器(如PotPlayer)不支持解析导致无法播放可关闭 | True |
|
||||
| open_url_info | 开启显示接口说明信息,用于控制是否显示接口来源、分辨率、协议类型等信息,为$符号后的内容,播放软件使用该信息对接口进行描述,若部分播放器(如PotPlayer)不支持解析导致无法播放可关闭 | False |
|
||||
| open_use_cache | 开启使用本地缓存数据,适用于查询请求失败场景(仅针对酒店源与组播源) | True |
|
||||
| open_use_old_result | 开启使用历史更新结果(包含模板与结果文件的接口),合并至本次更新中 | True |
|
||||
| app_port | 页面服务端口,用于控制页面服务的端口号 | 8000 |
|
||||
@ -46,4 +46,6 @@
|
||||
| sort_timeout | 单个接口测速超时时长,单位秒(s);数值越大测速所属时间越长,能提高获取接口数量,但质量会有所下降;数值越小测速所需时间越短,能获取低延时的接口,质量较好;调整此值能优化更新时间 | 10 |
|
||||
| source_file | 模板文件路径 | config/demo.txt |
|
||||
| subscribe_num | 结果中偏好的订阅源接口数量 | 10 |
|
||||
| urls_limit | 单个频道接口数量 | 10 |
|
||||
| time_zone | 时区,可用于控制更新时间显示的时区,可选值:Asia/Shanghai 或其它时区编码 | Asia/Shanghai |
|
||||
| urls_limit | 单个频道接口数量 | 10 |
|
||||
| update_time_position | 更新时间显示位置,需要开启 open_update_time 才能生效,可选值:top、bottom,top: 显示于结果顶部,bottom: 显示于结果底部 | top |
|
@ -20,7 +20,7 @@
|
||||
| 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 to display interface description information, used to control whether to display interface source, resolution, protocol type and other information, the content after the $ symbol, the playback software uses this information to describe the interface, if some players (such as PotPlayer) do not support parsing and cannot play, you can turn it off | True |
|
||||
| open_url_info | Enable to display interface description information, used to control whether to display interface source, resolution, protocol type and other information, the content after the $ symbol, the playback software uses this information to describe the interface, if some players (such as PotPlayer) do not support parsing and cannot play, you can turn it off | False |
|
||||
| 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 |
|
||||
| app_port | Page service port, used to control the port number of the page service | 8000 |
|
||||
@ -46,4 +46,6 @@
|
||||
| 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 |
|
||||
| subscribe_num | The number of preferred subscribe source interfaces in the results | 10 |
|
||||
| urls_limit | Number of interfaces per channel | 10 |
|
||||
| time_zone | Time zone, can be used to control the time zone displayed by the update time, optional values: Asia/Shanghai or other time zone codes | Asia/Shanghai |
|
||||
| urls_limit | Number of interfaces per channel | 10 |
|
||||
| update_time_position | Update time display position, need to enable open_update_time to take effect, optional values: top, bottom, top: display at the top of the result, bottom: display at the bottom of the result | top |
|
10
main.py
10
main.py
@ -30,7 +30,8 @@ from utils.tools import (
|
||||
format_interval,
|
||||
check_ipv6_support,
|
||||
resource_path,
|
||||
get_urls_from_file
|
||||
get_urls_from_file,
|
||||
get_version_info
|
||||
)
|
||||
|
||||
|
||||
@ -116,6 +117,9 @@ class UpdateSource:
|
||||
for channel_obj in self.channel_items.values()
|
||||
for name in channel_obj.keys()
|
||||
]
|
||||
if not channel_names:
|
||||
print(f"❌ No channel names found! Please check the {config.source_file}!")
|
||||
return
|
||||
await self.visit_page(channel_names)
|
||||
self.tasks = []
|
||||
append_total_data(
|
||||
@ -169,7 +173,7 @@ class UpdateSource:
|
||||
"wb",
|
||||
) as file:
|
||||
pickle.dump(channel_data_cache, file)
|
||||
convert_to_m3u()
|
||||
convert_to_m3u(channel_names[0])
|
||||
print(
|
||||
f"🥳 Update completed! Total time spent: {format_interval(time() - main_start_time)}. Please check the {user_final_file} file!"
|
||||
)
|
||||
@ -209,6 +213,8 @@ class UpdateSource:
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
info = get_version_info()
|
||||
print(f"ℹ️ {info['name']} Version: {info['version']}")
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
update_source = UpdateSource()
|
||||
|
@ -292,6 +292,19 @@ class DefaultUI:
|
||||
)
|
||||
self.open_update_time_checkbutton.pack(side=tk.LEFT, padx=4, pady=8)
|
||||
|
||||
self.update_time_position_label = tk.Label(
|
||||
frame_default_open_update_info_column1, text="位置:", width=3
|
||||
)
|
||||
self.update_time_position_label.pack(side=tk.LEFT, padx=4, pady=8)
|
||||
self.update_time_position_combo = ttk.Combobox(frame_default_open_update_info_column1, width=5)
|
||||
self.update_time_position_combo.pack(side=tk.LEFT, padx=4, pady=8)
|
||||
self.update_time_position_combo["values"] = ("顶部", "底部")
|
||||
if config.update_time_position == "bottom":
|
||||
self.update_time_position_combo.current(1)
|
||||
else:
|
||||
self.update_time_position_combo.current(0)
|
||||
self.update_time_position_combo.bind("<<ComboboxSelected>>", self.update_update_time_position)
|
||||
|
||||
self.open_url_info_label = tk.Label(
|
||||
frame_default_open_update_info_column2, text="显示接口信息:", width=12
|
||||
)
|
||||
@ -345,6 +358,18 @@ class DefaultUI:
|
||||
)
|
||||
self.ipv6_support_checkbutton.pack(side=tk.LEFT, padx=4, pady=8)
|
||||
|
||||
frame_time_zone = tk.Frame(root)
|
||||
frame_time_zone.pack(fill=tk.X)
|
||||
|
||||
self.time_zone_label = tk.Label(
|
||||
frame_time_zone, text="时区:", width=12
|
||||
)
|
||||
self.time_zone_label.pack(side=tk.LEFT, padx=4, pady=8)
|
||||
self.time_zone_entry = tk.Entry(frame_time_zone, width=18)
|
||||
self.time_zone_entry.pack(side=tk.LEFT, padx=4, pady=8)
|
||||
self.time_zone_entry.insert(0, config.time_zone)
|
||||
self.time_zone_entry.bind("<KeyRelease>", self.update_time_zone)
|
||||
|
||||
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)
|
||||
@ -431,6 +456,9 @@ class DefaultUI:
|
||||
def update_urls_limit(self, event):
|
||||
config.set("Settings", "urls_limit", self.urls_limit_entry.get())
|
||||
|
||||
def update_time_zone(self, event):
|
||||
config.set("Settings", "time_zone", self.time_zone_entry.get())
|
||||
|
||||
def update_open_update_time(self):
|
||||
config.set("Settings", "open_update_time", str(self.open_update_time_var.get()))
|
||||
|
||||
@ -450,6 +478,10 @@ class DefaultUI:
|
||||
def update_ipv_type(self, event):
|
||||
config.set("Settings", "ipv_type", self.ipv_type_combo.get())
|
||||
|
||||
def update_update_time_position(self, event):
|
||||
config.set("Settings", "update_time_position",
|
||||
'bottom' if self.update_time_position_combo.get() == '底部' else 'top')
|
||||
|
||||
def edit_whitelist_file(self):
|
||||
path = resource_path(constants.whitelist_path)
|
||||
if os.path.exists(path):
|
||||
@ -473,11 +505,13 @@ class DefaultUI:
|
||||
"request_timeout_entry",
|
||||
"source_file_entry",
|
||||
"source_file_button",
|
||||
"time_zone_entry",
|
||||
"final_file_entry",
|
||||
"final_file_button",
|
||||
"open_keep_all_checkbutton",
|
||||
"open_m3u_result_checkbutton",
|
||||
"urls_limit_entry",
|
||||
"update_time_position_combo",
|
||||
"open_update_time_checkbutton",
|
||||
"open_url_info_checkbutton",
|
||||
"open_empty_category_checkbutton",
|
||||
|
@ -6,7 +6,7 @@ import tkinter as tk
|
||||
from tkinter import messagebox
|
||||
from PIL import Image, ImageTk
|
||||
from utils.config import config
|
||||
from utils.tools import resource_path
|
||||
from utils.tools import resource_path, get_version_info
|
||||
from main import UpdateSource
|
||||
import asyncio
|
||||
import threading
|
||||
@ -19,14 +19,12 @@ from multicast import MulticastUI
|
||||
from hotel import HotelUI
|
||||
from subscribe import SubscribeUI
|
||||
from online_search import OnlineSearchUI
|
||||
import json
|
||||
from utils.speed import check_ffmpeg_installed_status
|
||||
|
||||
|
||||
class TkinterUI:
|
||||
def __init__(self, root):
|
||||
with open(resource_path("version.json"), "r", encoding="utf-8") as f:
|
||||
info = json.load(f)
|
||||
info = get_version_info()
|
||||
self.root = root
|
||||
self.root.title(info.get("name", ""))
|
||||
self.version = info.get("version", "")
|
||||
@ -46,46 +44,6 @@ class TkinterUI:
|
||||
webbrowser.open_new_tab(self.result_url)
|
||||
|
||||
def save_config(self):
|
||||
config_values = {
|
||||
"open_driver": self.default_ui.open_driver_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_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(),
|
||||
"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_supply": self.prefer_ui.open_supply_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(),
|
||||
"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(),
|
||||
"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():
|
||||
config.set("Settings", key, str(value))
|
||||
config.save()
|
||||
messagebox.showinfo("提示", "保存成功")
|
||||
|
||||
@ -274,7 +232,7 @@ def get_root_location(root):
|
||||
screen_width = root.winfo_screenwidth()
|
||||
screen_height = root.winfo_screenheight()
|
||||
width = 500
|
||||
height = 600
|
||||
height = 650
|
||||
x = (screen_width / 2) - (width / 2)
|
||||
y = (screen_height / 2) - (height / 2)
|
||||
return (width, height, x, y)
|
||||
|
@ -1,7 +1,6 @@
|
||||
import asyncio
|
||||
import base64
|
||||
import copy
|
||||
import datetime
|
||||
import os
|
||||
import pickle
|
||||
import re
|
||||
@ -27,10 +26,10 @@ from utils.tools import (
|
||||
add_url_info,
|
||||
remove_cache_info,
|
||||
resource_path,
|
||||
write_content_into_txt,
|
||||
get_urls_from_file,
|
||||
get_name_urls_from_file,
|
||||
get_logger,
|
||||
get_datetime_now
|
||||
)
|
||||
|
||||
|
||||
@ -634,23 +633,12 @@ def write_channel_to_file(data, ipv6=False, callback=None):
|
||||
if any(pref in ipv_type_prefer for pref in ["自动", "auto"]) or not ipv_type_prefer:
|
||||
ipv_type_prefer = ["ipv6", "ipv4"] if ipv6 else ["ipv4", "ipv6"]
|
||||
origin_type_prefer = config.origin_type_prefer
|
||||
if config.open_update_time:
|
||||
now = datetime.datetime.now()
|
||||
if os.environ.get("GITHUB_ACTIONS"):
|
||||
now += datetime.timedelta(hours=8)
|
||||
update_time = now.strftime("%Y-%m-%d %H:%M:%S")
|
||||
update_time_url = next(
|
||||
(get_total_urls(info_list, ipv_type_prefer, origin_type_prefer)[0]
|
||||
for channel_obj in data.values()
|
||||
for info_list in channel_obj.values() if info_list),
|
||||
"url"
|
||||
)
|
||||
write_content_into_txt(f"🕘️更新时间,#genre#", path, newline=False)
|
||||
write_content_into_txt(f"{update_time},{update_time_url}", path)
|
||||
write_content_into_txt("", path)
|
||||
first_cate = True
|
||||
content = ""
|
||||
for cate, channel_obj in data.items():
|
||||
print(f"\n{cate}:", end=" ")
|
||||
write_content_into_txt(f"{cate},#genre#", path)
|
||||
content += f"{'\n\n' if not first_cate else ''}{cate},#genre#"
|
||||
first_cate = False
|
||||
channel_obj_keys = channel_obj.keys()
|
||||
names_len = len(list(channel_obj_keys))
|
||||
for i, name in enumerate(channel_obj_keys):
|
||||
@ -663,17 +651,31 @@ def write_channel_to_file(data, ipv6=False, callback=None):
|
||||
no_result_name.append(name)
|
||||
continue
|
||||
for url in channel_urls:
|
||||
write_content_into_txt(f"{name},{url}", path, callback=callback)
|
||||
content += f"\n{name},{url}"
|
||||
if callback:
|
||||
callback()
|
||||
print()
|
||||
write_content_into_txt("", path)
|
||||
if open_empty_category and no_result_name:
|
||||
print("\n🈳 No result channel name:")
|
||||
write_content_into_txt("🈳无结果频道,#genre#", path)
|
||||
content += "\n\n🈳无结果频道,#genre#"
|
||||
for i, name in enumerate(no_result_name):
|
||||
end_char = ", " if i < len(no_result_name) - 1 else ""
|
||||
print(name, end=end_char)
|
||||
write_content_into_txt(f"{name},url", path)
|
||||
content += f"\n{name},url"
|
||||
print()
|
||||
if config.open_update_time:
|
||||
update_time_url = next(
|
||||
(get_total_urls(info_list, ipv_type_prefer, origin_type_prefer)[0]
|
||||
for channel_obj in data.values()
|
||||
for info_list in channel_obj.values() if info_list),
|
||||
"url"
|
||||
)
|
||||
if config.update_time_position == "top":
|
||||
content = f"🕘️更新时间,#genre#\n{get_datetime_now()},{update_time_url}\n\n{content}"
|
||||
else:
|
||||
content += f"\n\n🕘️更新时间,#genre#\n{get_datetime_now()},{update_time_url}"
|
||||
with open(path, "w", encoding="utf-8") as f:
|
||||
f.write(content)
|
||||
except Exception as e:
|
||||
print(f"❌ Write channel to file failed: {e}")
|
||||
|
||||
|
@ -307,6 +307,14 @@ class ConfigManager:
|
||||
def open_supply(self):
|
||||
return self.config.getboolean("Settings", "open_supply", fallback=True)
|
||||
|
||||
@property
|
||||
def update_time_position(self):
|
||||
return self.config.get("Settings", "update_time_position", fallback="top")
|
||||
|
||||
@property
|
||||
def time_zone(self):
|
||||
return self.config.get("Settings", "time_zone", fallback="Asia/Shanghai")
|
||||
|
||||
def load(self):
|
||||
"""
|
||||
Load the config
|
||||
|
@ -1,5 +1,6 @@
|
||||
import datetime
|
||||
import ipaddress
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
@ -11,6 +12,7 @@ from collections import defaultdict
|
||||
from logging.handlers import RotatingFileHandler
|
||||
from time import time
|
||||
|
||||
import pytz
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
from flask import send_file, make_response
|
||||
@ -348,7 +350,7 @@ def get_ip_address():
|
||||
return f"http://{ip}:{config.app_port}"
|
||||
|
||||
|
||||
def convert_to_m3u():
|
||||
def convert_to_m3u(first_channel_name=None):
|
||||
"""
|
||||
Convert result txt to m3u format
|
||||
"""
|
||||
@ -373,7 +375,7 @@ def convert_to_m3u():
|
||||
r"(CCTV|CETV)-(\d+)(\+.*)?",
|
||||
lambda m: f"{m.group(1)}{m.group(2)}"
|
||||
+ ("+" if m.group(3) else ""),
|
||||
original_channel_name,
|
||||
first_channel_name if current_group == "🕘️更新时间" else original_channel_name,
|
||||
)
|
||||
m3u_output += f'#EXTINF:-1 tvg-name="{processed_channel_name}" tvg-logo="https://live.fanmingming.cn/tv/{processed_channel_name}.png"'
|
||||
if current_group:
|
||||
@ -499,16 +501,19 @@ def resource_path(relative_path, persistent=False):
|
||||
return total_path
|
||||
|
||||
|
||||
def write_content_into_txt(content, path=None, newline=True, callback=None):
|
||||
def write_content_into_txt(content, path=None, position=None, callback=None):
|
||||
"""
|
||||
Write content into txt file
|
||||
"""
|
||||
if not path:
|
||||
return
|
||||
|
||||
with open(path, "a", encoding="utf-8") as f:
|
||||
if newline:
|
||||
f.write(f"\n{content}")
|
||||
mode = "r+" if position == "top" else "a"
|
||||
with open(path, mode, encoding="utf-8") as f:
|
||||
if position == "top":
|
||||
existing_content = f.read()
|
||||
f.seek(0, 0)
|
||||
f.write(f"{content}\n{existing_content}")
|
||||
else:
|
||||
f.write(content)
|
||||
|
||||
@ -579,3 +584,20 @@ def get_name_urls_from_file(path: str) -> dict[str, list]:
|
||||
if url not in name_urls[name]:
|
||||
name_urls[name].append(url)
|
||||
return name_urls
|
||||
|
||||
|
||||
def get_datetime_now():
|
||||
"""
|
||||
Get the datetime now
|
||||
"""
|
||||
now = datetime.datetime.now()
|
||||
time_zone = pytz.timezone(config.time_zone)
|
||||
return now.astimezone(time_zone).strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
|
||||
def get_version_info():
|
||||
"""
|
||||
Get the version info
|
||||
"""
|
||||
with open(resource_path("version.json"), "r", encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
"version": "1.5.8",
|
||||
"version": "1.5.9",
|
||||
"name": "IPTV-API"
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user