增加拼音排序支持,修复文件夹无法进行大小排序的问题

This commit is contained in:
x1ao4 2025-07-01 02:52:11 +08:00
parent 28d68f1f71
commit 8933311072
7 changed files with 179 additions and 26 deletions

View File

@ -40,6 +40,14 @@ from quark_auto_save import Config, format_bytes
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 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 from quark_auto_save import extract_episode_number, sort_file_by_name, chinese_to_arabic, is_date_format
# 导入拼音排序工具
try:
from utils.pinyin_sort import get_filename_pinyin_sort_key
except ImportError:
# 如果导入失败,使用简单的小写排序作为备用
def get_filename_pinyin_sort_key(filename):
return filename.lower()
# 导入数据库模块 # 导入数据库模块
try: try:
# 先尝试相对导入 # 先尝试相对导入
@ -1581,9 +1589,11 @@ def get_file_list():
# 优化排序:使用更高效的排序方法 # 优化排序:使用更高效的排序方法
def get_sort_key(file_item): def get_sort_key(file_item):
if sort_by == "file_name": if sort_by == "file_name":
return file_item["file_name"].lower() # 使用拼音排序
return get_filename_pinyin_sort_key(file_item["file_name"])
elif sort_by == "file_size": elif sort_by == "file_size":
return file_item["size"] if not file_item["dir"] else 0 # 文件夹按项目数量排序,文件按大小排序
return file_item.get("include_items", 0) if file_item["dir"] else file_item["size"]
else: # updated_at else: # updated_at
return file_item["updated_at"] return file_item["updated_at"]

View File

@ -174,9 +174,34 @@ class RecordDB:
total_records = cursor.fetchone()[0] total_records = cursor.fetchone()[0]
# 获取分页数据 # 获取分页数据
query_sql = f"SELECT * FROM transfer_records {where_sql} ORDER BY {sort_by} {order_direction} LIMIT ? OFFSET ?" if sort_by in ["task_name", "original_name", "renamed_to"]:
cursor.execute(query_sql, params + [page_size, offset]) # 对于需要拼音排序的字段先获取所有数据然后在Python中进行拼音排序
records = cursor.fetchall() query_sql = f"SELECT * FROM transfer_records {where_sql}"
cursor.execute(query_sql, params)
all_records = cursor.fetchall()
# 使用拼音排序
from utils.pinyin_sort import get_filename_pinyin_sort_key
# 根据排序字段选择对应的索引
field_index_map = {
"task_name": 2, # task_name字段索引
"original_name": 3, # original_name字段索引
"renamed_to": 4 # renamed_to字段索引
}
field_index = field_index_map[sort_by]
sorted_records = sorted(all_records, key=lambda x: get_filename_pinyin_sort_key(x[field_index]), reverse=(order_direction == "DESC"))
# 手动分页
start_idx = offset
end_idx = offset + page_size
records = sorted_records[start_idx:end_idx]
else:
# 其他字段使用SQL排序
query_sql = f"SELECT * FROM transfer_records {where_sql} ORDER BY {sort_by} {order_direction} LIMIT ? OFFSET ?"
cursor.execute(query_sql, params + [page_size, offset])
records = cursor.fetchall()
# 将结果转换为字典列表 # 将结果转换为字典列表
columns = [col[0] for col in cursor.description] columns = [col[0] for col in cursor.description]

1
app/static/js/pinyin-pro.min.js vendored Executable file

File diff suppressed because one or more lines are too long

View File

