diff --git a/.gitignore b/.gitignore index 2cf1da454af..9315b48695d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ dist -build \ No newline at end of file +build +result_new.log \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 60a4bfcb468..0d9cbc6021f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # 更新日志(Changelog) +## v1.3.6 + +### 2024/8/22 + +- 新增酒店源更新,支持 Tonkiang、FOFA 两种工作模式(Added hotel source updates, supporting Tonkiang and FOFA working modes) +- 重构 UI 界面软件,新增帮助-关于、获取频道名称编辑、酒店源相关配置、软件图标(Refactored UI interface software, added Help-About, channel name editing, hotel source related configuration, and software icon) +- 移除关注频道相关配置(Removed configuration related to followed channels) +- 修复 Docker 定时任务未执行问题(Fixed issue with Docker scheduled tasks not executing) +- 修复使用历史结果时频道数据异常问题(Fixed issue with channel data anomalies when using historical results) +- 优化 UI 界面软件运行生成配置目录,方便查看与修改(Optimized UI interface software to generate configuration directory for easier viewing and modification) + ## v1.3.5 ### 2024/8/14 diff --git a/README.md b/README.md index d0e25541f1e..e565db44fba 100644 --- a/README.md +++ b/README.md @@ -112,4 +112,4 @@ Fork 本项目并开启工作流更新 ## 赞赏 -![image](./docs/images/appreciate.jpg) +![image](./static/images/appreciate.jpg) diff --git a/README_en.md b/README_en.md index 417e846cfaf..265cf7b7e98 100644 --- a/README_en.md +++ b/README_en.md @@ -112,4 +112,4 @@ If you don't want to bother, and my configuration just meets your needs, you can ## Appreciate -![image](./docs/images/appreciate.jpg) +![image](./static/images/appreciate.jpg) diff --git a/config/config.ini b/config/config.ini index 5fb336959a2..e9189845a86 100644 --- a/config/config.ini +++ b/config/config.ini @@ -26,7 +26,7 @@ multicast_region_list = 广东 multicast_page_num = 5 open_proxy = False open_driver = False -open_hotel = False +open_hotel = True open_hotel_tonkiang = True open_hotel_fofa = True hotel_region_list = 广东 diff --git a/main.py b/main.py index c77e0d7db0b..bc293e06aae 100644 --- a/main.py +++ b/main.py @@ -1,5 +1,5 @@ import asyncio -from utils.config import config +from utils.config import config, copy_config from utils.channel import ( get_channel_items, append_data_to_info_data, @@ -34,7 +34,23 @@ def show_result(): user_final_file = config.get("Settings", "final_file") with open(user_final_file, "r", encoding="utf-8") as file: content = file.read() - return render_template_string("
{{ content }}
", content=content) + return render_template_string( + "
{{ content }}
", + content=content, + ) + + +@app.route("/log") +def show_log(): + user_log_file = "output/" + ( + "user_result.log" if os.path.exists("config/user_config.ini") else "result.log" + ) + with open(user_log_file, "r", encoding="utf-8") as file: + content = file.read() + return render_template_string( + "
{{ content }}
", + content=content, + ) class UpdateSource: @@ -94,6 +110,8 @@ def sort_pbar_update(self): async def main(self): try: self.channel_items = get_channel_items() + if self.run_ui: + copy_config() channel_names = [ name for channel_obj in self.channel_items.values() @@ -171,8 +189,6 @@ async def main(self): ) update_file(user_log_file, "output/result_new.log") print(f"Update completed! Please check the {user_final_file} file!") - if not os.environ.get("GITHUB_ACTIONS"): - print(f"You can access the result at {get_ip_address()}") if self.run_ui: self.update_progress( f"更新完成, 请检查{user_final_file}文件, 可访问以下链接:", @@ -191,16 +207,14 @@ def default_callback(self, *args, **kwargs): self.run_ui = True if callback else False if config.getboolean("Settings", "open_update"): await self.main() - if self.run_ui: - if not config.getboolean("Settings", "open_update"): - print(f"You can access the result at {get_ip_address()}") - self.update_progress( - f"服务启动成功, 可访问以下链接:", - 100, - True, - url=f"{get_ip_address()}", - ) - app.run(host="0.0.0.0", port=8000) + if self.run_ui and config.getboolean("Settings", "open_update") == False: + self.update_progress( + f"服务启动成功, 可访问以下链接:", + 100, + True, + url=f"{get_ip_address()}", + ) + run_app() def stop(self): for task in self.tasks: @@ -218,8 +232,13 @@ def scheduled_task(): loop.run_until_complete(update_source.start()) +def run_app(): + if not os.environ.get("GITHUB_ACTIONS"): + print(f"You can access the result at {get_ip_address()}") + app.run(host="0.0.0.0", port=8000) + + if __name__ == "__main__": if len(sys.argv) == 1 or (len(sys.argv) > 1 and sys.argv[1] == "scheduled_task"): scheduled_task() - if not os.environ.get("GITHUB_ACTIONS"): - app.run(host="0.0.0.0", port=8000) + run_app() diff --git a/docs/images/appreciate.jpg b/static/images/appreciate.jpg similarity index 100% rename from docs/images/appreciate.jpg rename to static/images/appreciate.jpg diff --git a/static/images/favicon.ico b/static/images/favicon.ico new file mode 100644 index 00000000000..b68c23b6202 Binary files /dev/null and b/static/images/favicon.ico differ diff --git a/tkinter_ui/about.py b/tkinter_ui/about.py new file mode 100644 index 00000000000..3e1e0a943e0 --- /dev/null +++ b/tkinter_ui/about.py @@ -0,0 +1,87 @@ +import tkinter as tk +from PIL import Image, ImageTk +import webbrowser +from utils.config import resource_path + + +class AboutUI: + def init_ui(self, root=None, version=None): + about_window = tk.Toplevel(root) + about_window.title("关于") + about_window_width = 430 + about_window_height = 430 + + version_frame = tk.Frame(about_window) + version_frame.pack(side=tk.TOP, fill=tk.X) + + version_label = tk.Label(version_frame, text=f"版本: {version}") + version_label.pack() + + author_row = tk.Frame(about_window) + author_row.pack() + author_row_column1 = tk.Frame(author_row) + author_row_column1.pack(side=tk.LEFT, fill=tk.Y) + author_row_column2 = tk.Frame(author_row) + author_row_column2.pack(side=tk.RIGHT, fill=tk.Y) + author_label = tk.Label(author_row_column1, text="作者:") + author_label.pack() + author_name = tk.Label( + author_row_column2, text="Govin", fg="blue", cursor="hand2" + ) + author_name.pack() + author_name.bind( + "", + lambda e: webbrowser.open_new_tab("https://github.com/Guovin"), + ) + + project_row = tk.Frame(about_window) + project_row.pack() + project_row_column1 = tk.Frame(project_row) + project_row_column1.pack(side=tk.LEFT, fill=tk.Y) + project_row_column2 = tk.Frame(project_row) + project_row_column2.pack(side=tk.RIGHT, fill=tk.Y) + project_label = tk.Label(project_row_column1, text="项目地址:") + project_label.pack() + project_link = tk.Label( + project_row_column2, + text="https://github.com/Guovin/TV", + fg="blue", + cursor="hand2", + ) + project_link.pack() + project_link.bind( + "", + lambda e: webbrowser.open_new_tab("https://github.com/Guovin/TV"), + ) + + disclaimer_label = tk.Label( + version_frame, + text="本软件仅供学习交流用途,数据均来源于互联网,禁止商业行为,一切法律责任与作者无关。", + wraplength=265, + ) + disclaimer_label.pack() + + image = Image.open(resource_path("static/images/appreciate.jpg")) + resized_image = image.resize((250, 250)) + photo = ImageTk.PhotoImage(resized_image) + image_label = tk.Label(about_window, image=photo) + image_label.image = photo + image_label.pack() + + appreciate_label = tk.Label(about_window, text="您的赞助是我更新的动力") + appreciate_label.pack() + + confirm_button = tk.ttk.Button( + about_window, text="确定", command=about_window.destroy + ) + confirm_button.pack(side=tk.RIGHT, padx=5) + + main_width = root.winfo_width() + main_height = root.winfo_height() + main_x = root.winfo_x() + main_y = root.winfo_y() + pos_x = main_x + (main_width // 2) - (about_window_width // 2) + pos_y = main_y + (main_height // 2) - (about_window_height // 2) + about_window.geometry( + f"{about_window_width}x{about_window_height}+{pos_x}+{pos_y}" + ) diff --git a/tkinter_ui/default.py b/tkinter_ui/default.py index 56324f9898d..473283c550f 100644 --- a/tkinter_ui/default.py +++ b/tkinter_ui/default.py @@ -4,6 +4,7 @@ from tkinter import scrolledtext from tkinter import filedialog import os +from utils.channel import get_channel_items class DefaultUI: @@ -67,7 +68,7 @@ def init_ui(self, root): frame_default_source_file_select = tk.Frame(root) frame_default_source_file_select.pack(fill=tk.X) - self.source_file_button = tk.Button( + self.source_file_button = tk.ttk.Button( frame_default_source_file_select, text="选择文件", command=self.select_source_file, @@ -106,7 +107,7 @@ def init_ui(self, root): frame_default_final_file_select = tk.Frame(root) frame_default_final_file_select.pack(fill=tk.X) - self.final_file_button = tk.Button( + self.final_file_button = tk.ttk.Button( frame_default_final_file_select, text="选择文件", command=self.select_final_file, @@ -305,6 +306,11 @@ def select_source_file(self): self.source_file_entry.delete(0, tk.END) self.source_file_entry.insert(0, filepath) config.set("Settings", "source_file", filepath) + get_channel_items(change_source_path=True) + self.source_channels_text.delete(1.0, tk.END) + self.source_channels_text.insert( + tk.END, config.get("Settings", "source_channels") + ) def update_source_channels(self, event): config.set( diff --git a/tkinter_ui/tkinter_ui.py b/tkinter_ui/tkinter_ui.py index 019827e5d61..7abb1190207 100644 --- a/tkinter_ui/tkinter_ui.py +++ b/tkinter_ui/tkinter_ui.py @@ -4,12 +4,12 @@ sys.path.append(os.path.dirname(sys.path[0])) import tkinter as tk from tkinter import messagebox -from tkinter import ttk from utils.config import config, resource_path, save_config from main import UpdateSource import asyncio import threading import webbrowser +from about import AboutUI from default import DefaultUI from multicast import MulticastUI from hotel import HotelUI @@ -25,6 +25,7 @@ def __init__(self, root): self.root = root self.root.title(info.get("name", "")) self.version = info.get("version", "") + self.about_ui = AboutUI() self.default_ui = DefaultUI() self.multicast_ui = MulticastUI() self.hotel_ui = HotelUI() @@ -129,14 +130,23 @@ def update_progress(self, title, progress, finished=False, url=None): def init_UI(self): - notebook = ttk.Notebook(self.root) - notebook.pack(expand=True, fill="both", padx=10, pady=0) + menu_bar = tk.Menu(self.root) + help_menu = tk.Menu(menu_bar, tearoff=0) + help_menu.add_command( + label="关于", + command=lambda: self.about_ui.init_ui(root=self.root, version=self.version), + ) + menu_bar.add_cascade(label="帮助", menu=help_menu) + self.root.config(menu=menu_bar) + + notebook = tk.ttk.Notebook(self.root) + notebook.pack(fill="both", padx=10, pady=5) - frame_default = ttk.Frame(notebook, width=500, height=500) - frame_multicast = ttk.Frame(notebook, width=500, height=500) - frame_hotel = ttk.Frame(notebook, width=500, height=500) - frame_subscribe = ttk.Frame(notebook, width=500, height=500) - frame_online_search = ttk.Frame(notebook, width=500, height=500) + frame_default = tk.ttk.Frame(notebook) + frame_multicast = tk.ttk.Frame(notebook) + frame_hotel = tk.ttk.Frame(notebook) + frame_subscribe = tk.ttk.Frame(notebook) + frame_online_search = tk.ttk.Frame(notebook) notebook.add(frame_default, text="通用设置") notebook.add(frame_multicast, text="组播源") @@ -157,45 +167,20 @@ def init_UI(self): root_operate_column2 = tk.Frame(root_operate) root_operate_column2.pack(side=tk.RIGHT, fill=tk.Y) - self.save_button = tk.Button( + self.save_button = tk.ttk.Button( root_operate_column1, text="保存设置", command=self.save_config ) self.save_button.pack(side=tk.LEFT, padx=4, pady=8) - self.run_button = tk.Button( + self.run_button = tk.ttk.Button( root_operate_column2, text="开始更新", command=self.on_run_update ) self.run_button.pack(side=tk.LEFT, padx=4, pady=8) - version_frame = tk.Frame(self.root) - version_frame.pack(side=tk.BOTTOM, fill=tk.X) - - self.version_label = tk.Label( - version_frame, text=self.version, fg="gray", anchor="se" - ) - self.version_label.pack(side=tk.RIGHT, padx=5, pady=5) - - self.author_label = tk.Label( - version_frame, - text="by Govin", - fg="gray", - anchor="se", - ) - self.author_label.pack(side=tk.LEFT, padx=5, pady=5) - - self.project_link = tk.Label( - version_frame, text="访问项目主页", fg="blue", cursor="hand2" - ) - self.project_link.pack(side=tk.LEFT, padx=5, pady=5) - self.project_link.bind( - "", - lambda e: webbrowser.open_new_tab("https://github.com/Guovin/TV"), - ) - root_progress = tk.Frame(self.root) root_progress.pack(fill=tk.X) - self.progress_bar = ttk.Progressbar( + self.progress_bar = tk.ttk.Progressbar( root_progress, length=300, mode="determinate" ) self.progress_bar.pack_forget() @@ -211,15 +196,22 @@ def init_UI(self): self.view_result_link.pack_forget() -if __name__ == "__main__": - root = tk.Tk() - tkinter_ui = TkinterUI(root) - tkinter_ui.init_UI() +def get_root_location(root): screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() width = 550 height = 750 x = (screen_width / 2) - (width / 2) y = (screen_height / 2) - (height / 2) - root.geometry("%dx%d+%d+%d" % (width, height, x, y)) + return (width, height, x, y) + + +if __name__ == "__main__": + root = tk.Tk() + tkinter_ui = TkinterUI(root) + tkinter_ui.init_UI() + screen_width = root.winfo_screenwidth() + screen_height = root.winfo_screenheight() + root.geometry("%dx%d+%d+%d" % get_root_location(root)) + root.iconbitmap(resource_path("static/images/favicon.ico")) root.mainloop() diff --git a/tkinter_ui/tkinter_ui.spec b/tkinter_ui/tkinter_ui.spec index f0ca2a0b4f4..6d5d6bd1f5d 100644 --- a/tkinter_ui/tkinter_ui.spec +++ b/tkinter_ui/tkinter_ui.spec @@ -1,7 +1,7 @@ # -*- mode: python ; coding: utf-8 -*- a = Analysis( - ['tkinter_ui.py', 'default.py', 'multicast.py', 'hotel.py', 'subscribe.py', 'online_search.py'], + ['tkinter_ui.py', 'about.py', 'default.py', 'multicast.py', 'hotel.py', 'subscribe.py', 'online_search.py'], pathex=[], binaries=[], datas=[ @@ -9,6 +9,9 @@ a = Analysis( ('../config/demo.txt', 'config'), ('../updates/multicast/multicast_map.json', 'updates/multicast'), ('../updates/multicast/multicast_region_result.json', 'updates/multicast'), + ('../static/images/favicon.ico', 'static/images'), + ('../static/images/appreciate.jpg', 'static/images'), + ('about.py', '.'), ('default.py', '.'), ('multicast.py', '.'), ('hotel.py', '.'), @@ -46,4 +49,5 @@ exe = EXE( target_arch=None, codesign_identity=None, entitlements_file=None, + icon='../static/images/favicon.ico' ) diff --git a/utils/channel.py b/utils/channel.py index 966ac007758..6182c47ac8a 100644 --- a/utils/channel.py +++ b/utils/channel.py @@ -24,7 +24,9 @@ ) -def get_channel_data_from_file(channels=None, file=None, names=None, from_result=False): +def get_channel_data_from_file( + channels=None, file=None, names=None, from_result=False, change_source_path=False +): """ Get the channel data from the file """ @@ -43,7 +45,7 @@ def get_channel_data_from_file(channels=None, file=None, names=None, from_result match = re.search(pattern, line) if match is not None: name = match.group(1).strip() - if name not in names: + if not change_source_path and name not in names: continue url = match.group(2).strip() if url and url not in channels[current_category][name]: @@ -51,7 +53,7 @@ def get_channel_data_from_file(channels=None, file=None, names=None, from_result return channels -def get_channel_items(): +def get_channel_items(change_source_path=False): """ Get the channel items from the source file """ @@ -63,7 +65,10 @@ def get_channel_items(): if os.path.exists(resource_path(user_source_file)): with open(resource_path(user_source_file), "r", encoding="utf-8") as file: channels = get_channel_data_from_file( - channels=channels, file=file, names=source_channel_names + channels=channels, + file=file, + names=source_channel_names, + change_source_path=change_source_path, ) if config.getboolean("Settings", "open_use_old_result") and os.path.exists( @@ -75,14 +80,16 @@ def get_channel_items(): file=file, names=source_channel_names, from_result=True, + change_source_path=change_source_path, ) channel_names = [ name for channel_obj in channels.values() for name in channel_obj.keys() ] - for source_name in source_channel_names: - if source_name not in channel_names: - channels["自定义频道"][source_name] = [] + if not change_source_path: + for source_name in source_channel_names: + if source_name not in channel_names: + channels["自定义频道"][source_name] = [] total_channel_names = ",".join( [name for channel_obj in channels.values() for name in channel_obj.keys()] ) @@ -514,6 +521,10 @@ def append_all_method_data( ("online_search", online_search_result), ]: if config.getboolean("Settings", f"open_{method}"): + if ( + method == "hotel_tonkiang" or method == "hotel_fofa" + ) and config.getboolean("Settings", f"open_hotel") == False: + continue data = append_data_to_info_data( data, cate, @@ -556,17 +567,21 @@ def append_all_method_data_keep_all( Append all method data to total info data, keep all channel name and urls """ for cate, channel_obj in items: - for result_name, result in [ + for method, result in [ ("subscribe", subscribe_result), ("multicast", multicast_result), ("hotel_tonkiang", hotel_tonkiang_result), ("hotel_fofa", hotel_fofa_result), ("online_search", online_search_result), ]: - if result and config.getboolean("Settings", f"open_{result_name}"): + if result and config.getboolean("Settings", f"open_{method}"): + if ( + method == "hotel_tonkiang" or method == "hotel_fofa" + ) and config.getboolean("Settings", f"open_hotel") == False: + continue for name, urls in result.items(): data = append_data_to_info_data(data, cate, name, urls) - print(name, f"{result_name.capitalize()} num:", len(urls)) + print(name, f"{method.capitalize()} num:", len(urls)) if config.getboolean("Settings", "open_use_old_result"): old_urls = channel_obj.get(name, []) data = append_data_to_info_data( diff --git a/utils/config.py b/utils/config.py index 8f9f4bd7d8f..554d31b9466 100644 --- a/utils/config.py +++ b/utils/config.py @@ -1,6 +1,7 @@ import os import sys import configparser +import shutil def resource_path(relative_path, persistent=False): @@ -50,6 +51,26 @@ def save_config(): else "config.ini" ) user_config_path = resource_path(user_config_file, persistent=True) - os.makedirs(os.path.dirname(user_config_path), exist_ok=True) + if not os.path.exists(user_config_path): + os.makedirs(os.path.dirname(user_config_path), exist_ok=True) with open(user_config_path, "w", encoding="utf-8") as configfile: config.write(configfile) + + +def copy_config(): + user_source_file = resource_path(config.get("Settings", "source_file")) + user_config_path = resource_path("config/user_config.ini") + default_config_path = resource_path("config/config.ini") + user_config_file = ( + user_config_path if os.path.exists(user_config_path) else default_config_path + ) + dest_folder = os.path.join(os.getcwd(), "config") + files_to_copy = [user_source_file, user_config_file] + try: + for src_file in files_to_copy: + dest_path = os.path.join(dest_folder, os.path.basename(src_file)) + if os.path.abspath(src_file) == os.path.abspath(dest_path): + continue + shutil.copy(src_file, dest_folder) + except Exception as e: + print(f"Failed to copy files: {str(e)}") diff --git a/version.json b/version.json index 753bcd9ecb0..ef1109dd7a6 100644 --- a/version.json +++ b/version.json @@ -1,4 +1,4 @@ { - "version": "1.3.5", + "version": "1.3.6", "name": "直播源接口更新工具" } \ No newline at end of file