From 5a5fa4cdeb3bdd0c6c0ecee388b590d3906b9bae Mon Sep 17 00:00:00 2001 From: x1ao4 Date: Sun, 25 May 2025 21:14:04 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=BA=E6=95=B0=E6=8D=AE=E5=BA=93=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E4=BF=9D=E5=AD=98=E8=B7=AF=E5=BE=84=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E7=9A=84=E5=AD=98=E5=82=A8=E5=8A=9F=E8=83=BD=EF=BC=8C=E4=B8=BA?= =?UTF-8?q?=E9=80=89=E6=8B=A9=E4=BF=9D=E5=AD=98=E5=88=B0=E7=9A=84=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=A4=B9=E9=A1=B5=E9=9D=A2=E5=A2=9E=E5=8A=A0=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E6=96=87=E4=BB=B6=E5=92=8C=E8=AE=B0=E5=BD=95=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/run.py | 49 +++++++++++++++++++++++ app/sdk/db.py | 71 ++++++++++++++++++++++++++------- app/static/css/main.css | 13 ++++--- app/templates/index.html | 84 ++++++++++++++++++++++++++++++++-------- quark_auto_save.py | 39 +++++++++++++++++-- 5 files changed, 218 insertions(+), 38 deletions(-) diff --git a/app/run.py b/app/run.py index 596bbd0..2f637b2 100644 --- a/app/run.py +++ b/app/run.py @@ -650,6 +650,55 @@ def delete_file(): account = Quark(config_data["cookie"][0], 0) if fid := request.json.get("fid"): response = account.delete([fid]) + + # 处理delete_records参数 + if request.json.get("delete_records") and response.get("code") == 0: + try: + # 初始化数据库 + db = RecordDB() + + # 获取save_path参数 + save_path = request.json.get("save_path", "") + + # 如果没有提供save_path,则不删除任何记录 + if not save_path: + response["deleted_records"] = 0 + # logging.info(f">>> 删除文件 {fid} 但未提供save_path,不删除任何记录") + return jsonify(response) + + # 查询与该文件ID和save_path相关的所有记录 + cursor = db.conn.cursor() + + # 使用file_id和save_path进行精确匹配 + cursor.execute("SELECT id FROM transfer_records WHERE file_id = ? AND save_path = ?", (fid, save_path)) + record_ids = [row[0] for row in cursor.fetchall()] + + # 如果没有找到匹配的file_id记录,尝试通过文件名查找 + if not record_ids: + # 获取文件名(如果有的话) + file_name = request.json.get("file_name", "") + if file_name: + # 使用文件名和save_path进行精确匹配 + cursor.execute(""" + SELECT id FROM transfer_records + WHERE (original_name = ? OR renamed_to = ?) + AND save_path = ? + """, (file_name, file_name, save_path)) + + record_ids = [row[0] for row in cursor.fetchall()] + + # 删除找到的所有记录 + deleted_count = 0 + for record_id in record_ids: + deleted_count += db.delete_record(record_id) + + # 添加删除记录的信息到响应中 + response["deleted_records"] = deleted_count + # logging.info(f">>> 删除文件 {fid} 同时删除了 {deleted_count} 条相关记录") + + except Exception as e: + logging.error(f">>> 删除记录时出错: {str(e)}") + # 不影响主流程,即使删除记录失败也返回文件删除成功 else: response = {"success": False, "message": "缺失必要字段: fid"} return jsonify(response) diff --git a/app/sdk/db.py b/app/sdk/db.py index 302d8c5..316de4e 100644 --- a/app/sdk/db.py +++ b/app/sdk/db.py @@ -31,9 +31,17 @@ class RecordDB: resolution TEXT, modify_date INTEGER NOT NULL, file_id TEXT, - file_type TEXT + file_type TEXT, + save_path TEXT ) ''') + + # 检查save_path字段是否存在,如果不存在则添加 + cursor.execute("PRAGMA table_info(transfer_records)") + columns = [column[1] for column in cursor.fetchall()] + if 'save_path' not in columns: + cursor.execute('ALTER TABLE transfer_records ADD COLUMN save_path TEXT') + self.conn.commit() def close(self): @@ -41,19 +49,19 @@ class RecordDB: self.conn.close() def add_record(self, task_name, original_name, renamed_to, file_size, modify_date, - duration="", resolution="", file_id="", file_type=""): + duration="", resolution="", file_id="", file_type="", save_path=""): """添加一条转存记录""" cursor = self.conn.cursor() cursor.execute( "INSERT INTO transfer_records (transfer_time, task_name, original_name, renamed_to, file_size, " - "duration, resolution, modify_date, file_id, file_type) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + "duration, resolution, modify_date, file_id, file_type, save_path) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", (int(time.time()), task_name, original_name, renamed_to, file_size, - duration, resolution, modify_date, file_id, file_type) + duration, resolution, modify_date, file_id, file_type, save_path) ) self.conn.commit() return cursor.lastrowid - def update_renamed_to(self, file_id, original_name, renamed_to, task_name=""): + def update_renamed_to(self, file_id, original_name, renamed_to, task_name="", save_path=""): """更新最近一条记录的renamed_to字段 Args: @@ -61,6 +69,7 @@ class RecordDB: original_name: 原文件名 renamed_to: 重命名后的文件名 task_name: 任务名称,可选项,如提供则作为附加筛选条件 + save_path: 保存路径,可选项,如提供则同时更新保存路径 Returns: 更新的记录数量 @@ -97,11 +106,17 @@ class RecordDB: if result: record_id = result[0] - # 更新记录 - cursor.execute( - "UPDATE transfer_records SET renamed_to = ? WHERE id = ?", - (renamed_to, record_id) - ) + # 根据是否提供save_path决定更新哪些字段 + if save_path: + cursor.execute( + "UPDATE transfer_records SET renamed_to = ?, save_path = ? WHERE id = ?", + (renamed_to, save_path, record_id) + ) + else: + cursor.execute( + "UPDATE transfer_records SET renamed_to = ? WHERE id = ?", + (renamed_to, record_id) + ) self.conn.commit() return cursor.rowcount @@ -124,7 +139,7 @@ class RecordDB: # 构建SQL查询 valid_columns = ["transfer_time", "task_name", "original_name", "renamed_to", - "file_size", "duration", "resolution", "modify_date"] + "file_size", "duration", "resolution", "modify_date", "save_path"] if sort_by not in valid_columns: sort_by = "transfer_time" @@ -140,7 +155,8 @@ class RecordDB: params.append(task_name_filter) if keyword_filter: - where_clauses.append("task_name LIKE ?") + where_clauses.append("(task_name LIKE ? OR original_name LIKE ?)") + params.append(f"%{keyword_filter}%") params.append(f"%{keyword_filter}%") where_clause = " AND ".join(where_clauses) @@ -192,4 +208,33 @@ class RecordDB: cursor = self.conn.cursor() cursor.execute("DELETE FROM transfer_records WHERE id = ?", (record_id,)) self.conn.commit() - return cursor.rowcount \ No newline at end of file + return cursor.rowcount + + def get_records_by_save_path(self, save_path, include_subpaths=False): + """根据保存路径查询记录 + + Args: + save_path: 要查询的保存路径 + include_subpaths: 是否包含子路径下的文件 + + Returns: + 匹配的记录列表 + """ + cursor = self.conn.cursor() + + if include_subpaths: + # 如果包含子路径,使用LIKE查询 + query = "SELECT * FROM transfer_records WHERE save_path LIKE ? ORDER BY transfer_time DESC" + cursor.execute(query, [f"{save_path}%"]) + else: + # 精确匹配路径 + query = "SELECT * FROM transfer_records WHERE save_path = ? ORDER BY transfer_time DESC" + cursor.execute(query, [save_path]) + + records = cursor.fetchall() + + # 将结果转换为字典列表 + if records: + columns = [col[0] for col in cursor.description] + return [dict(zip(columns, row)) for row in records] + return [] \ No newline at end of file diff --git a/app/static/css/main.css b/app/static/css/main.css index 89c394a..9ae9200 100644 --- a/app/static/css/main.css +++ b/app/static/css/main.css @@ -1633,10 +1633,11 @@ button.close:focus, /* 操作列 - 固定宽度 */ #fileSelectModal .table .col-action { - width: 53px; - min-width: 53px; - max-width: 53px; - text-align: center; + width: 188px; + min-width: 188px; + max-width: 188px; + text-align: left; + padding-left: 12px !important; } /* 确保单元格内容溢出时正确显示 */ @@ -3576,11 +3577,11 @@ input::-moz-list-button { /* 针对选择保存到的文件夹模式 - 带操作列的表格 */ #fileSelectModal[data-modal-type="target"] .breadcrumb { - min-width: 513px; /* 4列表格总宽度: 230px + 90px + 140px + 53px */ + min-width: 648px; /* 4列表格总宽度: 230px + 90px + 140px + 188px */ } #fileSelectModal[data-modal-type="target"] .table { - width: 513px; + width: 648px; } /* 针对命名预览模式 - 2列表格 */ diff --git a/app/templates/index.html b/app/templates/index.html index 7c54034..936284e 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -953,7 +953,8 @@ {{file.size | size}} {{file.updated_at | ts2date}} - 删除 + 删除文件 + 删除文件和记录 @@ -2290,12 +2291,23 @@ this.$delete(this.formData.magic_regex, key); } }, - deleteFile(fid, fname, isDir) { - if (!confirm(`确定要删除此项目吗?`)) { + deleteFile(fid, fname, isDir, deleteRecords = false) { + // 根据是否删除记录显示不同的确认提示 + let confirmMessage = deleteRecords + ? `确定要删除此项目及其关联记录吗?` + : `确定要删除此项目吗?`; + + if (!confirm(confirmMessage)) { return; } - axios.post('/delete_file', { fid: fid }) + // 获取当前路径作为save_path参数 + let save_path = ""; + if (this.fileSelect && this.fileSelect.paths) { + save_path = this.fileSelect.paths.map(item => item.name).join("/"); + } + + axios.post('/delete_file', { fid: fid, file_name: fname, delete_records: deleteRecords, save_path: save_path }) .then(response => { if (response.data.code === 0) { // 从列表中移除文件 @@ -2311,7 +2323,18 @@ } } - this.showToast('成功删除 1 个项目'); + // 显示成功消息,根据是否删除记录显示不同的消息 + if (deleteRecords) { + const deletedRecords = response.data.deleted_records || 0; + this.showToast(`成功删除 1 个项目${deletedRecords > 0 ? `及其关联的 ${deletedRecords} 条记录` : ''}`); + } else { + this.showToast('成功删除 1 个项目'); + } + + // 如果同时删除了记录,无论当前在哪个页面,都刷新历史记录 + if (deleteRecords) { + this.loadHistoryRecords(); + } } else { alert('删除失败: ' + response.data.message); } @@ -3403,36 +3426,53 @@ // 阻止事件冒泡 event.stopPropagation(); }, - deleteSelectedFiles(clickedFid, clickedFname, isDir) { + deleteSelectedFiles(clickedFid, clickedFname, isDir, deleteRecords = false) { // 如果是文件夹或者没有选中的文件,则按原来的方式删除单个文件 if (isDir || this.fileSelect.selectedFiles.length === 0) { - this.deleteFile(clickedFid, clickedFname, isDir); + this.deleteFile(clickedFid, clickedFname, isDir, deleteRecords); return; } // 如果点击的文件不在选中列表中,也按原来的方式删除单个文件 if (!this.fileSelect.selectedFiles.includes(clickedFid)) { - this.deleteFile(clickedFid, clickedFname, isDir); + this.deleteFile(clickedFid, clickedFname, isDir, deleteRecords); return; } // 多选删除 const selectedCount = this.fileSelect.selectedFiles.length; - // 根据选中数量使用不同的确认提示 - let confirmMessage = selectedCount === 1 - ? `确定要删除此项目吗?` - : `确定要删除选中的 ${selectedCount} 个项目吗?`; + // 根据选中数量和是否删除记录使用不同的确认提示 + let confirmMessage = ''; + if (deleteRecords) { + confirmMessage = selectedCount === 1 + ? `确定要删除此项目及其关联记录吗?` + : `确定要删除选中的 ${selectedCount} 个项目及其关联记录吗?`; + } else { + confirmMessage = selectedCount === 1 + ? `确定要删除此项目吗?` + : `确定要删除选中的 ${selectedCount} 个项目吗?`; + } if (confirm(confirmMessage)) { + // 获取当前路径作为save_path参数 + let save_path = ""; + if (this.fileSelect && this.fileSelect.paths) { + save_path = this.fileSelect.paths.map(item => item.name).join("/"); + } + // 创建一个Promise数组来处理所有删除请求 const deletePromises = this.fileSelect.selectedFiles.map(fid => { - return axios.post('/delete_file', { fid: fid }) + // 查找对应的文件对象,获取文件名 + const fileObj = this.fileSelect.fileList.find(file => file.fid === fid); + const fileName = fileObj ? fileObj.file_name : ''; + + return axios.post('/delete_file', { fid: fid, file_name: fileName, delete_records: deleteRecords, save_path: save_path }) .then(response => { - return { fid: fid, success: response.data.code === 0 }; + return { fid: fid, success: response.data.code === 0, deleted_records: response.data.deleted_records || 0 }; }) .catch(error => { - return { fid: fid, success: false }; + return { fid: fid, success: false, deleted_records: 0 }; }); }); @@ -3442,6 +3482,8 @@ // 统计成功和失败的数量 const successCount = results.filter(r => r.success).length; const failCount = results.length - successCount; + // 统计删除的记录数 + const totalDeletedRecords = results.reduce((sum, r) => sum + (r.deleted_records || 0), 0); // 从文件列表中移除成功删除的文件 const successfullyDeletedFids = results.filter(r => r.success).map(r => r.fid); @@ -3455,7 +3497,17 @@ if (failCount > 0) { alert(`成功删除 ${successCount} 个项目,${failCount} 个项目删除失败`); } else { - this.showToast(`成功删除 ${successCount} 个项目`); + // 根据是否删除记录显示不同的消息 + if (deleteRecords) { + this.showToast(`成功删除 ${successCount} 个项目${totalDeletedRecords > 0 ? `及其关联的 ${totalDeletedRecords} 条记录` : ''}`); + } else { + this.showToast(`成功删除 ${successCount} 个项目`); + } + } + + // 如果同时删除了记录,无论当前在哪个页面都刷新历史记录 + if (deleteRecords) { + this.loadHistoryRecords(); } }); } diff --git a/quark_auto_save.py b/quark_auto_save.py index 6c9e28a..0bb496c 100644 --- a/quark_auto_save.py +++ b/quark_auto_save.py @@ -1146,6 +1146,19 @@ class Quark: # 目前只是添加占位符,未来可以扩展功能 pass + # 获取保存路径 + save_path = task.get("savepath", "") + # 如果file_info中有子目录路径信息,则拼接完整路径 + subdir_path = file_info.get("subdir_path", "") + if subdir_path: + # 确保路径格式正确,避免双斜杠 + if save_path.endswith('/') and subdir_path.startswith('/'): + save_path = save_path + subdir_path[1:] + elif not save_path.endswith('/') and not subdir_path.startswith('/'): + save_path = save_path + '/' + subdir_path + else: + save_path = save_path + subdir_path + # 添加记录到数据库 db.add_record( task_name=task.get("taskname", ""), @@ -1156,7 +1169,8 @@ class Quark: duration=duration, resolution=resolution, file_id=file_id, - file_type=file_type + file_type=file_type, + save_path=save_path ) # 关闭数据库连接 @@ -1195,12 +1209,26 @@ class Quark: file_id = file_info.get("fid", "") task_name = task.get("taskname", "") + # 获取保存路径 + save_path = task.get("savepath", "") + # 如果file_info中有子目录路径信息,则拼接完整路径 + subdir_path = file_info.get("subdir_path", "") + if subdir_path: + # 确保路径格式正确,避免双斜杠 + if save_path.endswith('/') and subdir_path.startswith('/'): + save_path = save_path + subdir_path[1:] + elif not save_path.endswith('/') and not subdir_path.startswith('/'): + save_path = save_path + '/' + subdir_path + else: + save_path = save_path + subdir_path + # 更新记录 updated = db.update_renamed_to( file_id=file_id, original_name=original_name, renamed_to=renamed_to, - task_name=task_name + task_name=task_name, + save_path=save_path ) # 关闭数据库连接 @@ -1255,12 +1283,17 @@ class Quark: # 使用原文件名和任务名查找记录 task_name = task.get("taskname", "") + # 获取保存路径 + save_path = task.get("savepath", "") + # 注意:从日志中无法获取子目录信息,只能使用任务的主保存路径 + # 更新记录 updated = db.update_renamed_to( file_id="", # 不使用file_id查询,因为在日志中无法获取 original_name=old_name, renamed_to=new_name, - task_name=task_name + task_name=task_name, + save_path=save_path ) # 关闭数据库连接