@ -17,6 +17,7 @@
<script src="./static/js/axios.min.js"></script> <script src="./static/js/axios.min.js"></script>
<script src="./static/js/v-jsoneditor.min.js"></script> <script src="./static/js/v-jsoneditor.min.js"></script>
<script src="./static/js/sort_file_by_name.js"></script> <script src="./static/js/sort_file_by_name.js"></script>
<script src="./static/js/pinyin-pro.min.js"></script>
<script> <script>
// 添加检测文本溢出的自定义指令 // 添加检测文本溢出的自定义指令
Vue.directive('check-overflow', { Vue.directive('check-overflow', {
@ -1700,7 +1701,7 @@
} }
}); });
return [...taskNames].sort(); return this.sortTaskNamesByPinyin([...taskNames]);
}, },
taskNames() { taskNames() {
// 从任务列表中提取唯一的任务名称 // 从任务列表中提取唯一的任务名称
@ -1715,7 +1716,7 @@
} }
}); });
return [...taskNames].sort(); return this.sortTaskNamesByPinyin([...taskNames]);
}, },
totalPages() { totalPages() {
// 直接使用后端返回的total_pages // 直接使用后端返回的total_pages
@ -1953,6 +1954,14 @@
document.removeEventListener('click', this.handleOutsideClick); document.removeEventListener('click', this.handleOutsideClick);
}, },
methods: { methods: {
// 拼音排序辅助函数
sortTaskNamesByPinyin(taskNames) {
return taskNames.sort((a, b) => {
const aKey = pinyinPro.pinyin(a, { toneType: 'none', type: 'string' }).toLowerCase();
const bKey = pinyinPro.pinyin(b, { toneType: 'none', type: 'string' }).toLowerCase();
return aKey > bKey ? 1 : -1;
});
},
// 添加格式化分享链接警告信息的方法 // 添加格式化分享链接警告信息的方法
formatShareUrlBanMessage(message) { formatShareUrlBanMessage(message) {
if (!message) return message; if (!message) return message;
@ -3705,7 +3714,7 @@
} }
}).then(response => { }).then(response => {
if (response.data.success && response.data.data.all_task_names) { if (response.data.success && response.data.data.all_task_names) {
this.allTaskNames = response.data.data.all_task_names.sort(); this.allTaskNames = this.sortTaskNamesByPinyin(response.data.data.all_task_names);
} else { } else {
// 如果API失败回退到从当前页记录中提取任务名称 // 如果API失败回退到从当前页记录中提取任务名称
if (this.history.records && this.history.records.length > 0) { if (this.history.records && this.history.records.length > 0) {
@ -3715,7 +3724,7 @@
taskNames.add(record.task_name); taskNames.add(record.task_name);
} }
}); });
this.allTaskNames = [...taskNames].sort(); this.allTaskNames = this.sortTaskNamesByPinyin([...taskNames]);
} }
} }
}).catch(error => { }).catch(error => {
@ -3727,7 +3736,7 @@
taskNames.add(record.task_name); taskNames.add(record.task_name);
} }
}); });
this.allTaskNames = [...taskNames].sort(); this.allTaskNames = this.sortTaskNamesByPinyin([...taskNames]);
} }
}); });
}, },
@ -3859,8 +3868,9 @@
// 文件夹始终在前 // 文件夹始终在前
if (a.dir && !b.dir) return -1; if (a.dir && !b.dir) return -1;
if (!a.dir && b.dir) return 1; if (!a.dir && b.dir) return 1;
let aValue = a.file_name.toLowerCase(); // 使用拼音排序
let bValue = b.file_name.toLowerCase(); let aValue = pinyinPro.pinyin(a.file_name, { toneType: 'none', type: 'string' }).toLowerCase();
let bValue = pinyinPro.pinyin(b.file_name, { toneType: 'none', type: 'string' }).toLowerCase();
if (this.fileSelect.sortOrder === 'asc') { if (this.fileSelect.sortOrder === 'asc') {
return aValue > bValue ? 1 : -1; return aValue > bValue ? 1 : -1;
} else { } else {
@ -3887,9 +3897,9 @@
aValue = a.episode_number; aValue = a.episode_number;
bValue = b.episode_number; bValue = b.episode_number;
} else { } else {
// 否则使用重命名后的文件名进行字符串排序 // 否则使用重命名后的文件名进行拼音排序
aValue = (a.file_name_re || '').toLowerCase(); aValue = pinyinPro.pinyin(a.file_name_re || '', { toneType: 'none', type: 'string' }).toLowerCase();
bValue = (b.file_name_re || '').toLowerCase(); bValue = pinyinPro.pinyin(b.file_name_re || '', { toneType: 'none', type: 'string' }).toLowerCase();
} }
if (this.fileSelect.sortOrder === 'asc') { if (this.fileSelect.sortOrder === 'asc') {
@ -3934,8 +3944,9 @@
if (a.dir && !b.dir) return -1; if (a.dir && !b.dir) return -1;
if (!a.dir && b.dir) return 1; if (!a.dir && b.dir) return 1;
let aValue = a.dir ? 0 : (a.size || 0); // 文件夹按项目数量排序,文件按大小排序
let bValue = b.dir ? 0 : (b.size || 0); let aValue = a.dir ? (a.include_items || 0) : (a.size || 0);
let bValue = b.dir ? (b.include_items || 0) : (b.size || 0);
if (this.fileSelect.sortOrder === 'asc') { if (this.fileSelect.sortOrder === 'asc') {
return aValue > bValue ? 1 : -1; return aValue > bValue ? 1 : -1;
} else { } else {
@ -3958,8 +3969,9 @@
if (field === 'file_name') { if (field === 'file_name') {
if (a.dir && !b.dir) return -1; if (a.dir && !b.dir) return -1;
if (!a.dir && b.dir) return 1; if (!a.dir && b.dir) return 1;
let aValue = a.file_name.toLowerCase(); // 使用拼音排序
let bValue = b.file_name.toLowerCase(); let aValue = pinyinPro.pinyin(a.file_name, { toneType: 'none', type: 'string' }).toLowerCase();
let bValue = pinyinPro.pinyin(b.file_name, { toneType: 'none', type: 'string' }).toLowerCase();
if (order === 'asc') { if (order === 'asc') {
return aValue > bValue ? 1 : -1; return aValue > bValue ? 1 : -1;
} else { } else {
@ -3986,9 +3998,9 @@
aValue = a.episode_number; aValue = a.episode_number;
bValue = b.episode_number; bValue = b.episode_number;
} else { } else {
// 否则使用重命名后的文件名进行字符串排序 // 否则使用重命名后的文件名进行拼音排序
aValue = (a.file_name_re || '').toLowerCase(); aValue = pinyinPro.pinyin(a.file_name_re || '', { toneType: 'none', type: 'string' }).toLowerCase();
bValue = (b.file_name_re || '').toLowerCase(); bValue = pinyinPro.pinyin(b.file_name_re || '', { toneType: 'none', type: 'string' }).toLowerCase();
} }
if (order === 'asc') { if (order === 'asc') {
@ -4033,8 +4045,9 @@
if (a.dir && !b.dir) return -1; if (a.dir && !b.dir) return -1;
if (!a.dir && b.dir) return 1; if (!a.dir && b.dir) return 1;
let aValue = a.dir ? 0 : (a.size || 0); // 文件夹按项目数量排序,文件按大小排序
let bValue = b.dir ? 0 : (b.size || 0); let aValue = a.dir ? (a.include_items || 0) : (a.size || 0);
let bValue = b.dir ? (b.include_items || 0) : (b.size || 0);
if (order === 'asc') { if (order === 'asc') {
return aValue > bValue ? 1 : -1; return aValue > bValue ? 1 : -1;
} else { } else {

89
app/utils/pinyin_sort.py Normal file
View File

@ -0,0 +1,89 @@
# -*- coding: utf-8 -*-
"""
拼音排序工具模块
用于将中文字符串转换为拼音后进行排序
"""
try:
from pypinyin import lazy_pinyin, Style
PYPINYIN_AVAILABLE = True
except ImportError:
PYPINYIN_AVAILABLE = False
print("Warning: pypinyin not available, falling back to simple string sort")
def to_pinyin_for_sort(text):
"""
将字符串转换为拼音用于排序
保留非中文字符只转换中文字符为拼音
Args:
text (str): 要转换的字符串
Returns:
str: 转换后的拼音字符串用于排序比较
"""
if not text:
return ''
if PYPINYIN_AVAILABLE:
# 使用pypinyin库进行转换
# Style.NORMAL: 普通风格,不带声调
# errors='default': 保留无法转换的字符
pinyin_list = lazy_pinyin(text, style=Style.NORMAL, errors='default')
return ''.join(pinyin_list).lower()
else:
# 如果pypinyin不可用直接返回小写字符串
return text.lower()
def pinyin_compare(a, b):
"""
拼音排序比较函数
Args:
a (str): 第一个字符串
b (str): 第二个字符串
Returns:
int: 比较结果 (-1, 0, 1)
"""
pinyin_a = to_pinyin_for_sort(a)
pinyin_b = to_pinyin_for_sort(b)
if pinyin_a < pinyin_b:
return -1
elif pinyin_a > pinyin_b:
return 1
else:
return 0
def get_filename_pinyin_sort_key(filename):
"""
为文件名生成拼音排序键
保持完整内容只将汉字转换为拼音
Args:
filename (str): 文件名
Returns:
str: 用于排序的拼音字符串
"""
return to_pinyin_for_sort(filename)
def pinyin_sort_files(files, key_func=None, reverse=False):
"""
使用拼音排序对文件列表进行排序
Args:
files (list): 文件列表
key_func (callable): 从文件对象中提取文件名的函数默认为None假设files是字符串列表
reverse (bool): 是否逆序排序
Returns:
list: 排序后的文件列表
"""
if key_func is None:
# 假设files是字符串列表
return sorted(files, key=to_pinyin_for_sort, reverse=reverse)
else:
# 使用key_func提取文件名后进行拼音排序
return sorted(files, key=lambda x: to_pinyin_for_sort(key_func(x)), reverse=reverse)

View File

@ -8,6 +8,7 @@ import time
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
try: try:
from quark_auto_save import sort_file_by_name from quark_auto_save import sort_file_by_name
from app.utils.pinyin_sort import get_filename_pinyin_sort_key
except ImportError: except ImportError:
# 如果无法导入,提供一个简单的排序函数作为替代 # 如果无法导入,提供一个简单的排序函数作为替代
def sort_file_by_name(file): def sort_file_by_name(file):
@ -15,8 +16,21 @@ except ImportError:
filename = file.get("file_name", "") filename = file.get("file_name", "")
else: else:
filename = file filename = file
# 简单排序,主要通过文件名进行 # 简单排序,主要通过文件名进行(使用拼音排序)
return filename try:
from pypinyin import lazy_pinyin, Style
pinyin_list = lazy_pinyin(filename, style=Style.NORMAL, errors='ignore')
return ''.join(pinyin_list).lower()
except ImportError:
return filename.lower()
def get_filename_pinyin_sort_key(filename):
try:
from pypinyin import lazy_pinyin, Style
pinyin_list = lazy_pinyin(filename, style=Style.NORMAL, errors='ignore')
return ''.join(pinyin_list).lower()
except ImportError:
return filename.lower()
class Aria2: class Aria2:

View File

@ -2,3 +2,4 @@ flask
apscheduler apscheduler
requests requests
treelib treelib
pypinyin