def __init__(self, parent=None): super(GetAllFoldersWorker, self).__init__(parent) self._disk = None self.org_infos = None self._mutex = QMutex() self._is_work = False self.move_infos = None
class Callback(QThread): '''回调显示进度''' def __init__(self, task, parent=None): super(Callback, self).__init__(parent) self._task = task self._mutex = QMutex() self._stopped = True self._emit_msg = '' @property def emit_msg(self): return self._emit_msg def run(self): if self._stopped: self._mutex.lock() self._stopped = False old_size = self._task.now_size old_rate = int(1000 * old_size / self._task.total_size) old_time = time() sleep(uniform(0.5, 2)) now_size = self._task.now_size now_rate = int(1000 * now_size / self._task.total_size) now_time = time() if old_size != now_size and old_rate != now_rate: speed = change_size_unit((now_size - old_size) / (now_time - old_time)) + '/s' self._task.speed = speed self._task.rate = now_rate self._emit_msg = show_progress(self._task.name, self._task.total_size, self._task.now_size, speed) self._stopped = True self._mutex.unlock()
def __init__(self, parent=None): super(SetPwdWorker, self).__init__(parent) self._disk = None self.infos = [] self._work_id = -1 self._mutex = QMutex() self._is_work = False
def __init__(self, parent=None): super(RecManipulator, self).__init__(parent) self._disk = None self._mutex = QMutex() self._is_work = False self._action = None self._folders = [] self._files = []
def __init__(self, parent=None): super(RenameMkdirWorker, self).__init__(parent) self._disk = None self._work_id = -1 self._folder_list = None self.infos = None self._mutex = QMutex() self._is_work = False
def __init__(self, parent=None): super(DescPwdFetcher, self).__init__(parent) self._disk = None self.infos = None self.download = False self.dl_path = "" self._mutex = QMutex() self._is_work = False
def __init__(self, parent=None): super(GetMoreInfoWorker, self).__init__(parent) self._disk = None self._infos = None self._url = '' self._pwd = '' self._emit_link = False self._mutex = QMutex() self._is_work = False
def __init__(self, parent=None): super(CheckUpdateWorker, self).__init__(parent) self._ver = '' self._manual = False self._mutex = QMutex() self._is_work = False self._folder_id = None self._api = 'https://api.github.com/repos/rachpt/lanzou-gui/releases/latest' self._api_mirror = 'https://gitee.com/api/v5/repos/rachpt/lanzou-gui/releases/latest'
def __init__(self, parent=None): super(ListRefresher, self).__init__(parent) self._disk = None self._fid = -1 self.r_files = True self.r_folders = True self.r_path = True self._mutex = QMutex() self._is_work = False
def __init__(self, parent=None): super(GetSharedInfo, self).__init__(parent) self._disk = None self.share_url = "" self.pwd = "" self.is_file = "" self.is_folder = "" self._mutex = QMutex() self._is_work = False self._pat = r"(https?://(\w[-\w]*\.)?lanzou[a-z].com/[a-z]?[-/a-zA-Z0-9]+)[^a-zA-Z0-9]*([a-zA-Z0-9]+\w+)?"
def __init__(self, thread=3, parent=None): super(TaskManager, self).__init__(parent) self._disk = None self._tasks = {} self._queues = [] self._thread = thread self._count = 0 self._mutex = QMutex() self._is_work = False self._old_msg = "" self._workers = {} self._allow_big_file = False
class RemoveFilesWorker(QThread): '''删除文件(夹)线程''' msg = pyqtSignal(object, object) finished = pyqtSignal() def __init__(self, parent=None): super(RemoveFilesWorker, self).__init__(parent) self._disk = None self.infos = None self._mutex = QMutex() self._is_work = False def set_disk(self, disk): self._disk = disk def set_values(self, infos): self.infos = infos self.start() def __del__(self): self.wait() def stop(self): self._mutex.lock() self._is_work = False self._mutex.unlock() def run(self): if not self._is_work: self._mutex.lock() self._is_work = True if not self.infos: self._is_work = False self._mutex.unlock() return for i in self.infos: try: self._disk.delete(i['fid'], i['is_file']) except TimeoutError: self.msg.emit(f"删除 {i['name']} 因网络超时失败!", 3000) except Exception as e: logger.error(f"RemoveFileWorker error: e={e}") self.finished.emit() self._is_work = False self._mutex.unlock() else: self.msg.emit("后台正在运行删除指令!", 3100)
class GetRecListsWorker(QThread): '''获取回收站列表''' folders = pyqtSignal(object) infos = pyqtSignal(object, object) msg = pyqtSignal(str, int) def __init__(self, parent=None): super(GetRecListsWorker, self).__init__(parent) self._disk = None self._mutex = QMutex() self._is_work = False self._folder_id = None def set_disk(self, disk): self._disk = disk def set_values(self, fid): # 用于获取回收站指定文件夹内文件信息 self._folder_id = fid self.start() def __del__(self): self.wait() def stop(self): self._mutex.lock() self._is_work = False self._mutex.unlock() def run(self): if not self._is_work: self._mutex.lock() self._is_work = True try: if self._folder_id: file_lists = self._disk.get_rec_file_list( folder_id=self._folder_id) self._folder_id = None self.folders.emit(file_lists) raise UserWarning dir_lists = self._disk.get_rec_dir_list() file_lists = self._disk.get_rec_file_list(folder_id=-1) self.infos.emit(dir_lists, file_lists) self.msg.emit("刷新列表成功!", 2000) except TimeoutError: self.msg.emit("网络超时,请稍后重试!", 6000) except UserWarning: pass except Exception as e: logger.error(f"GetRecListsWorker error: e={e}") self._is_work = False self._mutex.unlock() else: self.msg.emit("后台正在运行,请稍后重试!", 3100)
class LogoutWorker(QThread): '''登出''' succeeded = pyqtSignal() msg = pyqtSignal(str, int) def __init__(self, parent=None): super(LogoutWorker, self).__init__(parent) self._disk = None self.update_ui = True self._mutex = QMutex() self._is_work = False def set_disk(self, disk): self._disk = disk def set_values(self, update_ui=True): self.update_ui = update_ui self.start() def __del__(self): self.wait() def stop(self): self._mutex.lock() self._is_work = False self._mutex.unlock() def run(self): if not self._is_work: self._mutex.lock() self._is_work = True try: res = self._disk.logout() if res == LanZouCloud.SUCCESS: if self.update_ui: self.succeeded.emit() self.msg.emit("已经退出登录!", 4000) else: self.msg.emit("失败,请重试!", 5000) except TimeoutError: self.msg.emit("网络超时,请稍后重试!", 6000) except Exception as e: logger.error(f"LogoutWorker error: e={e}") self._is_work = False self._mutex.unlock() else: self.msg.emit("后台正在运行,请稍后重试!", 3100)
def __init__(self, parent=None): super(RemoveFilesWorker, self).__init__(parent) self._disk = None self.infos = None self._mutex = QMutex() self._is_work = False
class GetAllFoldersWorker(QThread): '''获取所有文件夹name与fid,用于文件移动''' infos = pyqtSignal(object, object) msg = pyqtSignal(str, int) moved = pyqtSignal(bool, bool, bool) def __init__(self, parent=None): super(GetAllFoldersWorker, self).__init__(parent) self._disk = None self.org_infos = None self._mutex = QMutex() self._is_work = False self.move_infos = None def set_disk(self, disk): self._disk = disk def set_values(self, org_infos): self.org_infos = org_infos # 对话框标识文件与文件夹 self.move_infos = [] # 清除上次影响 self.start() def move_file(self, infos): '''移动文件至新的文件夹''' self.move_infos = infos # file_id, folder_id, f_name, type(size) self.start() def __del__(self): self.wait() def stop(self): self._mutex.lock() self._is_work = False self._mutex.unlock() def move_file_folder(self, info, no_err: bool, r_files: bool, r_folders: bool): """移动文件(夹)""" # no_err 判断是否需要更新 UI if info.is_file: # 文件 if self._disk.move_file(info.id, info.new_id) == LanZouCloud.SUCCESS: self.msg.emit(f"{info.name} 移动成功!", 3000) no_err = True r_files = True else: self.msg.emit(f"移动文件{info.name}失败!", 4000) else: # 文件夹 if self._disk.move_folder(info.id, info.new_id) == LanZouCloud.SUCCESS: self.msg.emit(f"{info.name} 移动成功!", 3000) no_err = True r_folders = True else: self.msg.emit(f"移动文件夹 {info.name} 失败!移动的文件夹中不能包含子文件夹!", 4000) return no_err, r_files, r_folders def run(self): if not self._is_work: self._mutex.lock() self._is_work = True if self.move_infos: # 移动文件 no_err = False r_files = False r_folders = False for info in self.move_infos: try: no_err, r_files, r_folders = self.move_file_folder( info, no_err, r_files, r_folders) except TimeoutError: self.msg.emit(f"移动文件(夹) {info.name} 失败,网络超时!请稍后重试", 5000) except Exception as e: logger.error(f"GetAllFoldersWorker error: e={e}") self.msg.emit(f"移动文件(夹) {info.name} 失败,未知错误!", 5000) if no_err: # 没有错误就更新ui sleep(2.1) # 等一段时间后才更新文件列表 self.moved.emit(r_files, r_folders, False) else: # 获取所有文件夹 try: self.msg.emit("网络请求中,请稍候……", 0) all_dirs_dict = self._disk.get_move_folders().name_id self.infos.emit(self.org_infos, all_dirs_dict) self.msg.emit("", 0) # 删除提示信息 except TimeoutError: self.msg.emit("网络超时!稍后重试", 6000) except Exception as e: logger.error(f"GetAllFoldersWorker error: e={e}") self._is_work = False self._mutex.unlock() else: self.msg.emit("后台正在运行,请稍后重试!", 3100)
class DescPwdFetcher(QThread): '''获取描述与提取码 线程''' desc = pyqtSignal(object) tasks = pyqtSignal(object) msg = pyqtSignal(object, object) def __init__(self, parent=None): super(DescPwdFetcher, self).__init__(parent) self._disk = None self.infos = None self.download = False self.dl_path = "" self._mutex = QMutex() self._is_work = False def set_disk(self, disk): self._disk = disk def set_values(self, infos, download=False, dl_path=""): self.infos = infos # 列表的列表 self.download = download # 标识激发下载器 self.dl_path = dl_path self.start() def __del__(self): self.wait() def stop(self): self._mutex.lock() self._is_work = False self._mutex.unlock() def run(self): if not self._is_work: self._mutex.lock() self._is_work = True try: if not self.infos: raise UserWarning _tasks = {} _infos = [] for info in self.infos: if info.id: # disk 运行 if info.is_file: # 文件 res = self._disk.get_share_info(info.id, is_file=True) else: # 文件夹 res = self._disk.get_share_info(info.id, is_file=False) if res.code == LanZouCloud.SUCCESS: info.pwd = res.pwd info.url = res.url info.desc = res.desc elif res.code == LanZouCloud.NETWORK_ERROR: self.msg.emit("网络错误,请稍后重试!", 6000) continue _infos.append(info) # info -> lanzou.gui.models.FileInfos _tasks[info.url] = DlJob(infos=info, path=self.dl_path, total_file=1) if self.download: self.tasks.emit(_tasks) else: # 激发简介更新 self.desc.emit(_infos) except TimeoutError: self.msg.emit("网络超时,请稍后重试!", 6000) except UserWarning: pass except Exception as e: logger.error(f"GetPwdFetcher error: e={e}") self._is_work = False self._mutex.unlock() else: self.msg.emit("后台正在运行指令!请稍后重试", 3100)
class RecManipulator(QThread): '''操作回收站''' msg = pyqtSignal(str, int) succeeded = pyqtSignal() def __init__(self, parent=None): super(RecManipulator, self).__init__(parent) self._disk = None self._mutex = QMutex() self._is_work = False self._action = None self._folders = [] self._files = [] def set_disk(self, disk): self._disk = disk def set_values(self, infos, action): # 操作回收站选定行 self._action = None self._folders = [] self._files = [] for item in infos: if isinstance(item, RecFile): self._files.append(item.id) elif isinstance(item, RecFolder): self._folders.append(item.id) self._action = action self.start() def __del__(self): self.wait() def stop(self): self._mutex.lock() self._is_work = False self._mutex.unlock() def run(self): if not self._is_work: self._mutex.lock() self._is_work = True try: res = None if self._action == "recovery": if self._files or self._folders: res = self._disk.recovery_multi( self._files, self._folders) if res == LanZouCloud.SUCCESS: self.msg.emit("选定文件(夹)恢复成功,即将刷新列表", 2500) elif self._action == "delete": if self._files or self._folders: if self._files or self._folders: res = self._disk.delete_rec_multi( self._files, self._folders) if res == LanZouCloud.SUCCESS: self.msg.emit("选定文件(夹)彻底删除成功,即将刷新列表", 2500) elif self._action == "clean": res = self._disk.clean_rec() if res == LanZouCloud.SUCCESS: self.msg.emit("清空回收站成功,即将刷新列表", 2500) elif self._action == "recovery_all": res = self._disk.recovery_all() if res == LanZouCloud.SUCCESS: self.msg.emit("文件(夹)全部还原成功,即将刷新列表", 2500) if isinstance(res, int): if res == LanZouCloud.FAILED: self.msg.emit("失败,请重试!", 4500) elif res == LanZouCloud.NETWORK_ERROR: self.msg.emit("网络错误,请稍后重试!", 4500) else: sleep(2.6) self.succeeded.emit() except TimeoutError: self.msg.emit("网络超时,请稍后重试!", 6000) except Exception as e: logger.error(f"RecManipulator error: e={e}") self._is_work = False self._action = None self._folders = [] self._files = [] self._mutex.unlock() else: self.msg.emit("后台正在运行,请稍后重试!", 3100)
class RenameMkdirWorker(QThread): """重命名、修改简介与新建文件夹 线程""" # infos = pyqtSignal(object, object) msg = pyqtSignal(str, int) update = pyqtSignal(object, object, object, object) def __init__(self, parent=None): super(RenameMkdirWorker, self).__init__(parent) self._disk = None self._work_id = -1 self._folder_list = None self.infos = None self._mutex = QMutex() self._is_work = False def set_disk(self, disk): self._disk = disk def set_values(self, infos, work_id, folder_list): self.infos = infos # 对话框标识文件与文件夹 self._work_id = work_id self._folder_list = folder_list self.start() def __del__(self): self.wait() def stop(self): self._mutex.lock() self._is_work = False self._mutex.unlock() def run(self): if not self._is_work: self._mutex.lock() self._is_work = True action = self.infos[0] try: if action == 'new': # 新建文件夹 new_name = self.infos[1] new_des = self.infos[2] if new_name in self._folder_list.keys(): self.msg.emit(f"文件夹已存在:{new_name}", 7000) else: res = self._disk.mkdir(self._work_id, new_name, new_des) if res == LanZouCloud.MKDIR_ERROR: self.msg.emit(f"创建文件夹失败:{new_name}", 7000) else: sleep(1.5) # 暂停一下,否则无法获取新建的文件夹 self.update.emit(self._work_id, False, True, False) # 此处仅更新文件夹,并显示 self.msg.emit(f"成功创建文件夹:{new_name}", 4000) else: # 重命名、修改简介 has_file = False has_folder = False failed = False for info in self.infos[1]: if info.is_file: # 修改文件描述 res = self._disk.set_desc(info.id, info.new_des, is_file=info.is_file) if res == LanZouCloud.SUCCESS: has_file = True else: failed = True else: # 修改文件夹,action == "folder" name = info.new_name or info.nmae res = self._disk._set_dir_info( info.id, str(name), str(info.new_des)) if res == LanZouCloud.SUCCESS: has_folder = True else: failed = True self.update.emit(self._work_id, has_file, has_folder, False) if failed: self.msg.emit("有发生错误!", 6000) else: self.msg.emit("修改成功!", 4000) except TimeoutError: self.msg.emit("网络超时,请稍后重试!", 6000) except Exception as e: logger.error(f"RenameMikdirWorker error: e={e}") self._is_work = False self._mutex.unlock() else: self.msg.emit("后台正在运行,请稍后重试!", 3100)
def __init__(self, task, parent=None): super(Callback, self).__init__(parent) self._task = task self._mutex = QMutex() self._stopped = True self._emit_msg = ''
def __init__(self, parent=None): super(GetRecListsWorker, self).__init__(parent) self._disk = None self._mutex = QMutex() self._is_work = False self._folder_id = None
QHBoxLayout, QLabel, QLineEdit, QMessageBox, QProgressBar, QPushButton, QStatusBar, QVBoxLayout, QWidget, ) WorkerRespnose = namedtuple( "WorkerRespnose", "thumb_img title author medias media_counts publish_date") DownloadCountsMutex = QMutex() # seperate worker thread for background processing and to avoid UI freez class WorkerThread(QThread): # setup response signal worker_response = pyqtSignal(WorkerRespnose) # setup error signal worker_err_response = pyqtSignal() # additional parameter as url def __init__(self, media_id): # invoke the __init__ of super as well super(WorkerThread, self).__init__() self.media_id = media_id
class ListRefresher(QThread): '''跟新目录文件与文件夹列表线程''' infos = pyqtSignal(object) err_msg = pyqtSignal(str, int) def __init__(self, parent=None): super(ListRefresher, self).__init__(parent) self._disk = None self._fid = -1 self.r_files = True self.r_folders = True self.r_path = True self._mutex = QMutex() self._is_work = False def set_disk(self, disk): self._disk = disk def set_values(self, fid, r_files=True, r_folders=True, r_path=True): if not self._is_work: self._fid = fid self.r_files = r_files self.r_folders = r_folders self.r_path = r_path self.start() else: self.err_msg.emit("正在更新目录,请稍后再试!", 3100) def __del__(self): self.wait() def stop(self): self._mutex.lock() self._is_work = False self._mutex.unlock() def goto_root_dir(self): self._fid = -1 self.run() def run(self): if not self._is_work: self._mutex.lock() self._is_work = True emit_infos = {} # 传递更新内容 emit_infos['r'] = { 'fid': self._fid, 'files': self.r_files, 'folders': self.r_folders, 'path': self.r_path } try: if self.r_files: # [i.id, i.name, i.size, i.time, i.downs, i.has_pwd, i.has_des] info = { i.name: i for i in self._disk.get_file_list(self._fid) } emit_infos['file_list'] = { key: info.get(key) for key in sorted(info.keys()) } # {name-File} if self.r_folders: folders, full_path = self._disk.get_dir_list(self._fid) if not full_path and not folders and self._fid != -1: self.err_msg.emit(f"文件夹id {self._fid} 不存在,将切换到更目录", 2900) self._is_work = False self._mutex.unlock() return self.goto_root_dir() info = {i.name: i for i in folders} emit_infos['folder_list'] = { key: info.get(key) for key in sorted(info.keys()) } # {name-Folder} emit_infos['path_list'] = full_path except TimeoutError: self.err_msg.emit("网络超时,无法更新目录,稍后再试!", 7000) except Exception as e: self.err_msg.emit("未知错误,无法更新目录,稍后再试!", 7000) logger.error(f"ListRefresher error: e={e}") else: self.infos.emit(emit_infos) self._is_work = False self._mutex.unlock()
class TaskManager(QThread): """任务控制器线程,控制后台上传下载""" mgr_msg = pyqtSignal(str, int) mgr_finished = pyqtSignal(int) update = pyqtSignal() def __init__(self, thread=3, parent=None): super(TaskManager, self).__init__(parent) self._disk = None self._tasks = {} self._queues = [] self._thread = thread self._count = 0 self._mutex = QMutex() self._is_work = False self._old_msg = "" self._workers = {} self._allow_big_file = False def set_allow_big_file(self, allow_big_file): self._allow_big_file = allow_big_file def set_disk(self, disk): self._disk = disk def set_thread(self, thread): self._thread = thread def stop_task(self, task): """暂停任务""" if task.url in self._workers and self._workers[task.url].isRunning(): logger.debug(f"Stop job: {task.url}") try: self._tasks[task.url].pause = True self._workers[task.url].stop() self._tasks[task.url].run = False except Exception as err: logger.error(f"Stop task: err={err}") else: logger.debug(f"Stop job: {task.url} is not running!") self.update.emit() def start_task(self, task): """开始已暂停任务""" if task.url not in self._workers: self.add_task(task) elif not self._workers[task.url].isRunning(): logger.debug(f"Start job: {task}") self._workers[task.url].start() self._tasks[task.url].run = True self._tasks[task.url].pause = False self.update.emit() self.start() def add_task(self, task): logger.debug(f"TaskMgr add one: added={task.added}, pause={task.pause}") if task.url not in self._tasks.keys(): self._tasks[task.url] = task task.added = False task.pause = False task.info = None self.start() def add_tasks(self, tasks: dict): logger.debug(f"TaskMgr add: tasks={tasks}") self._tasks.update(tasks) self.start() def del_task(self, task): logger.debug(f"TaskMgr del: url={task.url}") if task in self._queues: self._queues.remove(task) if task.url in self._tasks: del self._tasks[task.url] if task.url in self._workers: del self._workers[task.url] def _task_to_queue(self): for task in self._tasks.values(): if not task.added and not task.pause and task not in self._queues: logger.debug(f"TaskMgr task2queue: url={task.url}") self._queues.append(task) task.added = True def __del__(self): self.wait() def _ahead_msg(self, msg): if self._old_msg != msg: if self._count == 1: self.mgr_msg.emit(msg, 0) else: self.mgr_msg.emit(f"有{self._count}个后台任务正在运行", 0) self._old_msg = msg def _ahead_error(self): self.update.emit() def _ahead_folder_error(self, code, file): # 需要单独考虑,不在 task中 pass def _update_emit(self): self.update.emit() def _add_thread(self, task): self.update.emit() logger.debug(f"TaskMgr count: count={self._count}") self._count -= 1 del self._workers[task.url] # 发送所有任务完成信号 failed_task_num = 0 for task in self._tasks.values(): if not task.info: if task.rate < 1000: return None else: failed_task_num += 1 logger.debug(f"TaskMgr all finished!: failed_task_num={failed_task_num}") self.mgr_finished.emit(failed_task_num) def stop(self): self._mutex.lock() self._is_work = False self._mutex.unlock() def run(self): if not self._is_work: self._mutex.lock() self._is_work = True while True: self._task_to_queue() if not self._queues: break while self._count >= self._thread: self.sleep(1) self._count += 1 task = self._queues.pop() logger.debug(f"TaskMgr run: url={task.url}") if task.type == 'dl': self._workers[task.url] = Downloader(self._disk, task, Callback) self.mgr_msg.emit(f"准备下载:<font color='#FFA500'>{task.name}</font>", 0) else: self._workers[task.url] = Uploader(self._disk, task, Callback, self._allow_big_file) self.mgr_msg.emit(f"准备上传:<font color='#FFA500'>{task.name}</font>", 0) try: self._workers[task.url].finished_.connect(self._add_thread) self._workers[task.url].proc.connect(self._ahead_msg) self._workers[task.url].update.connect(self._update_emit) self._workers[task.url].folder_file_failed.connect(self._ahead_folder_error) self._workers[task.url].failed.connect(self._ahead_error) self._workers[task.url].start() except Exception as err: logger.error(f"TaskMgr Error: err={err}") self._is_work = False self._mutex.unlock()
class CheckUpdateWorker(QThread): '''检测软件更新''' infos = pyqtSignal(object, object) bg_update_infos = pyqtSignal(object, object) def __init__(self, parent=None): super(CheckUpdateWorker, self).__init__(parent) self._ver = '' self._manual = False self._mutex = QMutex() self._is_work = False self._folder_id = None self._api = 'https://api.github.com/repos/rachpt/lanzou-gui/releases/latest' self._api_mirror = 'https://gitee.com/api/v5/repos/rachpt/lanzou-gui/releases/latest' def set_values(self, ver: str, manual: bool = False): # 检查更新 self._ver = ver self._manual = manual self.start() def __del__(self): self.wait() def stop(self): self._mutex.lock() self._is_work = False self._mutex.unlock() def run(self): if not self._is_work: self._mutex.lock() self._is_work = True resp = None try: resp = requests.get(self._api).json() except (requests.RequestException, TimeoutError, requests.exceptions.ConnectionError): logger.debug("chcek update from github error") try: resp = requests.get(self._api_mirror).json() except: logger.debug("chcek update from gitee error") except Exception as e: logger.error(f"CheckUpdateWorker error: e={e}") if resp: try: tag_name, msg = resp['tag_name'], resp['body'] ver = self._ver.replace('v', '').split('-')[0].split('.') ver2 = tag_name.replace('v', '').split('-')[0].split('.') local_version = int(ver[0]) * 100 + int(ver[1]) * 10 + int( ver[2]) remote_version = int(ver2[0]) * 100 + int( ver2[1]) * 10 + int(ver2[2]) if remote_version > local_version: urls = re.findall(r'https?://[-\.a-zA-Z0-9/_#?&%@]+', msg) for url in urls: new_url = f'<a href="{url}">{url}</a>' msg = msg.replace(url, new_url) msg = msg.replace('\n', '<br />') self.infos.emit(tag_name, msg) if not self._manual: # 打开软件时检测更新 self.bg_update_infos.emit(tag_name, msg) elif self._manual: self.infos.emit("0", "目前还没有发布新版本!") except AttributeError: if self._manual: self.infos.emit("v0.0.0", "检查更新时发生异常,请重试!") except Exception as e: logger.error(f"Check Update Version error: e={e}") else: if self._manual: self.infos.emit( "v0.0.0", f"检查更新时 <a href='{self._api}'>api.github.com</a>、<a href='{self._api_mirror}'>gitee.com</a> 拒绝连接,请稍后重试!" ) self._manual = False self._is_work = False self._mutex.unlock() else: if self._manual: self.infos.emit("v0.0.0", "后台正在运行,请稍等!")
class GetMoreInfoWorker(QThread): '''获取文件直链、文件(夹)提取码描述,用于登录后显示更多信息''' infos = pyqtSignal(object) share_url = pyqtSignal(object) dl_link = pyqtSignal(object) msg = pyqtSignal(str, int) def __init__(self, parent=None): super(GetMoreInfoWorker, self).__init__(parent) self._disk = None self._infos = None self._url = '' self._pwd = '' self._emit_link = False self._mutex = QMutex() self._is_work = False def set_disk(self, disk): self._disk = disk def set_values(self, infos, emit_link=False): self._infos = infos self._emit_link = emit_link self.start() def get_dl_link(self, url, pwd): self._url = url self._pwd = pwd self.start() def __del__(self): self.wait() def stop(self): self._mutex.lock() self._is_work = False self._mutex.unlock() def run(self): # infos: ID/None,文件名,大小,日期,下载次数(dl_count),提取码(pwd),描述(desc),|链接(share-url) if not self._is_work and self._infos: self._mutex.lock() self._is_work = True try: if not self._url: # 获取普通信息 if isinstance(self._infos, Infos): if self._infos.id: # 从 disk 运行 self.msg.emit("网络请求中,请稍候……", 0) _info = self._disk.get_share_info( self._infos.id, is_file=self._infos.is_file) self._infos.desc = _info.desc self._infos.pwd = _info.pwd self._infos.url = _info.url if self._emit_link: self.share_url.emit(self._infos) else: self.infos.emit(self._infos) self.msg.emit("", 0) # 删除提示信息 else: # 获取下载直链 res = self._disk.get_file_info_by_url(self._url, self._pwd) if res.code == LanZouCloud.SUCCESS: self.dl_link.emit("{}".format(res.durl or "无")) # 下载直链 elif res.code == LanZouCloud.NETWORK_ERROR: self.dl_link.emit("网络错误!获取失败") # 下载直链 else: self.dl_link.emit("其它错误!") # 下载直链 except TimeoutError: self.msg.emit("网络超时!稍后重试", 6000) except Exception as e: logger.error(f"GetMoreInfoWorker error: e={e}") self._is_work = False self._url = '' self._pwd = '' self._mutex.unlock() else: self.msg.emit("后台正在运行,请稍后重试!", 3100)
def __init__(self) -> None: super(MainWindow, self).__init__() self.setWindowTitle("ASET") self.document_base = None self.preprocessing_phase = None self.matching_phase = None self.statistics = Statistics(True) self.collect_statistics = True # set up the api_thread and api and connect slots and signals self.feedback_mutex = QMutex() self.feedback_cond = QWaitCondition() self.api = ASETAPI(self.feedback_mutex, self.feedback_cond) self.api_thread = QThread() self.api.moveToThread(self.api_thread) self._connect_slots_and_signals() self.api_thread.start() # set up the status bar self.status_bar = self.statusBar() self.status_bar.setFont(STATUS_BAR_FONT) self.status_widget = QWidget(self.status_bar) self.status_widget_layout = QHBoxLayout(self.status_widget) self.status_widget_layout.setContentsMargins(0, 0, 0, 0) self.status_widget_message = QLabel() self.status_widget_message.setFont(STATUS_BAR_FONT) self.status_widget_message.setMinimumWidth(10) self.status_widget_layout.addWidget(self.status_widget_message) self.status_widget_progress = QProgressBar() self.status_widget_progress.setMinimumWidth(10) self.status_widget_progress.setMaximumWidth(200) self.status_widget_progress.setTextVisible(False) self.status_widget_progress.setMaximumHeight(20) self.status_widget_layout.addWidget(self.status_widget_progress) self.status_bar.addPermanentWidget(self.status_widget) # set up the actions self._all_actions = [] self._was_enabled = [] self.exit_action = QAction("&Exit", self) self.exit_action.setIcon(QIcon("aset_ui/resources/leave.svg")) self.exit_action.setStatusTip("Exit the application.") self.exit_action.triggered.connect(QApplication.instance().quit) self._all_actions.append(self.exit_action) self.show_document_base_creator_widget_action = QAction("&Create new document base", self) self.show_document_base_creator_widget_action.setIcon(QIcon("aset_ui/resources/two_documents.svg")) self.show_document_base_creator_widget_action.setStatusTip( "Create a new document base from a collection of .txt files and a list of attribute names." ) self.show_document_base_creator_widget_action.triggered.connect(self.show_document_base_creator_widget_task) self._all_actions.append(self.show_document_base_creator_widget_action) self.add_attribute_action = QAction("&Add attribute", self) self.add_attribute_action.setIcon(QIcon("aset_ui/resources/plus.svg")) self.add_attribute_action.setStatusTip("Add a new attribute to the document base.") self.add_attribute_action.triggered.connect(self.add_attribute_task) self.add_attribute_action.setEnabled(False) self._all_actions.append(self.add_attribute_action) self.remove_attribute_action = QAction("&Remove attribute", self) self.remove_attribute_action.setIcon(QIcon("aset_ui/resources/trash.svg")) self.remove_attribute_action.setStatusTip("Remove an attribute from the document base.") self.remove_attribute_action.triggered.connect(self.remove_attribute_task) self.remove_attribute_action.setEnabled(False) self._all_actions.append(self.remove_attribute_action) self.forget_matches_for_attribute_action = QAction("&Forget matches for attribute", self) self.forget_matches_for_attribute_action.setIcon(QIcon("aset_ui/resources/redo.svg")) self.forget_matches_for_attribute_action.setStatusTip("Forget the matches for a single attribute.") self.forget_matches_for_attribute_action.triggered.connect(self.forget_matches_for_attribute_task) self.forget_matches_for_attribute_action.setEnabled(False) self._all_actions.append(self.forget_matches_for_attribute_action) self.load_document_base_from_bson_action = QAction("&Load document base", self) self.load_document_base_from_bson_action.setIcon(QIcon("aset_ui/resources/folder.svg")) self.load_document_base_from_bson_action.setStatusTip("Load an existing document base from a .bson file.") self.load_document_base_from_bson_action.triggered.connect(self.load_document_base_from_bson_task) self._all_actions.append(self.load_document_base_from_bson_action) self.save_document_base_to_bson_action = QAction("&Save document base", self) self.save_document_base_to_bson_action.setIcon(QIcon("aset_ui/resources/save.svg")) self.save_document_base_to_bson_action.setStatusTip("Save the document base in a .bson file.") self.save_document_base_to_bson_action.triggered.connect(self.save_document_base_to_bson_task) self.save_document_base_to_bson_action.setEnabled(False) self._all_actions.append(self.save_document_base_to_bson_action) self.save_table_to_csv_action = QAction("&Export table to CSV", self) self.save_table_to_csv_action.setIcon(QIcon("aset_ui/resources/table.svg")) self.save_table_to_csv_action.setStatusTip("Save the table to a .csv file.") self.save_table_to_csv_action.triggered.connect(self.save_table_to_csv_task) self.save_table_to_csv_action.setEnabled(False) self._all_actions.append(self.save_table_to_csv_action) self.forget_matches_action = QAction("&Forget all matches", self) self.forget_matches_action.setIcon(QIcon("aset_ui/resources/redo.svg")) self.forget_matches_action.setStatusTip("Forget the matches for all attributes.") self.forget_matches_action.triggered.connect(self.forget_matches_task) self.forget_matches_action.setEnabled(False) self._all_actions.append(self.forget_matches_action) self.load_and_run_default_preprocessing_phase_action = QAction( "&Load and run default preprocessing phase", self ) self.load_and_run_default_preprocessing_phase_action.setStatusTip( "Load the default preprocessing phase and run it on the document collection." ) self.load_and_run_default_preprocessing_phase_action.setIcon(QIcon("aset_ui/resources/run_run.svg")) self.load_and_run_default_preprocessing_phase_action.setDisabled(True) self.load_and_run_default_preprocessing_phase_action.triggered.connect( self.load_and_run_default_preprocessing_phase_task ) self._all_actions.append(self.load_and_run_default_preprocessing_phase_action) self.load_preprocessing_phase_from_config_action = QAction("&Load preprocessing phase", self) self.load_preprocessing_phase_from_config_action.setStatusTip( "Load a preprocessing phase from a .json configuration file." ) self.load_preprocessing_phase_from_config_action.triggered.connect( self.load_preprocessing_phase_from_config_task ) self._all_actions.append(self.load_preprocessing_phase_from_config_action) self.save_preprocessing_phase_to_config_action = QAction("&Save preprocessing phase", self) self.save_preprocessing_phase_to_config_action.setStatusTip( "Save the preprocessing phase in a .json configuration file." ) self.save_preprocessing_phase_to_config_action.triggered.connect(self.save_preprocessing_phase_to_config_task) self.save_preprocessing_phase_to_config_action.setEnabled(False) self._all_actions.append(self.save_preprocessing_phase_to_config_action) self.run_preprocessing_phase_action = QAction("Run preprocessing phase", self) self.run_preprocessing_phase_action.setIcon(QIcon("aset_ui/resources/run.svg")) self.run_preprocessing_phase_action.setStatusTip("Run the preprocessing phase on the document collection.") self.run_preprocessing_phase_action.triggered.connect(self.run_preprocessing_phase_task) self.run_preprocessing_phase_action.setEnabled(False) self._all_actions.append(self.run_preprocessing_phase_action) self.load_and_run_default_matching_phase_action = QAction( "&Load and run default matching phase", self ) self.load_and_run_default_matching_phase_action.setStatusTip( "Load the default matching phase and run it on the document collection." ) self.load_and_run_default_matching_phase_action.setIcon(QIcon("aset_ui/resources/run_run.svg")) self.load_and_run_default_matching_phase_action.setDisabled(True) self.load_and_run_default_matching_phase_action.triggered.connect( self.load_and_run_default_preprocessing_phase_task ) self._all_actions.append(self.load_and_run_default_matching_phase_action) self.load_matching_phase_from_config_action = QAction("&Load matching phase", self) self.load_matching_phase_from_config_action.setStatusTip( "Load a matching phase from a .json configuration file." ) self.load_matching_phase_from_config_action.triggered.connect(self.load_matching_phase_from_config_task) self._all_actions.append(self.load_matching_phase_from_config_action) self.save_matching_phase_to_config_action = QAction("&Save matching phase", self) self.save_matching_phase_to_config_action.setStatusTip("Save the matching phase in a .json configuration file.") self.save_matching_phase_to_config_action.triggered.connect(self.save_matching_phase_to_config_task) self.save_matching_phase_to_config_action.setEnabled(False) self._all_actions.append(self.save_matching_phase_to_config_action) self.run_matching_phase_action = QAction("Run matching phase", self) self.run_matching_phase_action.setIcon(QIcon("aset_ui/resources/run.svg")) self.run_matching_phase_action.setStatusTip("Run the matching phase on the document collection.") self.run_matching_phase_action.triggered.connect(self.run_matching_phase_task) self.run_matching_phase_action.setEnabled(False) self._all_actions.append(self.run_matching_phase_action) self.enable_collect_statistics_action = QAction("&Enable statistics", self) self.enable_collect_statistics_action.setIcon(QIcon("aset_ui/resources/statistics.svg")) self.enable_collect_statistics_action.setStatusTip("Enable collecting statistics.") self.enable_collect_statistics_action.triggered.connect(self.enable_collect_statistics_task) self.enable_collect_statistics_action.setEnabled(False) self._all_actions.append(self.enable_collect_statistics_action) self.disable_collect_statistics_action = QAction("&Disable statistics", self) self.disable_collect_statistics_action.setIcon(QIcon("aset_ui/resources/statistics_incorrect.svg")) self.disable_collect_statistics_action.setStatusTip("Disable collecting statistics.") self.disable_collect_statistics_action.triggered.connect(self.disable_collect_statistics_task) self._all_actions.append(self.disable_collect_statistics_action) self.save_statistics_to_json_action = QAction("&Save statistics", self) self.save_statistics_to_json_action.setIcon(QIcon("aset_ui/resources/statistics_save.svg")) self.save_statistics_to_json_action.setStatusTip("Save the statistics to a .json file.") self.save_statistics_to_json_action.triggered.connect(self.save_statistics_to_json_task) self.save_statistics_to_json_action.setEnabled(False) self._all_actions.append(self.save_statistics_to_json_action) # set up the menu bar self.menubar = self.menuBar() self.menubar.setFont(MENU_FONT) self.file_menu = self.menubar.addMenu("&File") self.file_menu.setFont(MENU_FONT) self.file_menu.addAction(self.exit_action) self.document_base_menu = self.menubar.addMenu("&Document Base") self.document_base_menu.setFont(MENU_FONT) self.document_base_menu.addAction(self.show_document_base_creator_widget_action) self.document_base_menu.addSeparator() self.document_base_menu.addAction(self.load_document_base_from_bson_action) self.document_base_menu.addAction(self.save_document_base_to_bson_action) self.document_base_menu.addSeparator() self.document_base_menu.addAction(self.save_table_to_csv_action) self.document_base_menu.addSeparator() self.document_base_menu.addAction(self.add_attribute_action) self.document_base_menu.addAction(self.remove_attribute_action) self.document_base_menu.addSeparator() self.document_base_menu.addAction(self.forget_matches_for_attribute_action) self.document_base_menu.addAction(self.forget_matches_action) self.preprocessing_menu = self.menubar.addMenu("&Preprocessing") self.preprocessing_menu.setFont(MENU_FONT) self.preprocessing_menu.addAction(self.load_and_run_default_preprocessing_phase_action) self.preprocessing_menu.addSeparator() self.preprocessing_menu.addAction(self.load_preprocessing_phase_from_config_action) self.preprocessing_menu.addAction(self.save_preprocessing_phase_to_config_action) self.preprocessing_menu.addSeparator() self.preprocessing_menu.addAction(self.run_preprocessing_phase_action) self.matching_menu = self.menubar.addMenu("&Matching") self.matching_menu.setFont(MENU_FONT) self.matching_menu.addAction(self.load_and_run_default_matching_phase_action) self.matching_menu.addSeparator() self.matching_menu.addAction(self.load_matching_phase_from_config_action) self.matching_menu.addAction(self.save_matching_phase_to_config_action) self.matching_menu.addSeparator() self.matching_menu.addAction(self.run_matching_phase_action) self.statistics_menu = self.menubar.addMenu("&Statistics") self.statistics_menu.setFont(MENU_FONT) self.statistics_menu.addAction(self.enable_collect_statistics_action) self.statistics_menu.addAction(self.disable_collect_statistics_action) self.statistics_menu.addSeparator() self.statistics_menu.addAction(self.save_statistics_to_json_action) # start menu self.start_menu_widget = QWidget() self.start_menu_layout = QVBoxLayout(self.start_menu_widget) self.start_menu_layout.setContentsMargins(0, 0, 0, 0) self.start_menu_layout.setSpacing(30) self.start_menu_layout.setAlignment(Qt.AlignmentFlag.AlignTop) self.start_menu_widget.setMaximumWidth(400) self.start_menu_header = QLabel("Welcome to ASET!") self.start_menu_header.setFont(HEADER_FONT) self.start_menu_layout.addWidget(self.start_menu_header) self.start_menu_create_new_document_base_widget = QWidget() self.start_menu_create_new_document_base_layout = QVBoxLayout(self.start_menu_create_new_document_base_widget) self.start_menu_create_new_document_base_layout.setContentsMargins(0, 0, 0, 0) self.start_menu_create_new_document_base_layout.setSpacing(10) self.start_menu_layout.addWidget(self.start_menu_create_new_document_base_widget) self.start_menu_create_new_document_base_subheader = QLabel("Create a new document base.") self.start_menu_create_new_document_base_subheader.setFont(SUBHEADER_FONT) self.start_menu_create_new_document_base_layout.addWidget(self.start_menu_create_new_document_base_subheader) self.start_menu_create_new_document_base_wrapper_widget = QWidget() self.start_menu_create_new_document_base_wrapper_layout = QHBoxLayout( self.start_menu_create_new_document_base_wrapper_widget) self.start_menu_create_new_document_base_wrapper_layout.setContentsMargins(0, 0, 0, 0) self.start_menu_create_new_document_base_wrapper_layout.setSpacing(20) self.start_menu_create_new_document_base_wrapper_layout.setAlignment(Qt.AlignmentFlag.AlignLeft) self.start_menu_create_new_document_base_layout.addWidget( self.start_menu_create_new_document_base_wrapper_widget) self.start_menu_create_document_base_button = QPushButton() self.start_menu_create_document_base_button.setFixedHeight(45) self.start_menu_create_document_base_button.setFixedWidth(45) self.start_menu_create_document_base_button.setIcon(QIcon("aset_ui/resources/two_documents.svg")) self.start_menu_create_document_base_button.clicked.connect(self.show_document_base_creator_widget_task) self.start_menu_create_new_document_base_wrapper_layout.addWidget(self.start_menu_create_document_base_button) self.start_menu_create_document_base_label = QLabel( "Create a new document base from a directory\nof .txt files and a list of attribute names.") self.start_menu_create_document_base_label.setFont(LABEL_FONT) self.start_menu_create_new_document_base_wrapper_layout.addWidget(self.start_menu_create_document_base_label) self.start_menu_load_document_base_widget = QWidget() self.start_menu_load_document_base_layout = QVBoxLayout(self.start_menu_load_document_base_widget) self.start_menu_load_document_base_layout.setContentsMargins(0, 0, 0, 0) self.start_menu_load_document_base_layout.setSpacing(10) self.start_menu_layout.addWidget(self.start_menu_load_document_base_widget) self.start_menu_load_document_base_subheader = QLabel("Load an existing document base.") self.start_menu_load_document_base_subheader.setFont(SUBHEADER_FONT) self.start_menu_load_document_base_layout.addWidget(self.start_menu_load_document_base_subheader) self.start_menu_load_document_base_wrapper_widget = QWidget() self.start_menu_load_document_base_wrapper_layout = QHBoxLayout( self.start_menu_load_document_base_wrapper_widget) self.start_menu_load_document_base_wrapper_layout.setContentsMargins(0, 0, 0, 0) self.start_menu_load_document_base_wrapper_layout.setSpacing(20) self.start_menu_load_document_base_wrapper_layout.setAlignment(Qt.AlignmentFlag.AlignLeft) self.start_menu_load_document_base_layout.addWidget(self.start_menu_load_document_base_wrapper_widget) self.start_menu_load_document_base_button = QPushButton() self.start_menu_load_document_base_button.setFixedHeight(45) self.start_menu_load_document_base_button.setFixedWidth(45) self.start_menu_load_document_base_button.setIcon(QIcon("aset_ui/resources/folder.svg")) self.start_menu_load_document_base_button.clicked.connect(self.load_document_base_from_bson_task) self.start_menu_load_document_base_wrapper_layout.addWidget(self.start_menu_load_document_base_button) self.start_menu_load_document_base_label = QLabel("Load an existing document base\nfrom a .bson file.") self.start_menu_load_document_base_label.setFont(LABEL_FONT) self.start_menu_load_document_base_wrapper_layout.addWidget(self.start_menu_load_document_base_label) # main UI self.central_widget = QWidget(self) self.central_widget_layout = QHBoxLayout(self.central_widget) self.central_widget_layout.setAlignment(Qt.AlignmentFlag.AlignCenter) self.setCentralWidget(self.central_widget) self.document_base_creator_widget = DocumentBaseCreatorWidget(self) self.document_base_viewer_widget = DocumentBaseViewerWidget(self) self.interactive_matching_widget = InteractiveMatchingWidget(self) self.document_base_creator_widget.hide() self.document_base_viewer_widget.hide() self.interactive_matching_widget.hide() self.central_widget_layout.addWidget(self.start_menu_widget) self.central_widget_layout.update() self.resize(1400, 800) self.show() logger.info("Initialized MainWindow.")
class GetSharedInfo(QThread): '''提取界面获取分享链接信息''' infos = pyqtSignal(object) msg = pyqtSignal(str, int) update = pyqtSignal() clean = pyqtSignal() def __init__(self, parent=None): super(GetSharedInfo, self).__init__(parent) self._disk = None self.share_url = "" self.pwd = "" self.is_file = "" self.is_folder = "" self._mutex = QMutex() self._is_work = False self._pat = r"(https?://(\w[-\w]*\.)?lanzou[a-z].com/[a-z]?[-/a-zA-Z0-9]+)[^a-zA-Z0-9]*([a-zA-Z0-9]+\w+)?" def set_disk(self, disk): self._disk = disk def set_values(self, text, pwd_input=""): '''获取分享链接信息''' text = text.strip() pwd_input = pwd_input.strip() if not text: self.update.emit() return None for share_url, _, pwd in re.findall(self._pat, text): if is_file_url(share_url): # 文件链接 is_file = True is_folder = False self.msg.emit("正在获取文件链接信息……", 20000) elif is_folder_url(share_url): # 文件夹链接 is_folder = True is_file = False self.msg.emit("正在获取文件夹链接信息,可能需要几秒钟,请稍候……", 500000) else: self.msg.emit(f"{share_url} 为非法链接!", 0) self.update.emit() return None self.clean.emit() # 清理旧的显示信息 self.share_url = share_url if pwd_input: self.pwd = pwd_input elif pwd: self.pwd = pwd else: # 一个或两个汉字的提取码 pwd_ = text.split(' ')[-1].split(':')[-1].split(':')[-1] self.pwd = pwd_ if 1<= len(pwd_) <= 2 else '' self.is_file = is_file self.is_folder = is_folder self.start() break def __del__(self): self.wait() def stop(self): # 用于手动停止 self._mutex.lock() self._is_work = False self._mutex.unlock() def emit_msg(self, infos): '''根据查询信息发送状态信号''' show_time = 2999 # 提示显示时间,单位 ms if infos.code == LanZouCloud.FILE_CANCELLED: self.msg.emit("<font color='red'>文件不存在,或已删除!</font>", show_time) elif infos.code == LanZouCloud.URL_INVALID: self.msg.emit("<font color='red'>链接非法!</font>", show_time) elif infos.code == LanZouCloud.PASSWORD_ERROR: self.msg.emit(f"<font color='red'>提取码 [<b><font color='magenta'>{self.pwd}</font></b>] 错误!</font>", show_time) elif infos.code == LanZouCloud.LACK_PASSWORD: self.msg.emit("<font color='red'>请在链接后面跟上提取码,空格分割!</font>", show_time) elif infos.code == LanZouCloud.NETWORK_ERROR: self.msg.emit("<font color='red'>网络错误!</font>", show_time) elif infos.code == LanZouCloud.SUCCESS: self.msg.emit("<font color='#00CC00'>提取成功!</font>", show_time) else: self.msg.emit(f"<font color='red'>未知错误 code={infos.code}!</font>", show_time * 4) def run(self): if not self._is_work: self._mutex.lock() self._is_work = True try: if self.is_file: # 链接为文件 _infos = self._disk.get_share_info_by_url(self.share_url, self.pwd) self.emit_msg(_infos) self.infos.emit(_infos) elif self.is_folder: # 链接为文件夹 _infos = self._disk.get_folder_info_by_url(self.share_url, self.pwd) self.emit_msg(_infos) self.infos.emit(_infos) else: logger.error(f"GetShareInfo error: Not a file or folder!") except TimeoutError: self.msg.emit("font color='red'>网络超时!请稍后重试</font>", 5000) except Exception as e: self.msg.emit(f"font color='red'>未知错误:{e}</font>", 5000) logger.error(f"GetShareInfo error: e={e}") self._is_work = False self.update.emit() self._mutex.unlock() else: self.msg.emit("<font color='blue'>后台正在运行,稍后重试!</font>", 4000)
def __init__(self, parent=None): super(LogoutWorker, self).__init__(parent) self._disk = None self.update_ui = True self._mutex = QMutex() self._is_work = False
class SetPwdWorker(QThread): '''设置文件(夹)提取码 线程''' msg = pyqtSignal(str, int) update = pyqtSignal(object, object, object, object) def __init__(self, parent=None): super(SetPwdWorker, self).__init__(parent) self._disk = None self.infos = [] self._work_id = -1 self._mutex = QMutex() self._is_work = False def set_disk(self, disk): self._disk = disk def set_values(self, infos, work_id): self.infos = infos self._work_id = work_id self.start() def __del__(self): self.wait() def stop(self): self._mutex.lock() self._is_work = False self._mutex.unlock() def run(self): if not self._is_work: self._mutex.lock() self._is_work = True try: has_file = False has_folder = False failed = False failed_code = "" for infos in self.infos: if infos.is_file: # 文件 has_file = True new_pwd = infos.new_pwd if 2 > len(new_pwd) >= 1 or len(new_pwd) > 6: self.msg.emit("文件提取码为2-6位字符,关闭请留空!", 4000) raise UserWarning else: # 文件夹 has_folder = True new_pwd = infos.new_pwd if 2 > len(new_pwd) >= 1 or len(new_pwd) > 12: self.msg.emit("文件夹提取码为0-12位字符,关闭请留空!", 4000) raise UserWarning res = self._disk.set_passwd(infos.id, infos.new_pwd, infos.is_file) if res != LanZouCloud.SUCCESS: failed_code = failed_code + str(res) failed = True if failed: self.msg.emit(f"❌部分提取码变更失败:{failed_code},请勿使用特殊符号!", 4000) else: self.msg.emit("✅提取码变更成功!♬", 3000) self.update.emit(self._work_id, has_file, has_folder, False) except TimeoutError: self.msg.emit("网络超时,请稍后重试!", 6000) except UserWarning: pass except Exception as e: logger.error(f"SetPwdWorker error: e={e}") self._is_work = False self._mutex.unlock() else: self.msg.emit("后台正在运行,请稍后重试!", 3100)