mirror of
https://github.com/Cp0204/quark-auto-save.git
synced 2026-01-12 15:20:44 +08:00
在任务列表页面新增显示最近转存文件功能并优化 API 性能
- 在任务名称后显示最新转存文件名(格式:· 文件名) - 新增显示设置选项:始终显示/悬停显示/禁用 - 合并 /task_latest_records 和 /task_latest_files 为统一的 /task_latest_info API - 自动提取季数集数信息显示(如:乘风2025 - S06E10 → S06E10) - 样式与最近更新日期保持一致,支持悬停交互 - 完全向后兼容,不影响现有功能和配置
This commit is contained in:
parent
08e5a2f6e8
commit
d61c0ee9cd
101
app/run.py
101
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():
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
/* 显示设置行样式 */
|
||||
|
||||
@ -718,6 +718,18 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3 col-md-6 mb-2">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">最近转存文件</span>
|
||||
</div>
|
||||
<select class="form-control" v-model="formData.button_display.latest_transfer_file">
|
||||
<option value="always">始终显示</option>
|
||||
<option value="hover">悬停显示</option>
|
||||
<option value="disabled">禁用</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 性能设置 -->
|
||||
@ -800,6 +812,11 @@
|
||||
:class="{'hover-only': formData.button_display.latest_update_date === 'hover'}">
|
||||
· {{ taskLatestRecords[task.taskname] }}
|
||||
</span>
|
||||
<span v-if="formData.button_display.latest_transfer_file !== 'disabled' && taskLatestFiles[task.taskname]"
|
||||
class="task-latest-file"
|
||||
:class="{'hover-only': formData.button_display.latest_transfer_file === 'hover'}">
|
||||
· {{ taskLatestFiles[task.taskname] }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto task-buttons">
|
||||
@ -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) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user