diff --git a/images/main.ui b/images/main.ui
index cfea79d5b..bc8ad450d 100644
--- a/images/main.ui
+++ b/images/main.ui
@@ -6,8 +6,8 @@
0
0
- 499
- 466
+ 600
+ 550
@@ -34,7 +34,7 @@
- 40
+ 20
20
@@ -47,7 +47,7 @@
- 40
+ 20
20
@@ -61,7 +61,7 @@
20
- 40
+ 20
@@ -74,7 +74,7 @@
20
- 40
+ 20
@@ -127,56 +127,16 @@
-
-
-
- Qt::Vertical
-
-
-
- 20
- 40
-
-
-
-
- -
-
+
-
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
-
- 200
- 200
-
-
-
- .QLabel {
- image: url(:/icon/wechat.png);
-}
-
+
-
-
-
- Qt::AlignCenter
+ 全选
-
-
+
Qt::Horizontal
@@ -191,14 +151,109 @@
-
-
+
+
+
+ 0
+ 200
+
+
+
+ .QTableWidget {
+ background-color: #fff;
+ border: 1px solid #d9d9d9;
+ border-radius: 3px;
+}
+QTableWidget::item {
+ padding: 5px;
+}
+QTableWidget::item:selected {
+ background-color: #1890ff;
+}
+QHeaderView::section {
+ background-color: #f0f0f0;
+ padding: 5px;
+ border: 1px solid #d9d9d9;
+ font-weight: bold;
+}
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+ QAbstractItemView::SingleSelection
+
+
+ QAbstractItemView::SelectRows
+
+
+ true
+
+
+ Qt::SolidLine
+
+
+ 3
+
+
+ 150
+
+
+ true
+
+
+ 50
+
+
+ false
+
+
+ true
+
+
+ false
+
+
+ 30
+
+
+ true
+
+
+ 30
+
+
+ false
+
+
+ false
+
+
+
+ 选择
+
+
+
+
+ 文件路径
+
+
+
+
+ 大小
+
+
+
+
+ -
+
Qt::Vertical
20
- 40
+ 10
@@ -215,7 +270,7 @@
- 40
+ 20
20
@@ -277,7 +332,7 @@ QProgressBar::chunk {
- 40
+ 20
20
@@ -286,10 +341,7 @@ QProgressBar::chunk {
-
-
-
- 0
-
+
-
@@ -300,12 +352,80 @@ QProgressBar::chunk {
- 100
+ 20
20
+ -
+
+
+
+ 0
+ 35
+
+
+
+ .QLabel {
+ color: rgba(0,0,0,.65);
+ background-color: #fff;
+ border: 1px solid #d9d9d9;
+ border-right: 0px;
+ height: 24px;
+ font-size: 14px;
+ padding: 0px 10px;
+}
+.QLabel:hover {
+ color: #40a9ff;
+ background-color: #fff;
+ border: 1px solid #40a9ff;
+ height: 24px;
+ font-size: 14px
+}
+
+
+ 预览
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 0
+ 35
+
+
+
+ .QLabel {
+ color: rgba(0,0,0,.65);
+ background-color: #fff;
+ border: 1px solid #d9d9d9;
+ border-right: 0px;
+ height: 24px;
+ font-size: 14px;
+ padding: 0px 10px;
+}
+.QLabel:hover {
+ color: #40a9ff;
+ background-color: #fff;
+ border: 1px solid #40a9ff;
+ height: 24px;
+ font-size: 14px
+}
+
+
+ 执行删除
+
+
+ Qt::AlignCenter
+
+
+
-
@@ -327,7 +447,7 @@ QProgressBar::chunk {
}
- <html><head/><body><p>开始</p></body></html>
+ 一键清理
Qt::AlignCenter
@@ -361,7 +481,7 @@ QProgressBar::chunk {
}
- <html><head/><body><p>设置</p></body></html>
+ 设置
Qt::AlignCenter
@@ -395,7 +515,7 @@ QProgressBar::chunk {
}
- <html><head/><body><p>退出</p></body></html>
+ 退出
Qt::AlignCenter
@@ -412,7 +532,7 @@ QProgressBar::chunk {
- 100
+ 20
20
@@ -428,7 +548,7 @@ QProgressBar::chunk {
20
- 40
+ 10
diff --git a/main.py b/main.py
index 7b4bf31a8..95ba46d8b 100644
--- a/main.py
+++ b/main.py
@@ -1,7 +1,7 @@
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QGraphicsDropShadowEffect, QListWidgetItem, QListView, QWidget, \
- QLabel, QHBoxLayout, QFileDialog
+ QLabel, QHBoxLayout, QFileDialog, QTableWidgetItem, QHeaderView
from PyQt5.QtCore import Qt, QPropertyAnimation, QEasingCurve, QThread, pyqtSignal, QMutex, QSize, QEvent, QPoint, QTimer
from PyQt5.QtGui import QMouseEvent, QCursor, QColor
from PyQt5.uic import loadUi
@@ -14,26 +14,22 @@
from utils.deleteThread import *
from utils.multiDeleteThread import multiDeleteThread
from utils.selectVersion import *
-from utils.selectVersion import check_dir, existing_user_config
-# 设置应用程序在高DPI屏幕上启用高DPI缩放。Set the application to enable high DPI scaling on high DPI screens
-# 注意事项:此行代码必须在QApplication实例化之前调用,否则会调用失败。Notes: This line of code must be called before the instantiation of the QApplication object; otherwise, it will fail
+from utils.selectVersion import check_dir, existing_user_config, find_all_wechat_paths
+from utils.scanThread import ScanThread
+
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
-# determine if application is a script file or frozen exe
if getattr(sys, 'frozen', False):
working_dir = os.path.dirname(os.path.realpath(sys.executable))
elif __file__:
working_dir = os.path.split(os.path.realpath(__file__))[0]
-# 主窗口
class Window(QMainWindow):
def mousePressEvent(self, event):
- # 重写一堆方法使其支持拖动
if event.button() == Qt.LeftButton:
self.m_drag = True
self.m_DragPosition = event.globalPos() - self.pos()
event.accept()
- # self.setCursor(QCursor(Qt.OpenHandCursor))
def mouseMoveEvent(self, QMouseEvent):
try:
@@ -45,29 +41,22 @@ def mouseMoveEvent(self, QMouseEvent):
def mouseReleaseEvent(self, QMouseEvent):
self.m_drag = False
- # self.setCursor(QCursor(Qt.ArrowCursor))
def _frame(self):
- # 边框
self.setWindowFlags(Qt.FramelessWindowHint)
self.setAttribute(Qt.WA_TranslucentBackground, True)
- # 阴影
effect = QGraphicsDropShadowEffect(blurRadius=12, xOffset=0, yOffset=0)
effect.setColor(QColor(25, 25, 25, 170))
self.mainFrame.setGraphicsEffect(effect)
def doFadeIn(self):
- # 动画
self.animation = QPropertyAnimation(self, b'windowOpacity')
- # 持续时间250ms
self.animation.setDuration(250)
try:
- # 尝试先取消动画完成后关闭窗口的信号
self.animation.finished.disconnect(self.close)
except:
pass
self.animation.stop()
- # 透明度范围从0逐渐增加到1
self.animation.setEasingCurve(QEasingCurve.InOutCubic)
self.animation.setStartValue(0)
self.animation.setEndValue(1)
@@ -75,9 +64,7 @@ def doFadeIn(self):
def doFadeOut(self):
self.animation.stop()
- # 动画完成则关闭窗口
self.animation.finished.connect(self.close)
- # 透明度范围从1逐渐减少到0s
self.animation.setEasingCurve(QEasingCurve.InOutCubic)
self.animation.setStartValue(1)
self.animation.setEndValue(0)
@@ -94,7 +81,7 @@ def setWarninginfo(self, text):
background: #fff2f0;
}
""")
- self.lab_info.setWordWrap(True) # 启用自动换行
+ self.lab_info.setWordWrap(True)
self.lab_info.setText(text)
def setSuccessinfo(self, text):
@@ -108,7 +95,7 @@ def setSuccessinfo(self, text):
background: #f6ffed;
}
""")
- self.lab_info.setWordWrap(True) # 启用自动换行
+ self.lab_info.setWordWrap(True)
self.lab_info.setText(text)
@@ -133,7 +120,6 @@ def open_file(self):
elem for elem in list_
if elem != 'All Users' and elem != 'Applet' and elem != 'WMPF'
]
- # 如果已有用户配置,那么写入新的用户配置,否则默认写入新配置
dir_list = []
user_config = []
existing_user_config_dic = existing_user_config()
@@ -282,6 +268,9 @@ def deal_emit_slot(self, set_status):
self.config_exists = True
def closeEvent(self, event):
+ if hasattr(self, 'scan_thread') and self.scan_thread.isRunning():
+ self.scan_thread.stop()
+ self.scan_thread.wait()
sys.exit(0)
def eventFilter(self, object, event):
@@ -291,7 +280,7 @@ def eventFilter(self, object, event):
return True
elif object == self.lab_clean:
try:
- self.setSuccessinfo("正在清理中...")
+ self.setSuccessinfo("正在一键清理中...")
self.justdoit()
except:
self.setWarninginfo("清理失败,请检查配置文件后重试")
@@ -300,18 +289,158 @@ def eventFilter(self, object, event):
cw = ConfigWindow()
cw.Signal_OneParameter.connect(self.deal_emit_slot)
return True
+ elif object == self.lab_preview:
+ try:
+ self.start_preview()
+ except Exception as e:
+ self.setWarninginfo(f"预览失败:{str(e)}")
+ return True
+ elif object == self.lab_execute_delete:
+ try:
+ self.execute_delete()
+ except Exception as e:
+ self.setWarninginfo(f"删除失败:{str(e)}")
+ return True
return False
def _eventfilter(self):
- # 事件过滤
self.lab_close.installEventFilter(self)
self.lab_clean.installEventFilter(self)
self.lab_config.installEventFilter(self)
+ self.lab_preview.installEventFilter(self)
+ self.lab_execute_delete.installEventFilter(self)
+
+ def init_table(self):
+ self.table_files.setColumnCount(3)
+ self.table_files.setHorizontalHeaderLabels(["选择", "文件路径", "大小"])
+ self.table_files.horizontalHeader().setSectionResizeMode(0, QHeaderView.Fixed)
+ self.table_files.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
+ self.table_files.horizontalHeader().setSectionResizeMode(2, QHeaderView.Fixed)
+ self.table_files.setColumnWidth(0, 50)
+ self.table_files.setColumnWidth(2, 100)
+ self.table_files.setRowCount(0)
+ self.file_data = []
+
+ def clear_table(self):
+ self.table_files.setRowCount(0)
+ self.file_data = []
+ self.check_select_all.setChecked(False)
+
+ def add_file_to_table(self, file_path, file_size, file_type):
+ row = self.table_files.rowCount()
+ self.table_files.insertRow(row)
+
+ checkbox_item = QTableWidgetItem()
+ checkbox_item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled)
+ checkbox_item.setCheckState(Qt.Checked)
+ self.table_files.setItem(row, 0, checkbox_item)
+
+ path_item = QTableWidgetItem(file_path)
+ path_item.setToolTip(file_path)
+ self.table_files.setItem(row, 1, path_item)
+
+ size_item = QTableWidgetItem(file_size)
+ self.table_files.setItem(row, 2, size_item)
+
+ self.file_data.append({"path": file_path, "type": file_type})
+
+ def start_preview(self):
+ if not os.path.exists(working_dir + "/config.json"):
+ self.setWarninginfo("请先配置微信数据目录")
+ return
+
+ if hasattr(self, 'scan_thread') and self.scan_thread.isRunning():
+ self.setWarninginfo("正在扫描中,请稍候...")
+ return
+
+ self.clear_table()
+ self.bar_progress.setValue(0)
+ self.setSuccessinfo("正在扫描文件,请稍候...")
+
+ fd = open(working_dir + "/config.json", encoding="utf-8")
+ config = json.load(fd)
+
+ self.scan_thread = ScanThread(config)
+ self.scan_thread.scan_progress_signal.connect(self.on_scan_progress)
+ self.scan_thread.scan_file_found_signal.connect(self.on_scan_file_found)
+ self.scan_thread.scan_finished_signal.connect(self.on_scan_finished)
+ self.scan_thread.scan_error_signal.connect(self.on_scan_error)
+ self.scan_thread.start()
+
+ def on_scan_progress(self, progress):
+ self.bar_progress.setValue(progress)
+
+ def on_scan_file_found(self, file_path, file_size, file_type):
+ self.add_file_to_table(file_path, file_size, file_type)
+
+ def on_scan_finished(self, file_count, dir_count):
+ total = file_count + dir_count
+ if total == 0:
+ self.setSuccessinfo("扫描完成,没有找到需要清理的文件")
+ else:
+ self.setSuccessinfo(f"扫描完成,共找到 {total} 个文件/文件夹")
+ self.bar_progress.setValue(100)
+
+ def on_scan_error(self, error_msg):
+ self.setWarninginfo(f"扫描出错:{error_msg}")
+
+ def toggle_select_all(self, state):
+ if state == Qt.Checked:
+ for row in range(self.table_files.rowCount()):
+ item = self.table_files.item(row, 0)
+ if item:
+ item.setCheckState(Qt.Checked)
+ else:
+ for row in range(self.table_files.rowCount()):
+ item = self.table_files.item(row, 0)
+ if item:
+ item.setCheckState(Qt.Unchecked)
+
+ def execute_delete(self):
+ selected_files = []
+ selected_dirs = []
+
+ for row in range(self.table_files.rowCount()):
+ item = self.table_files.item(row, 0)
+ if item and item.checkState() == Qt.Checked:
+ file_info = self.file_data[row]
+ if file_info["type"] == "file":
+ selected_files.append(file_info["path"])
+ else:
+ selected_dirs.append(file_info["path"])
+
+ if len(selected_files) + len(selected_dirs) == 0:
+ self.setWarninginfo("请先选择要删除的文件")
+ return
+
+ self.setSuccessinfo("正在删除选中的文件...")
+
+ self.total_file = len(selected_files)
+ self.total_dir = len(selected_dirs)
+
+ share_thread_arr = [0]
+
+ if len(selected_files) + len(selected_dirs) > 0:
+ thread = multiDeleteThread(selected_files, selected_dirs, share_thread_arr)
+ thread.delete_process_signal.connect(self.callback)
+ thread.start()
+
+ self.remove_deleted_rows(selected_files, selected_dirs)
+
+ def remove_deleted_rows(self, deleted_files, deleted_dirs):
+ rows_to_remove = []
+ for row in range(self.table_files.rowCount()):
+ file_info = self.file_data[row]
+ if file_info["path"] in deleted_files or file_info["path"] in deleted_dirs:
+ rows_to_remove.append(row)
+
+ for row in sorted(rows_to_remove, reverse=True):
+ self.table_files.removeRow(row)
+ del self.file_data[row]
def get_fileNum(self, path, day, picCacheCheck, fileCheck, picCheck,
videoCheck, file_list, dir_list):
dir_name = PureWindowsPath(path)
- # Convert path to the right format for the current operating system
correct_path = Path(dir_name)
now = datetime.datetime.now()
if picCacheCheck:
@@ -353,12 +482,10 @@ def pathFileDeal(self, now, day, path, file_list, dir_list):
def getPathFileNum(self, now, day, path_one, path_two, file_list,
dir_list):
- # caculate path_one
self.pathFileDeal(now, day, path_one, file_list, dir_list)
td = datetime.datetime.now() - datetime.timedelta(days=day)
td_year = td.year
td_month = td.month
- # caculate path_two
if os.path.exists(path_two):
osdir = os.listdir(path_two)
dirlist = []
@@ -432,33 +559,109 @@ def justdoit(self):
self.total_file = total_file
self.total_dir = total_dir
for thread in thread_list:
- thread.run()
+ thread.start()
def show_config_window(self):
self.config_window = ConfigWindow()
self.setSuccessinfo("已经准备好,可以开始了!")
+ def smart_detect_wechat_path(self):
+ found_paths = find_all_wechat_paths()
+
+ if len(found_paths) > 0:
+ config = self.create_config_from_paths(found_paths)
+ self.save_config(config)
+ self.setSuccessinfo(f"自动检测到微信数据目录:{found_paths[0]}")
+ self.config_exists = True
+ return True
+ else:
+ return False
+
+ def create_config_from_paths(self, paths):
+ config = {"data_dir": [], "users": []}
+
+ for path in paths:
+ dir_list, names = get_dir_name(path)
+ for i, user_dir in enumerate(dir_list):
+ config["data_dir"].append(path)
+ config["users"].append({
+ "wechat_id": names[i],
+ "clean_days": 365,
+ "is_clean": True,
+ "clean_pic_cache": True,
+ "clean_file": False,
+ "clean_pic": True,
+ "clean_video": True,
+ "is_timer": True,
+ "timer": "0h"
+ })
+
+ return config
+
+ def save_config(self, config):
+ if len(config["data_dir"]) > 0:
+ with open(working_dir + "/config.json", "w", encoding="utf-8") as f:
+ json.dump(config, f)
+ return True
+ return False
+
+ def prompt_select_wechat_path(self):
+ self.setWarninginfo("未检测到微信数据目录,请手动选择")
+
+ folder_path = QFileDialog.getExistingDirectory(
+ self,
+ "请选择微信数据目录(通常是 'WeChat Files' 文件夹)",
+ ""
+ )
+
+ if folder_path and folder_path != '':
+ if check_dir(folder_path) == 0:
+ self.setSuccessinfo(f"已选择微信数据目录:{folder_path}")
+ config = self.create_config_from_paths([folder_path])
+ if self.save_config(config):
+ self.config_exists = True
+ return True
+ else:
+ self.setWarninginfo("保存配置失败")
+ return False
+ else:
+ self.setWarninginfo("选择的目录不是有效的微信数据目录,请重新选择")
+ return False
+ else:
+ self.setWarninginfo("未选择目录,部分功能可能无法使用")
+ return False
+
def __init__(self):
super().__init__()
loadUi(working_dir + "/images/main.ui", self)
self._frame()
self._eventfilter()
+ self.init_table()
+
+ self.check_select_all.stateChanged.connect(self.toggle_select_all)
+
self.doFadeIn()
self.config_exists = True
self.show()
- # 判断配置文件是否存在
if not os.path.exists(working_dir + "/config.json"):
- self.setWarninginfo("首次使用,即将自动弹出配置窗口")
+ self.setSuccessinfo("正在智能检测微信数据目录...")
self.config_exists = False
-
+
timer = QTimer(self)
- timer.timeout.connect(self.show_config_window)
- timer.setSingleShot(True) # 只执行一次
- # 设置定时器的时间间隔,这里设置为 1000ms(1秒)
- timer.start(1000)
+ def check_and_prompt():
+ if self.smart_detect_wechat_path():
+ self.setSuccessinfo("检测成功!已经准备好,可以开始了!")
+ else:
+ self.prompt_select_wechat_path()
+
+ timer.timeout.connect(check_and_prompt)
+ timer.setSingleShot(True)
+ timer.start(500)
+ else:
+ self.setSuccessinfo("已经准备好,可以开始了!")
if __name__ == '__main__':
diff --git a/utils/__pycache__/deleteThread.cpython-310.pyc b/utils/__pycache__/deleteThread.cpython-310.pyc
new file mode 100644
index 000000000..19bd4b18a
Binary files /dev/null and b/utils/__pycache__/deleteThread.cpython-310.pyc differ
diff --git a/utils/__pycache__/multiDeleteThread.cpython-310.pyc b/utils/__pycache__/multiDeleteThread.cpython-310.pyc
new file mode 100644
index 000000000..48e94ec52
Binary files /dev/null and b/utils/__pycache__/multiDeleteThread.cpython-310.pyc differ
diff --git a/utils/__pycache__/resources.cpython-310.pyc b/utils/__pycache__/resources.cpython-310.pyc
new file mode 100644
index 000000000..ec867f47e
Binary files /dev/null and b/utils/__pycache__/resources.cpython-310.pyc differ
diff --git a/utils/__pycache__/scanThread.cpython-310.pyc b/utils/__pycache__/scanThread.cpython-310.pyc
new file mode 100644
index 000000000..481c18057
Binary files /dev/null and b/utils/__pycache__/scanThread.cpython-310.pyc differ
diff --git a/utils/__pycache__/selectVersion.cpython-310.pyc b/utils/__pycache__/selectVersion.cpython-310.pyc
new file mode 100644
index 000000000..a89a81d73
Binary files /dev/null and b/utils/__pycache__/selectVersion.cpython-310.pyc differ
diff --git a/utils/scanThread.py b/utils/scanThread.py
new file mode 100644
index 000000000..c4c526d27
--- /dev/null
+++ b/utils/scanThread.py
@@ -0,0 +1,185 @@
+import os, datetime, re
+from pathlib import Path, PureWindowsPath
+from PyQt5.QtCore import QThread, pyqtSignal, QMutex
+
+
+class ScanThread(QThread):
+ scan_progress_signal = pyqtSignal(int)
+ scan_file_found_signal = pyqtSignal(str, str, str)
+ scan_finished_signal = pyqtSignal(int, int)
+ scan_error_signal = pyqtSignal(str)
+
+ def __init__(self, config):
+ super(ScanThread, self).__init__()
+ self.config = config
+ self.is_running = True
+ self.qmut = QMutex()
+
+ def stop(self):
+ self.is_running = False
+
+ def get_file_size_str(self, size):
+ if size < 1024:
+ return f"{size} B"
+ elif size < 1024 * 1024:
+ return f"{size / 1024:.2f} KB"
+ elif size < 1024 * 1024 * 1024:
+ return f"{size / (1024 * 1024):.2f} MB"
+ else:
+ return f"{size / (1024 * 1024 * 1024):.2f} GB"
+
+ def get_file_size(self, path):
+ try:
+ if os.path.isfile(path):
+ return os.path.getsize(path)
+ elif os.path.isdir(path):
+ total_size = 0
+ for dirpath, dirnames, filenames in os.walk(path):
+ for f in filenames:
+ fp = os.path.join(dirpath, f)
+ try:
+ total_size += os.path.getsize(fp)
+ except:
+ pass
+ return total_size
+ except:
+ pass
+ return 0
+
+ def get_fileNum(self, path, day, picCacheCheck, fileCheck, picCheck, videoCheck, file_list, dir_list):
+ if not self.is_running:
+ return
+
+ dir_name = PureWindowsPath(path)
+ correct_path = Path(dir_name)
+ now = datetime.datetime.now()
+
+ if picCacheCheck:
+ path_one = correct_path / 'Attachment'
+ path_two = correct_path / 'FileStorage/Cache'
+ self.getPathFileNum(now, day, path_one, path_two, file_list, dir_list)
+
+ if fileCheck:
+ path_one = correct_path / 'Files'
+ path_two = correct_path / 'FileStorage/File'
+ self.getPathFileNum(now, day, path_one, path_two, file_list, dir_list)
+
+ if picCheck:
+ path_one = correct_path / 'Image/Image'
+ path_two = correct_path / 'FileStorage/Image'
+ self.getPathFileNum(now, day, path_one, path_two, file_list, dir_list)
+
+ if videoCheck:
+ path_one = correct_path / 'Video'
+ path_two = correct_path / 'FileStorage/Video'
+ self.getPathFileNum(now, day, path_one, path_two, file_list, dir_list)
+
+ def pathFileDeal(self, now, day, path, file_list, dir_list):
+ if not self.is_running:
+ return
+
+ if os.path.exists(path):
+ filelist = [
+ f for f in os.listdir(path)
+ if os.path.isfile(os.path.join(path, f))
+ ]
+ for i in range(0, len(filelist)):
+ if not self.is_running:
+ return
+
+ file_path = os.path.join(path, filelist[i])
+ if os.path.isdir(file_path):
+ continue
+
+ try:
+ timestamp = datetime.datetime.fromtimestamp(
+ os.path.getmtime(file_path))
+ diff = (now - timestamp).days
+ if diff >= day:
+ file_list.append(file_path)
+ file_size = self.get_file_size(file_path)
+ file_size_str = self.get_file_size_str(file_size)
+ self.scan_file_found_signal.emit(file_path, file_size_str, "file")
+ except Exception as e:
+ print(f"Error processing file {file_path}: {e}")
+ continue
+
+ def getPathFileNum(self, now, day, path_one, path_two, file_list, dir_list):
+ if not self.is_running:
+ return
+
+ self.pathFileDeal(now, day, path_one, file_list, dir_list)
+
+ td = datetime.datetime.now() - datetime.timedelta(days=day)
+ td_year = td.year
+ td_month = td.month
+
+ if os.path.exists(path_two):
+ osdir = os.listdir(path_two)
+ dirlist = []
+ for i in range(0, len(osdir)):
+ file_path = os.path.join(path_two, osdir[i])
+ if os.path.isdir(file_path):
+ dirlist.append(osdir[i])
+
+ for i in range(0, len(dirlist)):
+ if not self.is_running:
+ return
+
+ file_path = os.path.join(path_two, dirlist[i])
+ if os.path.isfile(file_path):
+ continue
+
+ if re.match('\d{4}(\-)\d{2}', dirlist[i]) != None:
+ cyear = int(dirlist[i].split('-', 1)[0])
+ cmonth = int(dirlist[i].split('-', 1)[1])
+ if self.__before_deadline(cyear, cmonth, td_year, td_month):
+ dir_list.append(file_path)
+ file_size = self.get_file_size(file_path)
+ file_size_str = self.get_file_size_str(file_size)
+ self.scan_file_found_signal.emit(file_path, file_size_str, "dir")
+ else:
+ if cmonth == td_month:
+ self.pathFileDeal(now, day, file_path, file_list, dir_list)
+
+ def __before_deadline(self, cyear, cmonth, td_year, td_month):
+ if cyear < td_year:
+ return True
+ elif cyear > td_year:
+ return False
+ elif cyear == td_year:
+ return cmonth < td_month
+
+ def run(self):
+ try:
+ file_list = []
+ dir_list = []
+
+ i = 0
+ total_users = len(self.config["users"])
+
+ for value in self.config["users"]:
+ if not self.is_running:
+ break
+
+ if value["is_clean"]:
+ self.get_fileNum(
+ self.config["data_dir"][i],
+ int(value["clean_days"]),
+ value["clean_pic_cache"],
+ value["clean_file"],
+ value["clean_pic"],
+ value["clean_video"],
+ file_list,
+ dir_list
+ )
+
+ progress = int((i + 1) / total_users * 100)
+ self.scan_progress_signal.emit(progress)
+ i = i + 1
+
+ if self.is_running:
+ self.scan_finished_signal.emit(len(file_list), len(dir_list))
+
+ except Exception as e:
+ self.scan_error_signal.emit(str(e))
diff --git a/utils/selectVersion.py b/utils/selectVersion.py
index 96236907a..cc1dde214 100644
--- a/utils/selectVersion.py
+++ b/utils/selectVersion.py
@@ -7,10 +7,15 @@
working_dir = os.path.split(os.path.realpath(__file__))[0]
def check_dir(file_path):
- list_ = os.listdir(file_path)
- if 'All Users' in list_ or 'Applet' in list_ or 'WMPF' in list_:
- return 0
- else:
+ if not os.path.exists(file_path):
+ return 1
+ try:
+ list_ = os.listdir(file_path)
+ if 'All Users' in list_ or 'Applet' in list_ or 'WMPF' in list_:
+ return 0
+ else:
+ return 1
+ except:
return 1
@@ -43,15 +48,98 @@ def read_registry_value(key_path, value_name):
def get_dir_name(filepath):
dirlist = []
names = []
- list_ = os.listdir(filepath)
- # 换用lambda 表达式更安全,remove函数如果不存在对象会抛出异常
- list_ = [element for element in list_ if element != 'All Users' and element != 'Applet' and element != 'WMPF']
- for i in range(0, len(list_)):
- file_path = os.path.join(filepath, list_[i])
- if os.path.isdir(file_path):
- dirlist.append(file_path)
- names.append(list_[i])
- return (dirlist, names)
+ if not os.path.exists(filepath):
+ return ([], [])
+ try:
+ list_ = os.listdir(filepath)
+ list_ = [element for element in list_ if element != 'All Users' and element != 'Applet' and element != 'WMPF']
+ for i in range(0, len(list_)):
+ file_path = os.path.join(filepath, list_[i])
+ if os.path.isdir(file_path):
+ dirlist.append(file_path)
+ names.append(list_[i])
+ return (dirlist, names)
+ except:
+ return ([], [])
+
+def find_all_wechat_paths():
+ """
+ 智能扫描所有可能的微信数据目录位置
+ 返回所有找到的有效路径列表
+ """
+ user = getpass.getuser()
+ found_paths = []
+
+ common_paths = [
+ f'C:\\Users\\{user}\\Documents\\WeChat Files',
+ f'C:\\Users\\{user}\\OneDrive\\Documents\\WeChat Files',
+ f'D:\\Documents\\WeChat Files',
+ f'E:\\Documents\\WeChat Files',
+ f'F:\\Documents\\WeChat Files',
+ f'C:\\Users\\{user}\\AppData\\Local\\Packages\\TencentWeChatLimited.forWindows10_sdtnhv12zgd7a\\LocalCache\\Roaming\\Tencent\\WeChatAppStore\\WeChatAppStore Files',
+ f'C:\\Users\\{user}\\AppData\\Local\\Packages\\TencentWeChatLimited.WeChatUWP_sdtnhv12zgd7a\\LocalCache\\Roaming\\Tencent\\WeChatAppStore\\WeChatAppStore Files',
+ ]
+
+ for drive in ['C', 'D', 'E', 'F', 'G']:
+ common_paths.append(f'{drive}:\\WeChat Files')
+ common_paths.append(f'{drive}:\\WeChat\\WeChat Files')
+ common_paths.append(f'{drive}:\\Program Files\\Tencent\\WeChat\\WeChat Files')
+ common_paths.append(f'{drive}:\\Program Files (x86)\\Tencent\\WeChat\\WeChat Files')
+ common_paths.append(f'{drive}:\\Users\\{user}\\WeChat Files')
+ common_paths.append(f'{drive}:\\Users\\{user}\\Tencent Files\\WeChat Files')
+
+ for path in common_paths:
+ if check_dir(path) == 0:
+ if path not in found_paths:
+ found_paths.append(path)
+
+ registry_key_paths = [
+ r"software\tencent\wechat",
+ r"Software\Tencent\WeChat",
+ r"SOFTWARE\Tencent\WeChat",
+ ]
+
+ for key_path in registry_key_paths:
+ value_names = ["FileSavePath", "InstallPath"]
+ for value_name in value_names:
+ value = read_registry_value(key_path, value_name)
+ if value:
+ if value == 'MyDocument:':
+ doc_path = f'C:\\Users\\{user}\\Documents\\WeChat Files'
+ if check_dir(doc_path) == 0 and doc_path not in found_paths:
+ found_paths.append(doc_path)
+ elif os.path.isdir(value):
+ wechat_path = os.path.join(value, 'WeChat Files')
+ if check_dir(wechat_path) == 0 and wechat_path not in found_paths:
+ found_paths.append(wechat_path)
+ if check_dir(value) == 0 and value not in found_paths:
+ found_paths.append(value)
+
+ try:
+ for key_path in registry_key_paths:
+ try:
+ key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, key_path)
+ i = 0
+ while True:
+ try:
+ subkey_name = winreg.EnumKey(key, i)
+ if 'WeChat' in subkey_name or 'Wechat' in subkey_name or 'wechat' in subkey_name:
+ subkey_path = f"{key_path}\\{subkey_name}"
+ value = read_registry_value(subkey_path, "FileSavePath")
+ if value and os.path.isdir(value):
+ wechat_path = os.path.join(value, 'WeChat Files')
+ if check_dir(wechat_path) == 0 and wechat_path not in found_paths:
+ found_paths.append(wechat_path)
+ i += 1
+ except OSError:
+ break
+ winreg.CloseKey(key)
+ except:
+ pass
+ except:
+ pass
+
+ return found_paths
class selectVersion:
@@ -66,11 +154,9 @@ def getAllPath(self):
if os.path.exists(dic[key]):
return get_dir_name(dic[key])
- # 注册表路径和字段名
registry_key_path = r"software\tencent\wechat"
value_name = "FileSavePath"
- # 读取字段值
value = read_registry_value(registry_key_path, value_name)
if value and value != 'MyDocument:' and os.path.isdir(value):
@@ -79,4 +165,3 @@ def getAllPath(self):
return get_dir_name(fpath)
else:
return ([], [])
-