305 lines
13 KiB
Python
305 lines
13 KiB
Python
import os
|
||
import threading
|
||
import tkinter
|
||
import tkinter.font
|
||
import tkinter.ttk
|
||
from datetime import datetime, timedelta
|
||
from pathlib import Path
|
||
from typing import Optional
|
||
from time import sleep
|
||
import tkcalendar
|
||
from pywxdump import read_info
|
||
from decrypter.db_decrypt import DatabaseDecrypter
|
||
from decrypter.video_decrypt import VideoDecrypter
|
||
from gui.auto_scroll_guide import AutoScrollGuide
|
||
from gui.auto_scrolls_single_guide import AutoScrollSingleGuide
|
||
from gui.tool_tip import ToolTip
|
||
from entity.contact import Contact
|
||
from exporter.html_exporter import HtmlExporter
|
||
from gui.listbox_with_search import ListboxWithSearch
|
||
|
||
|
||
class Gui:
|
||
def __init__(self):
|
||
|
||
self.restart_note1 = None
|
||
self.restart_note2 = None
|
||
self.auto_scroll_single_guide = None
|
||
self.auto_scroll_guide = None
|
||
self.auto_scroll_frame = None
|
||
self.search_username = None
|
||
self.open_search_guide_image = None
|
||
self.open_search_guide = None
|
||
self.auto_scroll_button_single = None
|
||
self.auto_scroll_button_single_text = None
|
||
self.convert_video = None
|
||
self.convert_video_var = None
|
||
self.html_exporter_thread = None
|
||
self.confirm_button_text = None
|
||
self.succeed_label_2 = None
|
||
self.succeed_label = None
|
||
self.download_pic_var = Optional[tkinter.IntVar]
|
||
self.download_pic = None
|
||
self.auto_scroll_button_text = None
|
||
self.warning_label = None
|
||
self.root = None
|
||
self.waiting_label = None
|
||
self.listbox = None
|
||
self.begin_calendar = None
|
||
self.end_calendar = None
|
||
self.end_calendar_label = None
|
||
self.begin_calendar_label = None
|
||
self.confirm_button = None
|
||
self.decrypt_progressbar = None
|
||
self.export_progressbar = None
|
||
self.next_step_button = None
|
||
self.decrypter = None
|
||
self.auto_scroll_button = None
|
||
self.auto_scrolling_thread = None
|
||
self.decrypt_note = None
|
||
self.decrypt_note_text = None
|
||
self.account_info = None
|
||
self.video_decrypter = None
|
||
self.export_dir_name = None
|
||
self.exporting = False
|
||
# 1: 自动滚动数据 2: 解密数据库 3: 导出
|
||
self.page_stage = 1
|
||
|
||
def run_gui(self):
|
||
self.root = tkinter.Tk()
|
||
self.root.geometry('650x650')
|
||
self.root.title('朋友圈导出')
|
||
|
||
self.waiting_label = tkinter.ttk.Label(self.root, text="正在连接微信....",
|
||
font=("微软雅黑", 16), anchor='center')
|
||
self.waiting_label.place(relx=0.5, rely=0.05, anchor='center')
|
||
|
||
self.root.mainloop()
|
||
|
||
def wechat_logged_in(self, account_info):
|
||
|
||
self.account_info = account_info
|
||
self.waiting_label.config(text="微信已登录")
|
||
|
||
self.auto_scroll_button_text = tkinter.StringVar()
|
||
self.auto_scroll_button_text.set("读取全部朋友")
|
||
self.auto_scroll_button = tkinter.ttk.Button(self.root, textvariable=self.auto_scroll_button_text,
|
||
command=self.open_auto_scroll_guide)
|
||
self.auto_scroll_button.place(relx=0.35, rely=0.15, anchor='center')
|
||
|
||
self.auto_scroll_button_single_text = tkinter.StringVar()
|
||
self.auto_scroll_button_single_text.set("读取单个朋友")
|
||
self.auto_scroll_button_single = tkinter.ttk.Button(self.root, textvariable=self.auto_scroll_button_single_text,
|
||
command=self.switch_auto_scroll_single)
|
||
self.auto_scroll_button_single.place(relx=0.655, rely=0.15, anchor='center')
|
||
|
||
self.next_step_button = tkinter.ttk.Button(self.root, text="下一步", command=self.next_step)
|
||
self.next_step_button.place(relx=0.65, rely=0.8)
|
||
|
||
|
||
|
||
def open_auto_scroll_guide(self):
|
||
|
||
if self.auto_scroll_single_guide and self.auto_scroll_single_guide.frame:
|
||
self.auto_scroll_single_guide.frame.place_forget()
|
||
|
||
self.auto_scroll_guide = AutoScrollGuide(self.root)
|
||
self.auto_scroll_guide.frame.place(relx=0.5, rely=0.5, anchor='center')
|
||
|
||
def switch_auto_scroll_single(self):
|
||
|
||
if self.auto_scroll_guide and self.auto_scroll_guide.frame:
|
||
self.auto_scroll_guide.frame.place_forget()
|
||
|
||
self.auto_scroll_single_guide = AutoScrollSingleGuide(self.root)
|
||
self.auto_scroll_single_guide.frame.place(relx=0.5, rely=0.5, anchor='center')
|
||
|
||
|
||
def next_step(self):
|
||
|
||
if self.page_stage == 1:
|
||
|
||
if self.auto_scroll_guide and self.auto_scroll_guide.auto_thread:
|
||
self.auto_scroll_guide.auto_thread.set_scrolling(False)
|
||
|
||
if self.auto_scroll_single_guide and self.auto_scroll_single_guide.auto_thread:
|
||
self.auto_scroll_guide.auto_thread.set_scrolling(False)
|
||
|
||
self.auto_scroll_button.place_forget()
|
||
self.auto_scroll_button_single.place_forget()
|
||
|
||
if self.auto_scroll_guide and self.auto_scroll_guide.frame:
|
||
self.auto_scroll_guide.frame.place_forget()
|
||
|
||
if self.auto_scroll_single_guide and self.auto_scroll_single_guide.frame:
|
||
self.auto_scroll_single_guide.frame.place_forget()
|
||
|
||
self.restart_note1 = tkinter.Label(self.root, text="请关闭微信客户端", fg="red")
|
||
self.restart_note1.place(relx=0.5, rely=0.2, anchor='center')
|
||
self.restart_note2 = tkinter.Label(self.root, text="然后点击下一步")
|
||
self.restart_note2.place(relx=0.5, rely=0.3, anchor='center')
|
||
|
||
if self.page_stage == 2:
|
||
|
||
self.restart_note1.place_forget()
|
||
self.restart_note2.place_forget()
|
||
self.waiting_label.place_forget()
|
||
|
||
self.decrypter = DatabaseDecrypter(self, self.account_info.get("filePath"), self.account_info.get("key"))
|
||
|
||
self.decrypt_note_text = tkinter.StringVar()
|
||
self.decrypt_note_text.set("正在复制数据.....")
|
||
|
||
self.decrypt_note = tkinter.Label(self.root, textvariable=self.decrypt_note_text)
|
||
self.decrypt_note.place(relx=0.5, rely=0.2, anchor='center')
|
||
self.decrypt_progressbar = tkinter.ttk.Progressbar(self.root)
|
||
self.decrypt_progressbar.place(relx=0.5, rely=0.3, anchor='center')
|
||
|
||
# 进度值最大值
|
||
self.decrypt_progressbar['maximum'] = 100
|
||
# 进度值初始值
|
||
self.decrypt_progressbar['value'] = 0
|
||
# 解密过程禁用下一步按钮
|
||
self.next_step_button.config(state=tkinter.DISABLED)
|
||
self.decrypter.decrypt()
|
||
if self.page_stage == 3:
|
||
self.decrypt_note.place_forget()
|
||
self.decrypt_progressbar.place_forget()
|
||
self.init_export_page()
|
||
# 不再有下一步按钮
|
||
self.next_step_button.place_forget()
|
||
# 初始化视频导出器
|
||
self.video_decrypter = VideoDecrypter(self, self.account_info.get("filePath"))
|
||
|
||
self.page_stage = self.page_stage + 1
|
||
|
||
def init_export_page(self):
|
||
|
||
from app.DataBase import micro_msg_db
|
||
contact_datas = micro_msg_db.get_contact()
|
||
|
||
contacts = []
|
||
for c in contact_datas:
|
||
contact = Contact(c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7], c[8], c[9], c[10], c[11])
|
||
contacts.append(contact)
|
||
|
||
def validate_contact(this_contact: Contact):
|
||
c_type = this_contact.type
|
||
user_name: str = this_contact.userName
|
||
|
||
# 不是其他号码
|
||
is_misc_account = c_type == 1 or c_type == 33 or c_type == 513
|
||
# 不是公众号
|
||
is_gh_account = user_name.startswith("gh_")
|
||
# 不是聊天群
|
||
is_chatroom = user_name.endswith("@chatroom")
|
||
# 不是文件传输助手
|
||
is_filehelper = this_contact.userName == "filehelper"
|
||
return (not is_misc_account) and (not is_gh_account) and (not is_chatroom) and (not is_filehelper)
|
||
|
||
filtered = filter(validate_contact, contacts)
|
||
contacts = list(filtered)
|
||
|
||
self.listbox = ListboxWithSearch(self.root, contacts)
|
||
self.listbox.frame.place(relx=0.05, rely=0.03)
|
||
|
||
self.begin_calendar_label = tkinter.ttk.Label(text="开始日期")
|
||
self.begin_calendar_label.place(relx=0.65, rely=0.15)
|
||
|
||
# 默认开始时间是100天前
|
||
current_date = datetime.now()
|
||
half_year_ago = current_date - timedelta(days=100)
|
||
self.begin_calendar = tkcalendar.DateEntry(master=self.root, locale="zh_CN", year=half_year_ago.year,
|
||
month=half_year_ago.month, day=half_year_ago.day,
|
||
maxdate=datetime.now())
|
||
self.begin_calendar.place(relx=0.65, rely=0.2)
|
||
|
||
self.end_calendar_label = tkinter.ttk.Label(text="截止日期")
|
||
self.end_calendar_label.place(relx=0.65, rely=0.25)
|
||
self.end_calendar = tkcalendar.DateEntry(master=self.root, locale="zh_CN", maxdate=datetime.now())
|
||
self.end_calendar.place(relx=0.65, rely=0.3)
|
||
|
||
self.download_pic_var = tkinter.IntVar(value=0)
|
||
self.download_pic = tkinter.ttk.Checkbutton(self.root, text='下载图片', variable=self.download_pic_var)
|
||
self.download_pic.place(relx=0.65, rely=0.4)
|
||
ToolTip(self.download_pic, "将图片下载到电脑上,网页\n可离线查看,导出速度变慢")
|
||
|
||
self.convert_video_var = tkinter.IntVar(value=0)
|
||
self.convert_video = tkinter.ttk.Checkbutton(self.root, text='视频转码', variable=self.convert_video_var)
|
||
self.convert_video.place(relx=0.65, rely=0.45)
|
||
ToolTip(self.convert_video,
|
||
"视频原始格式为H265,只支持\nChrome浏览器播放,勾选后\n将视频转码为H264,支持大\n部分浏览器,但导出速度变慢")
|
||
|
||
self.confirm_button_text = tkinter.StringVar()
|
||
self.confirm_button_text.set("开始导出")
|
||
|
||
self.confirm_button = tkinter.ttk.Button(self.root, textvariable=self.confirm_button_text,
|
||
command=self.confirm_export)
|
||
self.confirm_button.place(relx=0.65, rely=0.6)
|
||
|
||
# 导出成功的提示
|
||
self.succeed_label = tkinter.Label(self.root, text="导出结束")
|
||
self.succeed_label_2 = tkinter.Label(self.root, text="打开文件夹", fg="#0000FF", cursor="hand2")
|
||
self.succeed_label_2.bind("<Button-1>", self.open_target_folder)
|
||
|
||
# 进度条
|
||
self.export_progressbar = tkinter.ttk.Progressbar(self.root, length=150)
|
||
|
||
def confirm_export(self):
|
||
|
||
if self.html_exporter_thread and not self.html_exporter_thread.stop_flag:
|
||
self.html_exporter_thread.stop()
|
||
else:
|
||
if not self.warning_label:
|
||
self.warning_label = tkinter.Label(self.root, fg="red")
|
||
self.warning_label.place(relx=0.65, rely=0.55)
|
||
|
||
self.warning_label.config(text="")
|
||
contacts = self.listbox.get_contacts()
|
||
if not contacts:
|
||
self.warning_label.config(text=f"请选择至少一个联系人")
|
||
return
|
||
|
||
if self.begin_calendar.get_date() > self.end_calendar.get_date():
|
||
self.warning_label.config(text=f"开始时间必须小于截止时间")
|
||
return
|
||
|
||
self.export_progressbar.place(relx=0.64, rely=0.68)
|
||
# 进度值最大值
|
||
self.export_progressbar['maximum'] = 100
|
||
# 进度值初始值
|
||
self.export_progressbar['value'] = 0
|
||
|
||
current_time = datetime.now()
|
||
self.export_dir_name = current_time.strftime("%Y_%m_%d_%H%M%S")
|
||
contact_map = {contact.userName: contact for contact in contacts}
|
||
|
||
self.confirm_button_text.set("停止导出")
|
||
self.succeed_label.place_forget()
|
||
self.succeed_label_2.place_forget()
|
||
|
||
# 导出线程
|
||
self.html_exporter_thread = HtmlExporter(self, self.export_dir_name, contact_map,
|
||
self.begin_calendar.get_date(), self.end_calendar.get_date(),
|
||
self.download_pic_var.get(), self.convert_video_var.get())
|
||
self.html_exporter_thread.start()
|
||
|
||
def update_decrypt_progressbar(self, progress):
|
||
self.decrypt_progressbar['value'] = progress
|
||
self.root.update()
|
||
|
||
def update_export_progressbar(self, progress):
|
||
self.export_progressbar['value'] = progress
|
||
self.root.update()
|
||
|
||
def export_succeed(self):
|
||
self.confirm_button_text.set("开始导出")
|
||
self.succeed_label.place(relx=0.64, rely=0.75)
|
||
self.succeed_label_2.place(relx=0.76, rely=0.75)
|
||
|
||
def open_target_folder(self, event):
|
||
folder_path = Path(f"output/{self.export_dir_name}/")
|
||
# 转换为绝对路径
|
||
absolute_path = folder_path.resolve()
|
||
os.startfile(absolute_path)
|