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