76 |
78 | ```
79 |
--------------------------------------------------------------------------------
/lanzou/gui/workers/__init__.py:
--------------------------------------------------------------------------------
1 | from lanzou.gui.workers.manager import TaskManager
2 | from lanzou.gui.workers.desc import DescPwdFetcher
3 | from lanzou.gui.workers.folders import GetAllFoldersWorker
4 | from lanzou.gui.workers.login import LoginLuncher, LogoutWorker
5 | from lanzou.gui.workers.more import GetMoreInfoWorker
6 | from lanzou.gui.workers.pwd import SetPwdWorker
7 | from lanzou.gui.workers.recovery import GetRecListsWorker, RecManipulator
8 | from lanzou.gui.workers.refresh import ListRefresher
9 | from lanzou.gui.workers.rename import RenameMkdirWorker
10 | from lanzou.gui.workers.rm import RemoveFilesWorker
11 | from lanzou.gui.workers.share import GetSharedInfo
12 | from lanzou.gui.workers.update import CheckUpdateWorker
13 |
14 |
15 | __all__ = ['TaskManager', 'GetSharedInfo', 'LoginLuncher', 'DescPwdFetcher',
16 | 'ListRefresher', 'GetRecListsWorker', 'RemoveFilesWorker',
17 | 'GetMoreInfoWorker', 'GetAllFoldersWorker', 'RenameMkdirWorker',
18 | 'SetPwdWorker', 'LogoutWorker', 'RecManipulator', 'CheckUpdateWorker']
19 |
--------------------------------------------------------------------------------
/lanzou/gui/workers/desc.py:
--------------------------------------------------------------------------------
1 | from PyQt6.QtCore import QThread, pyqtSignal, QMutex
2 | from lanzou.api import LanZouCloud
3 |
4 | from lanzou.gui.models import DlJob
5 | from lanzou.debug import logger
6 |
7 |
8 | class DescPwdFetcher(QThread):
9 | '''获取描述与提取码 线程'''
10 | desc = pyqtSignal(object)
11 | tasks = pyqtSignal(object)
12 | msg = pyqtSignal(object, object)
13 |
14 | def __init__(self, parent=None):
15 | super(DescPwdFetcher, self).__init__(parent)
16 | self._disk = None
17 | self.infos = None
18 | self.download = False
19 | self.dl_path = ""
20 | self._mutex = QMutex()
21 | self._is_work = False
22 |
23 | def set_disk(self, disk):
24 | self._disk = disk
25 |
26 | def set_values(self, infos, download=False, dl_path=""):
27 | self.infos = infos # 列表的列表
28 | self.download = download # 标识激发下载器
29 | self.dl_path = dl_path
30 | self.start()
31 |
32 | def __del__(self):
33 | self.wait()
34 |
35 | def stop(self):
36 | self._mutex.lock()
37 | self._is_work = False
38 | self._mutex.unlock()
39 |
40 | def run(self):
41 | if not self._is_work:
42 | self._mutex.lock()
43 | self._is_work = True
44 | try:
45 | if not self.infos:
46 | raise UserWarning
47 | _tasks = {}
48 | _infos = []
49 | for info in self.infos:
50 | if info.id: # disk 运行
51 | if info.is_file: # 文件
52 | res = self._disk.get_share_info(info.id, is_file=True)
53 | else: # 文件夹
54 | res = self._disk.get_share_info(info.id, is_file=False)
55 | if res.code == LanZouCloud.SUCCESS:
56 | info.pwd = res.pwd
57 | info.url = res.url
58 | info.desc = res.desc
59 | elif res.code == LanZouCloud.NETWORK_ERROR:
60 | self.msg.emit("网络错误,请稍后重试!", 6000)
61 | continue
62 | _infos.append(info) # info -> lanzou.gui.models.FileInfos
63 | _tasks[info.url] = DlJob(infos=info, path=self.dl_path, total_file=1)
64 | if self.download:
65 | self.tasks.emit(_tasks)
66 | else: # 激发简介更新
67 | self.desc.emit(_infos)
68 | except TimeoutError:
69 | self.msg.emit("网络超时,请稍后重试!", 6000)
70 | except UserWarning:
71 | pass
72 | except Exception as e:
73 | logger.error(f"GetPwdFetcher error: e={e}")
74 | self._is_work = False
75 | self._mutex.unlock()
76 | else:
77 | self.msg.emit("后台正在运行指令!请稍后重试", 3100)
78 |
--------------------------------------------------------------------------------
/lanzou/gui/workers/down.py:
--------------------------------------------------------------------------------
1 | from PyQt6.QtCore import QThread, pyqtSignal, QMutex
2 |
3 | from lanzou.api.utils import is_folder_url, is_file_url
4 | from lanzou.api import why_error
5 | from lanzou.debug import logger
6 |
7 |
8 | class Downloader(QThread):
9 | '''单个文件下载线程'''
10 | finished_ = pyqtSignal(object)
11 | folder_file_failed = pyqtSignal(object, object)
12 | failed = pyqtSignal()
13 | proc = pyqtSignal(str)
14 | update = pyqtSignal()
15 |
16 | def __init__(self, disk, task, callback, parent=None):
17 | super(Downloader, self).__init__(parent)
18 | self._disk = disk
19 | self._task = task
20 | self._callback_thread = callback(task)
21 |
22 | def stop(self):
23 | self.terminate()
24 |
25 | def _callback(self):
26 | """显示进度条的回调函数"""
27 | if not self._callback_thread.isRunning():
28 | self.update.emit()
29 | self.proc.emit(self._callback_thread.emit_msg)
30 | self._callback_thread.start()
31 |
32 | def _down_failed(self, code, file):
33 | """显示下载失败的回调函数"""
34 | self.folder_file_failed.emit(code, file)
35 |
36 | def __del__(self):
37 | self.wait()
38 |
39 | def run(self):
40 | if not self._task:
41 | logger.error("Download task is empty!")
42 | return None
43 | self._task.run = True
44 | try:
45 | if is_file_url(self._task.url): # 下载文件
46 | res = self._disk.down_file_by_url(self._task.url, self._task, self._callback)
47 | elif is_folder_url(self._task.url): # 下载文件夹
48 | res = self._disk.down_dir_by_url(self._task, self._callback)
49 | else:
50 | raise UserWarning
51 | if res == 0:
52 | self._task.rate = 1000 # 回调线程可能在休眠
53 | self.update.emit()
54 | else:
55 | self._task.info = why_error(res)
56 | logger.debug(f"Download : res={res}")
57 | self.failed.emit()
58 | except TimeoutError:
59 | self._task.info = "网络连接错误!"
60 | logger.error("Download TimeOut")
61 | self.failed.emit()
62 | except Exception as err:
63 | self._task.info = f"未知错误!err={err}"
64 | logger.error(f"Download error: err={err}")
65 | self.failed.emit()
66 | except UserWarning: pass
67 | self._task.run = False
68 | self.finished_.emit(self._task)
69 |
--------------------------------------------------------------------------------
/lanzou/gui/workers/folders.py:
--------------------------------------------------------------------------------
1 | from time import sleep
2 | from PyQt6.QtCore import QThread, pyqtSignal, QMutex
3 | from lanzou.api import LanZouCloud
4 | from lanzou.debug import logger
5 |
6 |
7 | class GetAllFoldersWorker(QThread):
8 | '''获取所有文件夹name与fid,用于文件移动'''
9 | infos = pyqtSignal(object, object)
10 | msg = pyqtSignal(str, int)
11 | moved = pyqtSignal(bool, bool, bool)
12 |
13 | def __init__(self, parent=None):
14 | super(GetAllFoldersWorker, self).__init__(parent)
15 | self._disk = None
16 | self.org_infos = None
17 | self._mutex = QMutex()
18 | self._is_work = False
19 | self.move_infos = None
20 |
21 | def set_disk(self, disk):
22 | self._disk = disk
23 |
24 | def set_values(self, org_infos):
25 | self.org_infos = org_infos # 对话框标识文件与文件夹
26 | self.move_infos = [] # 清除上次影响
27 | self.start()
28 |
29 | def move_file(self, infos):
30 | '''移动文件至新的文件夹'''
31 | self.move_infos = infos # file_id, folder_id, f_name, type(size)
32 | self.start()
33 |
34 | def __del__(self):
35 | self.wait()
36 |
37 | def stop(self):
38 | self._mutex.lock()
39 | self._is_work = False
40 | self._mutex.unlock()
41 |
42 | def move_file_folder(self, info, no_err:bool, r_files:bool, r_folders:bool):
43 | """移动文件(夹)"""
44 | # no_err 判断是否需要更新 UI
45 | if info.is_file: # 文件
46 | if self._disk.move_file(info.id, info.new_id) == LanZouCloud.SUCCESS:
47 | self.msg.emit(f"{info.name} 移动成功!", 3000)
48 | no_err = True
49 | r_files = True
50 | else:
51 | self.msg.emit(f"移动文件{info.name}失败!", 4000)
52 | else: # 文件夹
53 | if self._disk.move_folder(info.id, info.new_id) == LanZouCloud.SUCCESS:
54 | self.msg.emit(f"{info.name} 移动成功!", 3000)
55 | no_err = True
56 | r_folders = True
57 | else:
58 | self.msg.emit(f"移动文件夹 {info.name} 失败!移动的文件夹中不能包含子文件夹!", 4000)
59 | return no_err, r_files, r_folders
60 |
61 | def run(self):
62 | if not self._is_work:
63 | self._mutex.lock()
64 | self._is_work = True
65 | if self.move_infos: # 移动文件
66 | no_err = False
67 | r_files = False
68 | r_folders = False
69 | for info in self.move_infos:
70 | try:
71 | no_err, r_files, r_folders = self.move_file_folder(info, no_err, r_files, r_folders)
72 | except TimeoutError:
73 | self.msg.emit(f"移动文件(夹) {info.name} 失败,网络超时!请稍后重试", 5000)
74 | except Exception as e:
75 | logger.error(f"GetAllFoldersWorker error: e={e}")
76 | self.msg.emit(f"移动文件(夹) {info.name} 失败,未知错误!", 5000)
77 | if no_err: # 没有错误就更新ui
78 | sleep(2.1) # 等一段时间后才更新文件列表
79 | self.moved.emit(r_files, r_folders, False)
80 | else: # 获取所有文件夹
81 | try:
82 | self.msg.emit("网络请求中,请稍候……", 0)
83 | all_dirs_dict = self._disk.get_move_folders().name_id
84 | self.infos.emit(self.org_infos, all_dirs_dict)
85 | self.msg.emit("", 0) # 删除提示信息
86 | except TimeoutError:
87 | self.msg.emit("网络超时!稍后重试", 6000)
88 | except Exception as e:
89 | logger.error(f"GetAllFoldersWorker error: e={e}")
90 | self._is_work = False
91 | self._mutex.unlock()
92 | else:
93 | self.msg.emit("后台正在运行,请稍后重试!", 3100)
94 |
--------------------------------------------------------------------------------
/lanzou/gui/workers/login.py:
--------------------------------------------------------------------------------
1 | from PyQt6.QtCore import QThread, pyqtSignal, QMutex
2 | from lanzou.api import LanZouCloud
3 | from lanzou.debug import logger
4 |
5 |
6 | class LoginLuncher(QThread):
7 | '''登录线程'''
8 | code = pyqtSignal(bool, str, int)
9 | update_cookie = pyqtSignal(object)
10 | update_username = pyqtSignal(object)
11 |
12 | def __init__(self, parent=None):
13 | super(LoginLuncher, self).__init__(parent)
14 | self._disk = None
15 | self.username = ""
16 | self.password = ""
17 | self.cookie = None
18 |
19 | def set_disk(self, disk):
20 | self._disk = disk
21 |
22 | def set_values(self, username, password, cookie=None):
23 | self.username = username
24 | self.password = password
25 | self.cookie = cookie
26 | self.start()
27 |
28 | def run(self):
29 | try:
30 | if self.cookie:
31 | res = self._disk.login_by_cookie(self.cookie)
32 | if res == LanZouCloud.SUCCESS:
33 | if not self.username:
34 | username = self._disk.get_user_name()
35 | if isinstance(username, str):
36 | self.update_username.emit(username)
37 | logger.debug(f"login by Cookie: username={username}")
38 | self.code.emit(True, "
通过Cookie登录成功! ≧◉◡◉≦", 5000)
39 | return None
40 | logger.debug(f"login by Cookie err: res={res}")
41 | if (not self.username or not self.password) and not self.cookie:
42 | logger.debug("login err: No UserName、No cookie")
43 | self.code.emit(False, "
登录失败: 没有用户或密码", 3000)
44 | else:
45 | res = self._disk.login(self.username, self.password)
46 | if res == LanZouCloud.SUCCESS:
47 | self.code.emit(True, "
登录成功! ≧◉◡◉≦", 5000)
48 | _cookie = self._disk.get_cookie()
49 | self.update_cookie.emit(_cookie)
50 | else:
51 | logger.debug(f"login err: res={res}")
52 | self.code.emit(False, "
登录失败,可能是用户名或密码错误!", 8000)
53 | self.update_cookie.emit(None)
54 | except TimeoutError:
55 | self.code.emit(False, "
网络超时!", 3000)
56 | except Exception as e:
57 | logger.error(f"LoginLuncher error: e={e}")
58 |
59 |
60 | class LogoutWorker(QThread):
61 | '''登出'''
62 | succeeded = pyqtSignal()
63 | msg = pyqtSignal(str, int)
64 |
65 | def __init__(self, parent=None):
66 | super(LogoutWorker, self).__init__(parent)
67 | self._disk = None
68 | self.update_ui = True
69 | self._mutex = QMutex()
70 | self._is_work = False
71 |
72 | def set_disk(self, disk):
73 | self._disk = disk
74 |
75 | def set_values(self, update_ui=True):
76 | self.update_ui = update_ui
77 | self.start()
78 |
79 | def __del__(self):
80 | self.wait()
81 |
82 | def stop(self):
83 | self._mutex.lock()
84 | self._is_work = False
85 | self._mutex.unlock()
86 |
87 | def run(self):
88 | if not self._is_work:
89 | self._mutex.lock()
90 | self._is_work = True
91 | try:
92 | res = self._disk.logout()
93 | if res == LanZouCloud.SUCCESS:
94 | if self.update_ui:
95 | self.succeeded.emit()
96 | self.msg.emit("已经退出登录!", 4000)
97 | else:
98 | self.msg.emit("失败,请重试!", 5000)
99 | except TimeoutError:
100 | self.msg.emit("网络超时,请稍后重试!", 6000)
101 | except Exception as e:
102 | logger.error(f"LogoutWorker error: e={e}")
103 | self._is_work = False
104 | self._mutex.unlock()
105 | else:
106 | self.msg.emit("后台正在运行,请稍后重试!", 3100)
107 |
--------------------------------------------------------------------------------
/lanzou/gui/workers/manager.py:
--------------------------------------------------------------------------------
1 | from time import sleep, time
2 | from random import uniform
3 | from PyQt6.QtCore import QThread, pyqtSignal, QMutex
4 |
5 | from lanzou.gui.workers.down import Downloader
6 | from lanzou.gui.workers.upload import Uploader
7 | from lanzou.debug import logger
8 |
9 |
10 | def change_size_unit(total):
11 | if total < 1 << 10:
12 | return "{:.2f} B".format(total)
13 | elif total < 1 << 20:
14 | return "{:.2f} KB".format(total / (1 << 10))
15 | elif total < 1 << 30:
16 | return "{:.2f} MB".format(total / (1 << 20))
17 | else:
18 | return "{:.2f} GB".format(total / (1 << 30))
19 |
20 |
21 | def show_progress(file_name, total_size, now_size, speed=0, symbol="█"):
22 | """显示进度条的回调函数"""
23 | percent = now_size / total_size
24 | # 进度条长总度
25 | file_len = len(file_name)
26 | if file_len >= 20:
27 | bar_len = 20
28 | elif file_len >= 10:
29 | bar_len = 30
30 | else:
31 | bar_len = 40
32 | if total_size >= 1048576:
33 | unit = "MB"
34 | piece = 1048576
35 | else:
36 | unit = "KB"
37 | piece = 1024
38 | bar_str = ("
" + symbol * round(bar_len * percent)
39 | + "" + symbol * round(bar_len * (1 - percent)) + "")
40 | msg = "\r{:>5.1f}%\t[{}] {:.1f}/{:.1f}{} | {} | {} ".format(
41 | percent * 100,
42 | bar_str,
43 | now_size / piece,
44 | total_size / piece,
45 | unit,
46 | speed,
47 | file_name,
48 | )
49 | if total_size == now_size:
50 | msg = msg + "|
Done!"
51 | return msg
52 |
53 |
54 | class Callback(QThread):
55 | '''回调显示进度'''
56 | def __init__(self, task, parent=None):
57 | super(Callback, self).__init__(parent)
58 | self._task = task
59 | self._mutex = QMutex()
60 | self._stopped = True
61 | self._emit_msg = ''
62 |
63 | @property
64 | def emit_msg(self):
65 | return self._emit_msg
66 |
67 | def run(self):
68 | if self._stopped:
69 | self._mutex.lock()
70 | self._stopped = False
71 | old_size = self._task.now_size
72 | old_rate = int(1000 * old_size / self._task.total_size)
73 | old_time = time()
74 | sleep(uniform(0.5, 2))
75 | now_size = self._task.now_size
76 | now_rate = int(1000 * now_size / self._task.total_size)
77 | now_time = time()
78 | if old_size != now_size and old_rate != now_rate:
79 | speed = change_size_unit((now_size - old_size) / (now_time - old_time)) + '/s'
80 | self._task.speed = speed
81 | self._task.rate = now_rate
82 | self._emit_msg = show_progress(self._task.name, self._task.total_size, self._task.now_size, speed)
83 | self._stopped = True
84 | self._mutex.unlock()
85 |
86 |
87 | class TaskManager(QThread):
88 | """任务控制器线程,控制后台上传下载"""
89 | mgr_msg = pyqtSignal(str, int)
90 | mgr_finished = pyqtSignal(int)
91 | update = pyqtSignal()
92 |
93 | def __init__(self, thread=3, parent=None):
94 | super(TaskManager, self).__init__(parent)
95 | self._disk = None
96 | self._tasks = {}
97 | self._queues = []
98 | self._thread = thread
99 | self._count = 0
100 | self._mutex = QMutex()
101 | self._is_work = False
102 | self._old_msg = ""
103 | self._workers = {}
104 | self._allow_big_file = False
105 |
106 | def set_allow_big_file(self, allow_big_file):
107 | self._allow_big_file = allow_big_file
108 |
109 | def set_disk(self, disk):
110 | self._disk = disk
111 |
112 | def set_thread(self, thread):
113 | self._thread = thread
114 |
115 | def stop_task(self, task):
116 | """暂停任务"""
117 | if task.url in self._workers and self._workers[task.url].isRunning():
118 | logger.debug(f"Stop job: {task.url}")
119 | try:
120 | self._tasks[task.url].pause = True
121 | self._workers[task.url].stop()
122 | self._tasks[task.url].run = False
123 | except Exception as err:
124 | logger.error(f"Stop task: err={err}")
125 | else:
126 | logger.debug(f"Stop job: {task.url} is not running!")
127 | self.update.emit()
128 |
129 | def start_task(self, task):
130 | """开始已暂停任务"""
131 | if task.url not in self._workers:
132 | self.add_task(task)
133 | elif not self._workers[task.url].isRunning():
134 | logger.debug(f"Start job: {task}")
135 | self._workers[task.url].start()
136 | self._tasks[task.url].run = True
137 | self._tasks[task.url].pause = False
138 | self.update.emit()
139 | self.start()
140 |
141 | def add_task(self, task):
142 | logger.debug(f"TaskMgr add one: added={task.added}, pause={task.pause}")
143 | if task.url not in self._tasks.keys():
144 | self._tasks[task.url] = task
145 | task.added = False
146 | task.pause = False
147 | task.info = None
148 | self.start()
149 |
150 | def add_tasks(self, tasks: dict):
151 | logger.debug(f"TaskMgr add: tasks={tasks}")
152 | self._tasks.update(tasks)
153 | self.start()
154 |
155 | def del_task(self, task):
156 | logger.debug(f"TaskMgr del: url={task.url}")
157 | if task in self._queues:
158 | self._queues.remove(task)
159 | if task.url in self._tasks:
160 | del self._tasks[task.url]
161 | if task.url in self._workers:
162 | del self._workers[task.url]
163 |
164 | def _task_to_queue(self):
165 | for task in self._tasks.values():
166 | if not task.added and not task.pause and task not in self._queues:
167 | logger.debug(f"TaskMgr task2queue: url={task.url}")
168 | self._queues.append(task)
169 | task.added = True
170 |
171 | def __del__(self):
172 | self.wait()
173 |
174 | def _ahead_msg(self, msg):
175 | if self._old_msg != msg:
176 | if self._count == 1:
177 | self.mgr_msg.emit(msg, 0)
178 | else:
179 | self.mgr_msg.emit(f"有{self._count}个后台任务正在运行", 0)
180 | self._old_msg = msg
181 |
182 | def _ahead_error(self):
183 | self.update.emit()
184 |
185 | def _ahead_folder_error(self, code, file):
186 | # 需要单独考虑,不在 task中
187 | pass
188 |
189 | def _update_emit(self):
190 | self.update.emit()
191 |
192 | def _add_thread(self, task):
193 | self.update.emit()
194 | logger.debug(f"TaskMgr count: count={self._count}")
195 | self._count -= 1
196 | del self._workers[task.url]
197 | # 发送所有任务完成信号
198 | failed_task_num = 0
199 | for task in self._tasks.values():
200 | if not task.info:
201 | if task.rate < 1000:
202 | return None
203 | else:
204 | failed_task_num += 1
205 | logger.debug(f"TaskMgr all finished!: failed_task_num={failed_task_num}")
206 | self.mgr_finished.emit(failed_task_num)
207 |
208 | def stop(self):
209 | self._mutex.lock()
210 | self._is_work = False
211 | self._mutex.unlock()
212 |
213 | def run(self):
214 | if not self._is_work:
215 | self._mutex.lock()
216 | self._is_work = True
217 | while True:
218 | self._task_to_queue()
219 | if not self._queues:
220 | break
221 | while self._count >= self._thread:
222 | self.sleep(1)
223 | self._count += 1
224 | task = self._queues.pop()
225 | logger.debug(f"TaskMgr run: url={task.url}")
226 | if task.type == 'dl':
227 | self._workers[task.url] = Downloader(self._disk, task, Callback)
228 | self.mgr_msg.emit(f"准备下载:
{task.name}", 0)
229 | else:
230 | self._workers[task.url] = Uploader(self._disk, task, Callback, self._allow_big_file)
231 | self.mgr_msg.emit(f"准备上传:
{task.name}", 0)
232 | try:
233 | self._workers[task.url].finished_.connect(self._add_thread)
234 | self._workers[task.url].proc.connect(self._ahead_msg)
235 | self._workers[task.url].update.connect(self._update_emit)
236 | self._workers[task.url].folder_file_failed.connect(self._ahead_folder_error)
237 | self._workers[task.url].failed.connect(self._ahead_error)
238 | self._workers[task.url].start()
239 | except Exception as err:
240 | logger.error(f"TaskMgr Error: err={err}")
241 | self._is_work = False
242 | self._mutex.unlock()
243 |
--------------------------------------------------------------------------------
/lanzou/gui/workers/more.py:
--------------------------------------------------------------------------------
1 | from PyQt6.QtCore import QThread, pyqtSignal, QMutex
2 | from lanzou.api import LanZouCloud
3 |
4 | from lanzou.gui.models import Infos
5 | from lanzou.debug import logger
6 |
7 |
8 | class GetMoreInfoWorker(QThread):
9 | '''获取文件直链、文件(夹)提取码描述,用于登录后显示更多信息'''
10 | infos = pyqtSignal(object)
11 | share_url = pyqtSignal(object)
12 | dl_link = pyqtSignal(object)
13 | msg = pyqtSignal(str, int)
14 |
15 | def __init__(self, parent=None):
16 | super(GetMoreInfoWorker, self).__init__(parent)
17 | self._disk = None
18 | self._infos = None
19 | self._url = ''
20 | self._pwd = ''
21 | self._emit_link = False
22 | self._mutex = QMutex()
23 | self._is_work = False
24 |
25 | def set_disk(self, disk):
26 | self._disk = disk
27 |
28 | def set_values(self, infos, emit_link=False):
29 | self._infos = infos
30 | self._emit_link = emit_link
31 | self.start()
32 |
33 | def get_dl_link(self, url, pwd):
34 | self._url = url
35 | self._pwd = pwd
36 | self.start()
37 |
38 | def __del__(self):
39 | self.wait()
40 |
41 | def stop(self):
42 | self._mutex.lock()
43 | self._is_work = False
44 | self._mutex.unlock()
45 |
46 | def run(self):
47 | # infos: ID/None,文件名,大小,日期,下载次数(dl_count),提取码(pwd),描述(desc),|链接(share-url)
48 | if not self._is_work and self._infos:
49 | self._mutex.lock()
50 | self._is_work = True
51 | try:
52 | if not self._url: # 获取普通信息
53 | if isinstance(self._infos, Infos):
54 | if self._infos.id: # 从 disk 运行
55 | self.msg.emit("网络请求中,请稍候……", 0)
56 | _info = self._disk.get_share_info(self._infos.id, is_file=self._infos.is_file)
57 | self._infos.desc = _info.desc
58 | self._infos.pwd = _info.pwd
59 | self._infos.url = _info.url
60 | if self._emit_link:
61 | self.share_url.emit(self._infos)
62 | else:
63 | self.infos.emit(self._infos)
64 | self.msg.emit("", 0) # 删除提示信息
65 | else: # 获取下载直链
66 | res = self._disk.get_file_info_by_url(self._url, self._pwd)
67 | if res.code == LanZouCloud.SUCCESS:
68 | self.dl_link.emit("{}".format(res.durl or "无")) # 下载直链
69 | elif res.code == LanZouCloud.NETWORK_ERROR:
70 | self.dl_link.emit("网络错误!获取失败") # 下载直链
71 | else:
72 | self.dl_link.emit("其它错误!") # 下载直链
73 | except TimeoutError:
74 | self.msg.emit("网络超时!稍后重试", 6000)
75 | except Exception as e:
76 | logger.error(f"GetMoreInfoWorker error: e={e}")
77 | self._is_work = False
78 | self._url = ''
79 | self._pwd = ''
80 | self._mutex.unlock()
81 | else:
82 | self.msg.emit("后台正在运行,请稍后重试!", 3100)
83 |
--------------------------------------------------------------------------------
/lanzou/gui/workers/pwd.py:
--------------------------------------------------------------------------------
1 | from PyQt6.QtCore import QThread, pyqtSignal, QMutex
2 | from lanzou.api import LanZouCloud
3 | from lanzou.debug import logger
4 |
5 |
6 | class SetPwdWorker(QThread):
7 | '''设置文件(夹)提取码 线程'''
8 | msg = pyqtSignal(str, int)
9 | update = pyqtSignal(object, object, object, object)
10 |
11 | def __init__(self, parent=None):
12 | super(SetPwdWorker, self).__init__(parent)
13 | self._disk = None
14 | self.infos = []
15 | self._work_id = -1
16 | self._mutex = QMutex()
17 | self._is_work = False
18 |
19 | def set_disk(self, disk):
20 | self._disk = disk
21 |
22 | def set_values(self, infos, work_id):
23 | self.infos = infos
24 | self._work_id = work_id
25 | self.start()
26 |
27 | def __del__(self):
28 | self.wait()
29 |
30 | def stop(self):
31 | self._mutex.lock()
32 | self._is_work = False
33 | self._mutex.unlock()
34 |
35 | def run(self):
36 | if not self._is_work:
37 | self._mutex.lock()
38 | self._is_work = True
39 | try:
40 | has_file = False
41 | has_folder = False
42 | failed = False
43 | failed_code = ""
44 | for infos in self.infos:
45 | if infos.is_file: # 文件
46 | has_file = True
47 | new_pwd = infos.new_pwd
48 | if 2 > len(new_pwd) >= 1 or len(new_pwd) > 6:
49 | self.msg.emit("文件提取码为2-6位字符,关闭请留空!", 4000)
50 | raise UserWarning
51 | else: # 文件夹
52 | has_folder = True
53 | new_pwd = infos.new_pwd
54 | if 2 > len(new_pwd) >= 1 or len(new_pwd) > 12:
55 | self.msg.emit("文件夹提取码为0-12位字符,关闭请留空!", 4000)
56 | raise UserWarning
57 | res = self._disk.set_passwd(infos.id, infos.new_pwd, infos.is_file)
58 | if res != LanZouCloud.SUCCESS:
59 | failed_code = failed_code + str(res)
60 | failed = True
61 | if failed:
62 | self.msg.emit(f"❌部分提取码变更失败:{failed_code},请勿使用特殊符号!", 4000)
63 | else:
64 | self.msg.emit("✅提取码变更成功!♬", 3000)
65 |
66 | self.update.emit(self._work_id, has_file, has_folder, False)
67 | except TimeoutError:
68 | self.msg.emit("网络超时,请稍后重试!", 6000)
69 | except UserWarning:
70 | pass
71 | except Exception as e:
72 | logger.error(f"SetPwdWorker error: e={e}")
73 | self._is_work = False
74 | self._mutex.unlock()
75 | else:
76 | self.msg.emit("后台正在运行,请稍后重试!", 3100)
77 |
--------------------------------------------------------------------------------
/lanzou/gui/workers/recovery.py:
--------------------------------------------------------------------------------
1 | from time import sleep
2 | from PyQt6.QtCore import QThread, pyqtSignal, QMutex
3 | from lanzou.api import LanZouCloud
4 |
5 | from lanzou.api.types import RecFolder, RecFile
6 | from lanzou.debug import logger
7 |
8 |
9 | class GetRecListsWorker(QThread):
10 | '''获取回收站列表'''
11 | folders = pyqtSignal(object)
12 | infos = pyqtSignal(object, object)
13 | msg = pyqtSignal(str, int)
14 |
15 | def __init__(self, parent=None):
16 | super(GetRecListsWorker, self).__init__(parent)
17 | self._disk = None
18 | self._mutex = QMutex()
19 | self._is_work = False
20 | self._folder_id = None
21 |
22 | def set_disk(self, disk):
23 | self._disk = disk
24 |
25 | def set_values(self, fid):
26 | # 用于获取回收站指定文件夹内文件信息
27 | self._folder_id = fid
28 | self.start()
29 |
30 | def __del__(self):
31 | self.wait()
32 |
33 | def stop(self):
34 | self._mutex.lock()
35 | self._is_work = False
36 | self._mutex.unlock()
37 |
38 | def run(self):
39 | if not self._is_work:
40 | self._mutex.lock()
41 | self._is_work = True
42 | try:
43 | if self._folder_id:
44 | file_lists = self._disk.get_rec_file_list(folder_id=self._folder_id)
45 | self._folder_id = None
46 | self.folders.emit(file_lists)
47 | raise UserWarning
48 | dir_lists = self._disk.get_rec_dir_list()
49 | file_lists = self._disk.get_rec_file_list(folder_id=-1)
50 | self.infos.emit(dir_lists, file_lists)
51 | self.msg.emit("刷新列表成功!", 2000)
52 | except TimeoutError:
53 | self.msg.emit("网络超时,请稍后重试!", 6000)
54 | except UserWarning:
55 | pass
56 | except Exception as e:
57 | logger.error(f"GetRecListsWorker error: e={e}")
58 | self._is_work = False
59 | self._mutex.unlock()
60 | else:
61 | self.msg.emit("后台正在运行,请稍后重试!", 3100)
62 |
63 |
64 | class RecManipulator(QThread):
65 | '''操作回收站'''
66 | msg = pyqtSignal(str, int)
67 | succeeded = pyqtSignal()
68 |
69 | def __init__(self, parent=None):
70 | super(RecManipulator, self).__init__(parent)
71 | self._disk = None
72 | self._mutex = QMutex()
73 | self._is_work = False
74 | self._action = None
75 | self._folders = []
76 | self._files= []
77 |
78 | def set_disk(self, disk):
79 | self._disk = disk
80 |
81 | def set_values(self, infos, action):
82 | # 操作回收站选定行
83 | self._action = None
84 | self._folders = []
85 | self._files= []
86 | for item in infos:
87 | if isinstance(item, RecFile):
88 | self._files.append(item.id)
89 | elif isinstance(item, RecFolder):
90 | self._folders.append(item.id)
91 | self._action = action
92 | self.start()
93 |
94 | def __del__(self):
95 | self.wait()
96 |
97 | def stop(self):
98 | self._mutex.lock()
99 | self._is_work = False
100 | self._mutex.unlock()
101 |
102 | def run(self):
103 | if not self._is_work:
104 | self._mutex.lock()
105 | self._is_work = True
106 | try:
107 | res = None
108 | if self._action == "recovery":
109 | if self._files or self._folders:
110 | res = self._disk.recovery_multi(self._files, self._folders)
111 | if res == LanZouCloud.SUCCESS:
112 | self.msg.emit("选定文件(夹)恢复成功,即将刷新列表", 2500)
113 | elif self._action == "delete":
114 | if self._files or self._folders:
115 | if self._files or self._folders:
116 | res = self._disk.delete_rec_multi(self._files, self._folders)
117 | if res == LanZouCloud.SUCCESS:
118 | self.msg.emit("选定文件(夹)彻底删除成功,即将刷新列表", 2500)
119 | elif self._action == "clean":
120 | res = self._disk.clean_rec()
121 | if res == LanZouCloud.SUCCESS:
122 | self.msg.emit("清空回收站成功,即将刷新列表", 2500)
123 | elif self._action == "recovery_all":
124 | res = self._disk.recovery_all()
125 | if res == LanZouCloud.SUCCESS:
126 | self.msg.emit("文件(夹)全部还原成功,即将刷新列表", 2500)
127 | if isinstance(res, int):
128 | if res == LanZouCloud.FAILED:
129 | self.msg.emit("失败,请重试!", 4500)
130 | elif res == LanZouCloud.NETWORK_ERROR:
131 | self.msg.emit("网络错误,请稍后重试!", 4500)
132 | else:
133 | sleep(2.6)
134 | self.succeeded.emit()
135 | except TimeoutError:
136 | self.msg.emit("网络超时,请稍后重试!", 6000)
137 | except Exception as e:
138 | logger.error(f"RecManipulator error: e={e}")
139 | self._is_work = False
140 | self._action = None
141 | self._folders = []
142 | self._files= []
143 | self._mutex.unlock()
144 | else:
145 | self.msg.emit("后台正在运行,请稍后重试!", 3100)
146 |
--------------------------------------------------------------------------------
/lanzou/gui/workers/refresh.py:
--------------------------------------------------------------------------------
1 | from PyQt6.QtCore import QThread, pyqtSignal, QMutex
2 | from lanzou.debug import logger
3 |
4 |
5 | class ListRefresher(QThread):
6 | '''跟新目录文件与文件夹列表线程'''
7 | infos = pyqtSignal(object)
8 | err_msg = pyqtSignal(str, int)
9 |
10 | def __init__(self, parent=None):
11 | super(ListRefresher, self).__init__(parent)
12 | self._disk = None
13 | self._fid = -1
14 | self.r_files = True
15 | self.r_folders = True
16 | self.r_path = True
17 | self._mutex = QMutex()
18 | self._is_work = False
19 |
20 | def set_disk(self, disk):
21 | self._disk = disk
22 |
23 | def set_values(self, fid, r_files=True, r_folders=True, r_path=True):
24 | if not self._is_work:
25 | self._fid = fid
26 | self.r_files = r_files
27 | self.r_folders = r_folders
28 | self.r_path = r_path
29 | self.start()
30 | else:
31 | self.err_msg.emit("正在更新目录,请稍后再试!", 3100)
32 |
33 | def __del__(self):
34 | self.wait()
35 |
36 | def stop(self):
37 | self._mutex.lock()
38 | self._is_work = False
39 | self._mutex.unlock()
40 |
41 | def goto_root_dir(self):
42 | self._fid = -1
43 | self.run()
44 |
45 | def run(self):
46 | if not self._is_work:
47 | self._mutex.lock()
48 | self._is_work = True
49 | emit_infos = {}
50 | # 传递更新内容
51 | emit_infos['r'] = {'fid': self._fid, 'files': self.r_files, 'folders': self.r_folders, 'path': self.r_path}
52 | try:
53 | if self.r_files:
54 | # [i.id, i.name, i.size, i.time, i.downs, i.has_pwd, i.has_des]
55 | info = {i.name: i for i in self._disk.get_file_list(self._fid)}
56 | emit_infos['file_list'] = {key: info.get(key) for key in sorted(info.keys())} # {name-File}
57 | if self.r_folders:
58 | folders, full_path = self._disk.get_dir_list(self._fid)
59 | if not full_path and not folders and self._fid != -1:
60 | self.err_msg.emit(f"文件夹id {self._fid} 不存在,将切换到更目录", 2900)
61 | self._is_work = False
62 | self._mutex.unlock()
63 | return self.goto_root_dir()
64 | info = {i.name: i for i in folders}
65 | emit_infos['folder_list'] = {key: info.get(key) for key in sorted(info.keys())} # {name-Folder}
66 | emit_infos['path_list'] = full_path
67 | except TimeoutError:
68 | self.err_msg.emit("网络超时,无法更新目录,稍后再试!", 7000)
69 | except Exception as e:
70 | self.err_msg.emit("未知错误,无法更新目录,稍后再试!", 7000)
71 | logger.error(f"ListRefresher error: e={e}")
72 | else:
73 | self.infos.emit(emit_infos)
74 | self._is_work = False
75 | self._mutex.unlock()
76 |
--------------------------------------------------------------------------------
/lanzou/gui/workers/rename.py:
--------------------------------------------------------------------------------
1 | from time import sleep
2 | from PyQt6.QtCore import QThread, pyqtSignal, QMutex
3 | from lanzou.api import LanZouCloud
4 | from lanzou.debug import logger
5 |
6 |
7 | class RenameMkdirWorker(QThread):
8 | """重命名、修改简介与新建文件夹 线程"""
9 | # infos = pyqtSignal(object, object)
10 | msg = pyqtSignal(str, int)
11 | update = pyqtSignal(object, object, object, object)
12 |
13 | def __init__(self, parent=None):
14 | super(RenameMkdirWorker, self).__init__(parent)
15 | self._disk = None
16 | self._work_id = -1
17 | self._folder_list = None
18 | self.infos = None
19 | self._mutex = QMutex()
20 | self._is_work = False
21 |
22 | def set_disk(self, disk):
23 | self._disk = disk
24 |
25 | def set_values(self, infos, work_id, folder_list):
26 | self.infos = infos # 对话框标识文件与文件夹
27 | self._work_id = work_id
28 | self._folder_list = folder_list
29 | self.start()
30 |
31 | def __del__(self):
32 | self.wait()
33 |
34 | def stop(self):
35 | self._mutex.lock()
36 | self._is_work = False
37 | self._mutex.unlock()
38 |
39 | def run(self):
40 | if not self._is_work:
41 | self._mutex.lock()
42 | self._is_work = True
43 |
44 | action = self.infos[0]
45 | try:
46 | if action == 'new': # 新建文件夹
47 | new_name = self.infos[1]
48 | new_des = self.infos[2]
49 | if new_name in self._folder_list.keys():
50 | self.msg.emit(f"文件夹已存在:{new_name}", 7000)
51 | else:
52 | res = self._disk.mkdir(self._work_id, new_name, new_des)
53 | if res == LanZouCloud.MKDIR_ERROR:
54 | self.msg.emit(f"创建文件夹失败:{new_name}", 7000)
55 | else:
56 | sleep(1.5) # 暂停一下,否则无法获取新建的文件夹
57 | self.update.emit(self._work_id, False, True, False) # 此处仅更新文件夹,并显示
58 | self.msg.emit(f"成功创建文件夹:{new_name}", 4000)
59 | else: # 重命名、修改简介
60 | has_file = False
61 | has_folder = False
62 | failed = False
63 | for info in self.infos[1]:
64 | if info.is_file: # 修改文件描述
65 | res = self._disk.set_desc(info.id, info.new_des, is_file=info.is_file)
66 | if res == LanZouCloud.SUCCESS:
67 | has_file = True
68 | else:
69 | failed = True
70 | else: # 修改文件夹,action == "folder"
71 | name = info.new_name or info.nmae
72 | res = self._disk._set_dir_info(info.id, str(name), str(info.new_des))
73 | if res == LanZouCloud.SUCCESS:
74 | has_folder = True
75 | else:
76 | failed = True
77 | self.update.emit(self._work_id, has_file, has_folder, False)
78 | if failed:
79 | self.msg.emit("有发生错误!", 6000)
80 | else:
81 | self.msg.emit("修改成功!", 4000)
82 | except TimeoutError:
83 | self.msg.emit("网络超时,请稍后重试!", 6000)
84 | except Exception as e:
85 | logger.error(f"RenameMikdirWorker error: e={e}")
86 |
87 | self._is_work = False
88 | self._mutex.unlock()
89 | else:
90 | self.msg.emit("后台正在运行,请稍后重试!", 3100)
91 |
--------------------------------------------------------------------------------
/lanzou/gui/workers/rm.py:
--------------------------------------------------------------------------------
1 | from PyQt6.QtCore import QThread, pyqtSignal, QMutex
2 | from lanzou.debug import logger
3 |
4 |
5 | class RemoveFilesWorker(QThread):
6 | '''删除文件(夹)线程'''
7 | msg = pyqtSignal(object, object)
8 | finished = pyqtSignal()
9 |
10 | def __init__(self, parent=None):
11 | super(RemoveFilesWorker, self).__init__(parent)
12 | self._disk = None
13 | self.infos = None
14 | self._mutex = QMutex()
15 | self._is_work = False
16 |
17 | def set_disk(self, disk):
18 | self._disk = disk
19 |
20 | def set_values(self, infos):
21 | self.infos = infos
22 | self.start()
23 |
24 | def __del__(self):
25 | self.wait()
26 |
27 | def stop(self):
28 | self._mutex.lock()
29 | self._is_work = False
30 | self._mutex.unlock()
31 |
32 | def run(self):
33 | if not self._is_work:
34 | self._mutex.lock()
35 | self._is_work = True
36 | if not self.infos:
37 | self._is_work = False
38 | self._mutex.unlock()
39 | return
40 | for i in self.infos:
41 | try:
42 | self._disk.delete(i['fid'], i['is_file'])
43 | except TimeoutError:
44 | self.msg.emit(f"删除 {i['name']} 因网络超时失败!", 3000)
45 | except Exception as e:
46 | logger.error(f"RemoveFileWorker error: e={e}")
47 | self.finished.emit()
48 | self._is_work = False
49 | self._mutex.unlock()
50 | else:
51 | self.msg.emit("后台正在运行删除指令!", 3100)
52 |
--------------------------------------------------------------------------------
/lanzou/gui/workers/share.py:
--------------------------------------------------------------------------------
1 | import re
2 | from PyQt6.QtCore import QThread, pyqtSignal, QMutex
3 |
4 | from lanzou.api.utils import is_folder_url, is_file_url
5 | from lanzou.api import LanZouCloud
6 | from lanzou.debug import logger
7 |
8 |
9 | class GetSharedInfo(QThread):
10 | '''提取界面获取分享链接信息'''
11 | infos = pyqtSignal(object)
12 | msg = pyqtSignal(str, int)
13 | update = pyqtSignal()
14 | clean = pyqtSignal()
15 |
16 | def __init__(self, parent=None):
17 | super(GetSharedInfo, self).__init__(parent)
18 | self._disk = None
19 | self.share_url = ""
20 | self.pwd = ""
21 | self.is_file = ""
22 | self.is_folder = ""
23 | self._mutex = QMutex()
24 | self._is_work = False
25 | 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+)?"
26 |
27 | def set_disk(self, disk):
28 | self._disk = disk
29 |
30 | def set_values(self, text, pwd_input=""):
31 | '''获取分享链接信息'''
32 | text = text.strip()
33 | pwd_input = pwd_input.strip()
34 | if not text:
35 | self.update.emit()
36 | return None
37 | for share_url, _, pwd in re.findall(self._pat, text):
38 | if is_file_url(share_url): # 文件链接
39 | is_file = True
40 | is_folder = False
41 | self.msg.emit("正在获取文件链接信息……", 20000)
42 | elif is_folder_url(share_url): # 文件夹链接
43 | is_folder = True
44 | is_file = False
45 | self.msg.emit("正在获取文件夹链接信息,可能需要几秒钟,请稍候……", 500000)
46 | else:
47 | self.msg.emit(f"{share_url} 为非法链接!", 0)
48 | self.update.emit()
49 | return None
50 | self.clean.emit() # 清理旧的显示信息
51 | self.share_url = share_url
52 | if pwd_input:
53 | self.pwd = pwd_input
54 | elif pwd:
55 | self.pwd = pwd
56 | else: # 一个或两个汉字的提取码
57 | pwd_ = text.split(' ')[-1].split(':')[-1].split(':')[-1]
58 | self.pwd = pwd_ if 1<= len(pwd_) <= 2 else ''
59 | self.is_file = is_file
60 | self.is_folder = is_folder
61 | self.start()
62 | break
63 |
64 | def __del__(self):
65 | self.wait()
66 |
67 | def stop(self): # 用于手动停止
68 | self._mutex.lock()
69 | self._is_work = False
70 | self._mutex.unlock()
71 |
72 | def emit_msg(self, infos):
73 | '''根据查询信息发送状态信号'''
74 | show_time = 2999 # 提示显示时间,单位 ms
75 | if infos.code == LanZouCloud.FILE_CANCELLED:
76 | self.msg.emit("
文件不存在,或已删除!", show_time)
77 | elif infos.code == LanZouCloud.URL_INVALID:
78 | self.msg.emit("
链接非法!", show_time)
79 | elif infos.code == LanZouCloud.PASSWORD_ERROR:
80 | self.msg.emit(f"
提取码 [{self.pwd}] 错误!", show_time)
81 | elif infos.code == LanZouCloud.LACK_PASSWORD:
82 | self.msg.emit("
请在链接后面跟上提取码,空格分割!", show_time)
83 | elif infos.code == LanZouCloud.NETWORK_ERROR:
84 | self.msg.emit("
网络错误!", show_time)
85 | elif infos.code == LanZouCloud.SUCCESS:
86 | self.msg.emit("
提取成功!", show_time)
87 | else:
88 | self.msg.emit(f"
未知错误 code={infos.code}!", show_time * 4)
89 |
90 | def run(self):
91 | if not self._is_work:
92 | self._mutex.lock()
93 | self._is_work = True
94 | try:
95 | if self.is_file: # 链接为文件
96 | _infos = self._disk.get_share_info_by_url(self.share_url, self.pwd)
97 | self.emit_msg(_infos)
98 | self.infos.emit(_infos)
99 | elif self.is_folder: # 链接为文件夹
100 | _infos = self._disk.get_folder_info_by_url(self.share_url, self.pwd)
101 | self.emit_msg(_infos)
102 | self.infos.emit(_infos)
103 | else:
104 | logger.error(f"GetShareInfo error: Not a file or folder!")
105 | except TimeoutError:
106 | self.msg.emit("font color='red'>网络超时!请稍后重试", 5000)
107 | except Exception as e:
108 | self.msg.emit(f"font color='red'>未知错误:{e}", 5000)
109 | logger.error(f"GetShareInfo error: e={e}")
110 | self._is_work = False
111 | self.update.emit()
112 | self._mutex.unlock()
113 | else:
114 | self.msg.emit("
后台正在运行,稍后重试!", 4000)
115 |
--------------------------------------------------------------------------------
/lanzou/gui/workers/update.py:
--------------------------------------------------------------------------------
1 | from time import sleep
2 | import re
3 | import requests
4 | from PyQt6.QtCore import QThread, pyqtSignal, QMutex
5 | from lanzou.debug import logger
6 |
7 |
8 | class CheckUpdateWorker(QThread):
9 | '''检测软件更新'''
10 | infos = pyqtSignal(object, object)
11 | bg_update_infos = pyqtSignal(object, object)
12 |
13 | def __init__(self, parent=None):
14 | super(CheckUpdateWorker, self).__init__(parent)
15 | self._ver = ''
16 | self._manual = False
17 | self._mutex = QMutex()
18 | self._is_work = False
19 | self._folder_id = None
20 | self._api = 'https://api.github.com/repos/rachpt/lanzou-gui/releases/latest'
21 | self._api_mirror = 'https://gitee.com/api/v5/repos/rachpt/lanzou-gui/releases/latest'
22 |
23 | def set_values(self, ver: str, manual: bool=False):
24 | # 检查更新
25 | self._ver = ver
26 | self._manual = manual
27 | self.start()
28 |
29 | def __del__(self):
30 | self.wait()
31 |
32 | def stop(self):
33 | self._mutex.lock()
34 | self._is_work = False
35 | self._mutex.unlock()
36 |
37 | def run(self):
38 | if not self._is_work:
39 | self._mutex.lock()
40 | self._is_work = True
41 | resp = None
42 | try:
43 | resp = requests.get(self._api).json()
44 | except (requests.RequestException, TimeoutError, requests.exceptions.ConnectionError):
45 | logger.debug("chcek update from github error")
46 | try: resp = requests.get(self._api_mirror).json()
47 | except:
48 | logger.debug("chcek update from gitee error")
49 | except Exception as e:
50 | logger.error(f"CheckUpdateWorker error: e={e}")
51 | if resp:
52 | try:
53 | tag_name, msg = resp['tag_name'], resp['body']
54 | ver = self._ver.replace('v', '').split('-')[0].split('.')
55 | ver2 = tag_name.replace('v', '').split('-')[0].split('.')
56 | local_version = int(ver[0]) * 100 + int(ver[1]) * 10 + int(ver[2])
57 | remote_version = int(ver2[0]) * 100 + int(ver2[1]) * 10 + int(ver2[2])
58 | if remote_version > local_version:
59 | urls = re.findall(r'https?://[-\.a-zA-Z0-9/_#?&%@]+', msg)
60 | for url in urls:
61 | new_url = f'
{url}'
62 | msg = msg.replace(url, new_url)
63 | msg = msg.replace('\n', '
')
64 | self.infos.emit(tag_name, msg)
65 | if not self._manual: # 打开软件时检测更新
66 | self.bg_update_infos.emit(tag_name, msg)
67 | elif self._manual:
68 | self.infos.emit("0", "目前还没有发布新版本!")
69 | except AttributeError:
70 | if self._manual:
71 | self.infos.emit("v0.0.0", "检查更新时发生异常,请重试!")
72 | except Exception as e:
73 | logger.error(f"Check Update Version error: e={e}")
74 | else:
75 | if self._manual:
76 | self.infos.emit("v0.0.0", f"检查更新时
api.github.com、
gitee.com 拒绝连接,请稍后重试!")
77 | self._manual = False
78 | self._is_work = False
79 | self._mutex.unlock()
80 | else:
81 | if self._manual:
82 | self.infos.emit("v0.0.0", "后台正在运行,请稍等!")
83 |
--------------------------------------------------------------------------------
/lanzou/gui/workers/upload.py:
--------------------------------------------------------------------------------
1 | import os
2 | from PyQt6.QtCore import QThread, pyqtSignal, QMutex
3 | from lanzou.api import LanZouCloud
4 | from lanzou.debug import logger
5 |
6 |
7 | class Uploader(QThread):
8 | '''单个文件上传线程'''
9 | finished_ = pyqtSignal(object)
10 | folder_file_failed = pyqtSignal(object, object)
11 | failed = pyqtSignal()
12 | proc = pyqtSignal(str)
13 | update = pyqtSignal()
14 |
15 | def __init__(self, disk, task, callback, allow_big_file=False, parent=None):
16 | super(Uploader, self).__init__(parent)
17 | self._disk = disk
18 | self._task = task
19 | self._allow_big_file = allow_big_file
20 | self._callback_thread = callback(task)
21 |
22 | def stop(self):
23 | self.terminate()
24 |
25 | def _callback(self):
26 | """显示进度条的回调函数"""
27 | if not self._callback_thread.isRunning():
28 | self.update.emit()
29 | self.proc.emit(self._callback_thread.emit_msg)
30 | self._callback_thread.start()
31 |
32 | def _down_failed(self, code, file):
33 | """显示下载失败的回调函数"""
34 | self.folder_file_failed.emit(code, file)
35 |
36 | def __del__(self):
37 | self.wait()
38 |
39 | def run(self):
40 | if not self._task:
41 | logger.error("Upload task is empty!")
42 | return None
43 | self._task.run = True
44 | try:
45 | if os.path.isdir(self._task.url):
46 | code, fid, isfile = self._disk.upload_dir(self._task, self._callback, self._allow_big_file)
47 | else:
48 | code, fid, isfile = self._disk.upload_file(self._task, self._task.url, self._task.fid,
49 | self._callback, self._allow_big_file)
50 | except TimeoutError:
51 | self._task.info = LanZouCloud.NETWORK_ERROR
52 | self.update.emit()
53 | except Exception as err:
54 | logger.error(f"Upload error: err={err}")
55 | self._task.info = err
56 | self.update.emit()
57 | else:
58 | if code == LanZouCloud.SUCCESS:
59 | if self._task.pwd:
60 | self._disk.set_passwd(fid, self._task.pwd, is_file=isfile)
61 | if self._task.desc:
62 | self._disk.set_desc(fid, self._task.desc, is_file=isfile)
63 | self._task.rate = 1000 # 回调线程可能在休眠
64 | else:
65 | self.failed.emit()
66 | self._task.run = False
67 | self.finished_.emit(self._task)
68 |
--------------------------------------------------------------------------------
/lanzou/login_assister.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from PyQt5.QtCore import QUrl, pyqtSignal
3 | from PyQt5.QtWidgets import QApplication, QDialog, QVBoxLayout
4 | from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineProfile
5 |
6 |
7 | class MyWebEngineView(QWebEngineView):
8 | def __init__(self, user, pwd, *args, **kwargs):
9 | super(MyWebEngineView, self).__init__(*args, **kwargs)
10 | self.cookies = {}
11 | self._user = user
12 | self._pwd = pwd
13 | # 绑定cookie被添加的信号槽
14 | QWebEngineProfile.defaultProfile().cookieStore().cookieAdded.connect(self.onCookieAdd)
15 | self.loadFinished.connect(self._on_load_finished)
16 | self.urlChanged.connect(self._on_load_finished)
17 |
18 | def _on_load_finished(self):
19 | self.page().toHtml(self.Callable)
20 |
21 | def Callable(self, html_str):
22 | try:
23 | self.html = html_str
24 | js = """var l_name=document.getElementsByName('username');
25 | if (l_name.length > 0) {{
26 | l_name[0].value = '{}';
27 | }};
28 | var l_pwd=document.getElementsByName('password');
29 | if (l_pwd.length > 0) {{
30 | l_pwd[0].value = '{}';
31 | }};""".format(self._user, self._pwd)
32 | self.page().runJavaScript(js)
33 | except: pass
34 | # except Exception as e:
35 | # print("Err:", e)
36 |
37 | def onCookieAdd(self, cookie):
38 | name = cookie.name().data().decode('utf-8')
39 | value = cookie.value().data().decode('utf-8')
40 | self.cookies[name] = value
41 |
42 | def get_cookie(self):
43 | cookie_dict = {}
44 | for key, value in self.cookies.items():
45 | if key in ('ylogin', 'phpdisk_info'):
46 | cookie_dict[key] = value
47 | return cookie_dict
48 |
49 |
50 | class LoginWindow(QDialog):
51 | cookie = pyqtSignal(object)
52 |
53 | def __init__(self, user=None, pwd=None, gui=False):
54 | super().__init__()
55 | self._user = user
56 | self._pwd = pwd
57 | self._base_url = 'https://pc.woozooo.com/'
58 | self._gui = gui
59 | self.setup()
60 |
61 | def setup(self):
62 | self.setWindowTitle('滑动滑块,完成登录')
63 | url = self._base_url + 'account.php?action=login&ref=/mydisk.php'
64 | QWebEngineProfile.defaultProfile().cookieStore().deleteAllCookies()
65 | self.web = MyWebEngineView(self._user, self._pwd)
66 | self.web.urlChanged.connect(self.get_cookie)
67 | self.web.resize(480, 400)
68 | self.web.load(QUrl(url))
69 | self.box = QVBoxLayout(self)
70 | self.box.addWidget(self.web)
71 |
72 | def get_cookie(self):
73 | home_url = self._base_url + 'mydisk.php'
74 | if self.web.url().toString() == home_url:
75 | cookie = self.web.get_cookie()
76 | if cookie:
77 | if self._gui:
78 | try: print(";".join([f'{k}={v}' for k, v in cookie.items()]), end='')
79 | except: pass
80 | else:
81 | self.cookie.emit(cookie)
82 | self.reject()
83 |
84 |
85 | if __name__ == "__main__":
86 | if len(sys.argv) == 3:
87 | username, password = sys.argv[1], sys.argv[2]
88 | app = QApplication(sys.argv)
89 | form = LoginWindow(username, password, gui=True)
90 | form.show()
91 | sys.exit(app.exec())
92 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import sys
4 | from PyQt6.QtWidgets import QApplication
5 |
6 | from lanzou.gui.gui import MainWindow, get_lanzou_logo
7 |
8 |
9 | if __name__ == "__main__":
10 | app = QApplication(sys.argv)
11 | app.setWindowIcon(get_lanzou_logo())
12 | form = MainWindow()
13 | form.show()
14 | form.call_login_launcher()
15 | sys.exit(app.exec())
16 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | PyQt6
2 | # PyQtWebEngine # 可能需要装 spyder 解决 PyQtWebEngine 版本冲突
3 | requests
4 | requests_toolbelt
5 | browser_cookie3
6 |
7 | # browser_cookie3 requires
8 | # pyaes
9 | # pbkdf2
10 | # keyring
11 | # lz4
12 | # pycryptodome
13 | # SecretStorage
14 | # linux: pip install secretstorage dbus-python
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | from setuptools import setup, find_packages
4 | from lanzou.gui import version
5 |
6 | setup(
7 | name='lanzou-gui',
8 | version=version,
9 | description='Lanzou Cloud GUI',
10 | license="MIT",
11 | author='rachpt',
12 | author_email='rachpt@126.com',
13 | packages=find_packages(),
14 | package_data={
15 | '': []
16 | },
17 | python_requires=">=3.6",
18 | url='https://github.com/rachpt/lanzou-gui',
19 | keywords=['lanzou', 'lanzoucloud', 'gui', 'application', 'PyQt6', 'Python 3'],
20 | classifiers=(
21 | 'Development Status :: 3 - Alpha',
22 | 'Programming Language :: Python :: 3.8',
23 | 'Programming Language :: Python :: 3.7',
24 | 'Programming Language :: Python :: 3.6',
25 | 'Programming Language :: Python :: 3 :: Only',
26 | 'Environment :: X11 Applications :: Qt',
27 | 'Topic :: Internet',
28 | 'Operating System :: Microsoft :: Windows',
29 | 'Operating System :: POSIX :: Linux',
30 | 'Operating System :: MacOS',
31 | ),
32 | install_requires=[
33 | 'PyQt6',
34 | 'PyQtWebEngine',
35 | 'requests',
36 | 'requests_toolbelt',
37 | 'browser_cookie3',
38 | ],
39 | )
40 |
--------------------------------------------------------------------------------
/version_info.txt:
--------------------------------------------------------------------------------
1 | # version infos
2 |
3 | VSVersionInfo(
4 | ffi=FixedFileInfo(
5 | filevers=(0, 5, 1, 0),
6 | prodvers=(0, 5, 1, 0),
7 | mask=0x3f,
8 | flags=0x0,
9 | OS=0x4,
10 | fileType=0x1,
11 | subtype=0x0,
12 | date=(0, 0)
13 | ),
14 | kids=[
15 | StringFileInfo([
16 | StringTable(
17 | u'040904B0',
18 | [StringStruct(u'CompanyName', u'rachpt.cn'),
19 | StringStruct(u'FileDescription', u'蓝奏云客户端'),
20 | StringStruct(u'FileVersion', u'0.5.1'),
21 | StringStruct(u'InternalName', u'lanzou-gui'),
22 | StringStruct(u'LegalCopyright', u'Copyright (c) rachpt'),
23 | StringStruct(u'OriginalFilename', u'lanzou-gui_win64_v0.5.1.exe'),
24 | StringStruct(u'ProductName', u'lanzou-gui_win64'),
25 | StringStruct(u'ProductVersion', u'0.5.1 (r220605)')])
26 | ]),
27 | VarFileInfo([VarStruct(u'Translation', [2052, 1033])])
28 | ]
29 | )
30 |
--------------------------------------------------------------------------------