diff --git a/app/run.py b/app/run.py index 2392e3c..37abac0 100644 --- a/app/run.py +++ b/app/run.py @@ -40,6 +40,35 @@ from quark_auto_save import Config, format_bytes sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from quark_auto_save import extract_episode_number, sort_file_by_name, chinese_to_arabic, is_date_format + +def process_season_episode_info(filename): + """ + 处理文件名中的季数和集数信息 + + Args: + filename: 文件名(不含扩展名) + + Returns: + 处理后的显示名称 + """ + # 匹配 SxxExx 格式(不区分大小写) + # 支持 S1E1, S01E01, s13e10 等格式 + season_episode_match = re.search(r'[Ss](\d{1,2})[Ee](\d{1,3})', filename) + if season_episode_match: + season = season_episode_match.group(1).zfill(2) # 确保两位数 + episode = season_episode_match.group(2).zfill(2) # 确保两位数 + return f"S{season}E{episode}" + + # 匹配只有 Exx 或 EPxx 格式(不区分大小写) + # 支持 E1, E01, EP1, EP01, e10, ep10 等格式 + episode_only_match = re.search(r'[Ee][Pp]?(\d{1,3})', filename) + if episode_only_match: + episode = episode_only_match.group(1).zfill(2) # 确保两位数 + return f"E{episode}" + + # 如果没有匹配到季数集数信息,返回原文件名 + return filename + # 导入拼音排序工具 try: from utils.pinyin_sort import get_filename_pinyin_sort_key @@ -1364,18 +1393,18 @@ def delete_history_records(): }) -# 获取任务最新转存记录日期 -@app.route("/task_latest_records") -def get_task_latest_records(): +# 获取任务最新转存信息(包括日期和文件) +@app.route("/task_latest_info") +def get_task_latest_info(): if not is_login(): return jsonify({"success": False, "message": "未登录"}) try: # 初始化数据库 db = RecordDB() - - # 获取所有任务的最新转存记录 cursor = db.conn.cursor() + + # 获取所有任务的最新转存时间 query = """ SELECT task_name, MAX(transfer_time) as latest_transfer_time FROM transfer_records @@ -1383,13 +1412,14 @@ def get_task_latest_records(): GROUP BY task_name """ cursor.execute(query) - results = cursor.fetchall() + latest_times = cursor.fetchall() - # 转换为字典格式 - task_latest_records = {} - for row in results: - task_name, latest_time = row + task_latest_records = {} # 存储最新转存日期 + task_latest_files = {} # 存储最新转存文件 + + for task_name, latest_time in latest_times: if latest_time: + # 1. 处理最新转存日期 try: # 确保时间戳在合理范围内 timestamp = int(latest_time) @@ -1404,20 +1434,67 @@ def get_task_latest_records(): except (ValueError, TypeError, OverflowError): pass # 忽略无效的时间戳 + # 2. 处理最新转存文件 + # 获取该任务在最新转存时间附近(同一分钟内)的所有文件 + # 这样可以处理同时转存多个文件但时间戳略有差异的情况 + time_window = 60000 # 60秒的时间窗口(毫秒) + query = """ + SELECT renamed_to, original_name, transfer_time, modify_date + FROM transfer_records + WHERE task_name = ? AND transfer_time >= ? AND transfer_time <= ? + ORDER BY id DESC + """ + cursor.execute(query, (task_name, latest_time - time_window, latest_time + time_window)) + files = cursor.fetchall() + + if files: + if len(files) == 1: + # 如果只有一个文件,直接使用 + best_file = files[0][0] # renamed_to + else: + # 如果有多个文件,使用全局排序函数进行排序 + file_list = [] + for renamed_to, original_name, transfer_time, modify_date in files: + # 构造文件信息字典,模拟全局排序函数需要的格式 + file_info = { + 'file_name': renamed_to, + 'original_name': original_name, + 'updated_at': transfer_time # 使用转存时间而不是文件修改时间 + } + file_list.append(file_info) + + # 使用全局排序函数进行正向排序,最后一个就是最新的 + try: + sorted_files = sorted(file_list, key=sort_file_by_name) + best_file = sorted_files[-1]['file_name'] # 取排序后的最后一个文件 + except Exception as e: + # 如果排序失败,使用第一个文件作为备选 + best_file = files[0][0] + + # 去除扩展名并处理季数集数信息 + if best_file: + file_name_without_ext = os.path.splitext(best_file)[0] + processed_name = process_season_episode_info(file_name_without_ext) + task_latest_files[task_name] = processed_name + db.close() return jsonify({ "success": True, - "data": task_latest_records + "data": { + "latest_records": task_latest_records, + "latest_files": task_latest_files + } }) except Exception as e: return jsonify({ "success": False, - "message": f"获取任务最新记录失败: {str(e)}" + "message": f"获取任务最新信息失败: {str(e)}" }) + # 删除单条转存记录 @app.route("/delete_history_record", methods=["POST"]) def delete_history_record(): diff --git a/app/static/css/main.css b/app/static/css/main.css index 27e6fe9..f2f41f6 100644 --- a/app/static/css/main.css +++ b/app/static/css/main.css @@ -4060,17 +4060,25 @@ table.selectable-records .expand-button:hover { color: var(--dark-text-color); font-size: 0.95rem; font-weight: normal; - opacity: 1; - transition: opacity 0.2s ease; } -/* 悬停显示模式 */ -.task .btn:not(:hover) .task-latest-date.hover-only { - opacity: 0; +/* 任务最近转存文件样式 */ +.task-latest-file { + color: var(--dark-text-color); + font-size: 0.95rem; + font-weight: normal; } -.task .btn:hover .task-latest-date.hover-only { - opacity: 1; +/* 悬停显示模式 - 使用display来避免占用空间,同时保持平滑的悬停效果 */ +.task-latest-date.hover-only, +.task-latest-file.hover-only { + display: none; + transition: all 0.2s ease; +} + +.task .btn:hover .task-latest-date.hover-only, +.task .btn:hover .task-latest-file.hover-only { + display: inline; } /* 显示设置行样式 */ diff --git a/app/templates/index.html b/app/templates/index.html index 41dfb67..d445fbd 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -718,6 +718,18 @@ +
+
+
+ 最近转存文件 +
+ +
+
@@ -800,6 +812,11 @@ :class="{'hover-only': formData.button_display.latest_update_date === 'hover'}"> · {{ taskLatestRecords[task.taskname] }} + + · {{ taskLatestFiles[task.taskname] }} +
@@ -1633,7 +1650,8 @@ delete_task: "always", refresh_plex: "always", refresh_alist: "always", - latest_update_date: "always" + latest_update_date: "always", + latest_transfer_file: "always" }, file_performance: { api_page_size: 200, @@ -1663,6 +1681,7 @@ taskDirSelected: "", taskNameFilter: "", taskLatestRecords: {}, // 存储每个任务的最新转存记录日期 + taskLatestFiles: {}, // 存储每个任务的最近转存文件 modalLoading: false, smart_param: { index: null, @@ -2560,13 +2579,18 @@ delete_task: "always", refresh_plex: "always", refresh_alist: "always", - latest_update_date: "always" + latest_update_date: "always", + latest_transfer_file: "always" }; } // 确保最近更新日期配置存在(向后兼容) if (!config_data.button_display.latest_update_date) { config_data.button_display.latest_update_date = "always"; } + // 确保最近转存文件配置存在(向后兼容) + if (!config_data.button_display.latest_transfer_file) { + config_data.button_display.latest_transfer_file = "always"; + } // 确保文件整理性能配置存在 if (!config_data.file_performance) { config_data.file_performance = { @@ -2590,8 +2614,8 @@ this.configModified = false; }, 100); - // 加载任务最新记录 - this.loadTaskLatestRecords(); + // 加载任务最新信息(包括记录和文件) + this.loadTaskLatestInfo(); // 数据加载完成后检查分享链接状态 if (this.activeTab === 'tasklist') { @@ -4167,18 +4191,19 @@ } }); }, - loadTaskLatestRecords() { - // 获取所有任务的最新转存记录日期 - axios.get('/task_latest_records') + loadTaskLatestInfo() { + // 获取所有任务的最新转存信息(包括日期和文件) + axios.get('/task_latest_info') .then(response => { if (response.data.success) { - this.taskLatestRecords = response.data.data; + this.taskLatestRecords = response.data.data.latest_records; + this.taskLatestFiles = response.data.data.latest_files; } else { - console.error('获取任务最新记录失败:', response.data.message); + console.error('获取任务最新信息失败:', response.data.message); } }) .catch(error => { - console.error('获取任务最新记录失败:', error); + console.error('获取任务最新信息失败:', error); }); }, openDatePicker(index) {