diff --git a/.dockerignore b/.dockerignore
index b585e690fae..3ff335c0a25 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -6,7 +6,6 @@ tkinter_ui
**/*.md
**/*.jpg
**/*.png
-version.json
.git
.github
.gitignore
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 65e16db80a2..bf1ff9457db 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -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
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 75def33c10b..b1b22b4938b 100644
--- a/CHANGELOG.md
+++ b/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)
+- 🪄 调整默认不显示接口信息,兼容更多播放器
+
+
+ English
+
+- ❤️ 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
+
+
+
## v1.5.8
### 2024/12/30
diff --git a/Pipfile b/Pipfile
index 1ba99cb1bb8..fe82df68503 100644
--- a/Pipfile
+++ b/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"
diff --git a/Pipfile.lock b/Pipfile.lock
index 8e55c0c9076..c8a0ec2b63c 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -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",
diff --git a/README.md b/README.md
index e6a6611966d..cffad296fa9 100644
--- a/README.md
+++ b/README.md
@@ -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 |
## 快速上手
diff --git a/README_en.md b/README_en.md
index ce30f74c678..e9ae58666f2 100644
--- a/README_en.md
+++ b/README_en.md
@@ -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
diff --git a/config/config.ini b/config/config.ini
index de46bffa38e..ac4d1a70e7f 100644
--- a/config/config.ini
+++ b/config/config.ini
@@ -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
\ No newline at end of file
+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
\ No newline at end of file
diff --git a/docs/config.md b/docs/config.md
index db997b4224c..bac551d511a 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -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 |
\ No newline at end of file
+| time_zone | 时区,可用于控制更新时间显示的时区,可选值:Asia/Shanghai 或其它时区编码 | Asia/Shanghai |
+| urls_limit | 单个频道接口数量 | 10 |
+| update_time_position | 更新时间显示位置,需要开启 open_update_time 才能生效,可选值:top、bottom,top: 显示于结果顶部,bottom: 显示于结果底部 | top |
\ No newline at end of file
diff --git a/docs/config_en.md b/docs/config_en.md
index e165697e698..9f3027672b0 100644
--- a/docs/config_en.md
+++ b/docs/config_en.md
@@ -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 |
\ No newline at end of file
+| 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 |
\ No newline at end of file
diff --git a/main.py b/main.py
index cb43e5e4fad..17c52a8cdbc 100644
--- a/main.py
+++ b/main.py
@@ -30,7 +30,8 @@
format_interval,
check_ipv6_support,
resource_path,
- get_urls_from_file
+ get_urls_from_file,
+ get_version_info
)
@@ -116,6 +117,9 @@ async def main(self):
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 @@ async def main(self):
"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 @@ def stop(self):
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()
diff --git a/tkinter_ui/default.py b/tkinter_ui/default.py
index 1ac94ba588c..728aaf533a1 100644
--- a/tkinter_ui/default.py
+++ b/tkinter_ui/default.py
@@ -292,6 +292,19 @@ def init_ui(self, root):
)
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("<>", 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 @@ def init_ui(self, root):
)
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("", 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 @@ def update_request_timeout(self, event):
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 @@ def update_ipv6_support(self):
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 @@ def change_entry_state(self, state):
"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",
diff --git a/tkinter_ui/tkinter_ui.py b/tkinter_ui/tkinter_ui.py
index d57e3fba86e..17cb75a01d9 100644
--- a/tkinter_ui/tkinter_ui.py
+++ b/tkinter_ui/tkinter_ui.py
@@ -6,7 +6,7 @@
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 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 @@ def view_result_link_callback(self, event):
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)
diff --git a/updates/fofa/fofa_hotel_region_result.pkl b/updates/fofa/fofa_hotel_region_result.pkl
index d1cee976a59..7f988ad372d 100644
Binary files a/updates/fofa/fofa_hotel_region_result.pkl and b/updates/fofa/fofa_hotel_region_result.pkl differ
diff --git a/utils/channel.py b/utils/channel.py
index 4bbafc0ff4b..c7c2a1a88ee 100644
--- a/utils/channel.py
+++ b/utils/channel.py
@@ -1,7 +1,6 @@
import asyncio
import base64
import copy
-import datetime
import os
import pickle
import re
@@ -27,10 +26,10 @@
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}")
diff --git a/utils/config.py b/utils/config.py
index e270e4dff5c..85900eaba3e 100644
--- a/utils/config.py
+++ b/utils/config.py
@@ -307,6 +307,14 @@ def app_port(self):
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
diff --git a/utils/tools.py b/utils/tools.py
index c519fa048d9..20eaf9ebb4f 100644
--- a/utils/tools.py
+++ b/utils/tools.py
@@ -1,5 +1,6 @@
import datetime
import ipaddress
+import json
import logging
import os
import re
@@ -11,6 +12,7 @@
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)
diff --git a/version.json b/version.json
index d348031dd39..b631edb0179 100644
--- a/version.json
+++ b/version.json
@@ -1,4 +1,4 @@
{
- "version": "1.5.8",
+ "version": "1.5.9",
"name": "IPTV-API"
}
\ No newline at end of file