From e7d8666dcb1cfd24a29e6df5d1d84f221230c001 Mon Sep 17 00:00:00 2001 From: x1ao4 Date: Sun, 6 Jul 2025 02:59:47 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E9=80=89=E6=8B=A9=E8=B5=B7?= =?UTF-8?q?=E5=A7=8B=E6=96=87=E4=BB=B6=E6=A8=A1=E6=80=81=E6=A1=86=E7=9A=84?= =?UTF-8?q?=E6=8E=92=E5=BA=8F=E6=96=B9=E5=BC=8F=EF=BC=8C=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E8=B5=B7=E5=A7=8B=E6=96=87=E4=BB=B6=E5=A4=B1=E6=95=88=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/static/js/sort_file_by_name.js | 24 +++- app/templates/index.html | 61 ++++++-- quark_auto_save.py | 224 +++++++++++++++++++++++++---- 3 files changed, 266 insertions(+), 43 deletions(-) diff --git a/app/static/js/sort_file_by_name.js b/app/static/js/sort_file_by_name.js index c2e2708..21c03c0 100644 --- a/app/static/js/sort_file_by_name.js +++ b/app/static/js/sort_file_by_name.js @@ -87,11 +87,15 @@ function sortFileByName(file) { } // MM-DD if (date_value === Infinity) { - match = filename.match(/(? 12) [month, day] = [day, month]; - date_value = 20000000 + month * 100 + day; + // 验证是否为有效的月日组合 + if ((month >= 1 && month <= 12 && day >= 1 && day <= 31) || + (day >= 1 && day <= 12 && month >= 1 && month <= 31)) { + if (month > 12) [month, day] = [day, month]; + date_value = 20000000 + month * 100 + day; + } } } @@ -145,7 +149,19 @@ function sortFileByName(file) { if (/^\d+$/.test(file_name_without_ext)) { episode_value = parseInt(file_name_without_ext); } else { - match = filename.match(/(\d+)/); + // 预处理:移除分辨率标识(如 720p, 1080P, 2160p 等) + let filename_without_resolution = filename; + const resolution_patterns = [ + /\b\d+[pP]\b/g, // 匹配 720p, 1080P, 2160p 等 + /\b\d+x\d+\b/g, // 匹配 1920x1080 等 + // 注意:不移除4K/8K,因为剧集匹配规则中有 (\d+)[-_\s]*4[Kk] 模式 + ]; + + for (const pattern of resolution_patterns) { + filename_without_resolution = filename_without_resolution.replace(pattern, ' '); + } + + match = filename_without_resolution.match(/(\d+)/); if (match) episode_value = parseInt(match[1]); } } diff --git a/app/templates/index.html b/app/templates/index.html index 56e2757..03da261 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -4447,20 +4447,32 @@ // 按选定字段和顺序对文件列表进行排序 this.fileSelect.fileList.sort((a, b) => { if (field === 'file_name') { - // 文件夹始终在前 - if (a.dir && !b.dir) return -1; - if (!a.dir && b.dir) return 1; - // 检查当前模态框类型,选择起始文件模态框使用全局文件排序函数 const modalType = document.getElementById('fileSelectModal').getAttribute('data-modal-type'); if (modalType === 'start-file') { - // 选择起始文件模态框:使用全局文件排序函数 + // 选择起始文件模态框:按过滤状态和类型排序 + // 1. 检查过滤状态 - 包括没有file_name_re字段的情况 + const aFiltered = !a.file_name_re || a.file_name_re === '×' || a.file_name_re.startsWith('×'); + const bFiltered = !b.file_name_re || b.file_name_re === '×' || b.file_name_re.startsWith('×'); + + // 无论升序还是降序,未被过滤的项目都排在前面,被过滤的项目排在后面 + if (!aFiltered && bFiltered) return -1; + if (aFiltered && !bFiltered) return 1; + + // 2. 在同一过滤状态内,文件夹排在文件前面 + if (a.dir && !b.dir) return -1; + if (!a.dir && b.dir) return 1; + + // 使用全局文件排序函数 const ka = sortFileByName(a), kb = sortFileByName(b); for (let i = 0; i < ka.length; ++i) { if (ka[i] !== kb[i]) return this.fileSelect.sortOrder === 'asc' ? (ka[i] > kb[i] ? 1 : -1) : (ka[i] < kb[i] ? 1 : -1); } return 0; } else { + // 其他模态框:文件夹排在文件前面 + if (a.dir && !b.dir) return -1; + if (!a.dir && b.dir) return 1; // 其他模态框:使用拼音排序 let aValue = pinyinPro.pinyin(a.file_name, { toneType: 'none', type: 'string' }).toLowerCase(); let bValue = pinyinPro.pinyin(b.file_name, { toneType: 'none', type: 'string' }).toLowerCase(); @@ -4568,14 +4580,39 @@ manualSortFileList(field, order) { this.fileSelect.fileList.sort((a, b) => { if (field === 'file_name') { - if (a.dir && !b.dir) return -1; - if (!a.dir && b.dir) return 1; - // 使用智能排序函数 - const ka = sortFileByName(a), kb = sortFileByName(b); - for (let i = 0; i < ka.length; ++i) { - if (ka[i] !== kb[i]) return order === 'asc' ? (ka[i] > kb[i] ? 1 : -1) : (ka[i] < kb[i] ? 1 : -1); + // 检查当前模态框类型,选择起始文件模态框使用特殊排序逻辑 + const modalType = document.getElementById('fileSelectModal').getAttribute('data-modal-type'); + if (modalType === 'start-file') { + // 选择起始文件模态框:按过滤状态和类型排序 + // 1. 检查过滤状态 - 包括没有file_name_re字段的情况 + const aFiltered = !a.file_name_re || a.file_name_re === '×' || a.file_name_re.startsWith('×'); + const bFiltered = !b.file_name_re || b.file_name_re === '×' || b.file_name_re.startsWith('×'); + + // 无论升序还是降序,未被过滤的项目都排在前面,被过滤的项目排在后面 + if (!aFiltered && bFiltered) return -1; + if (aFiltered && !bFiltered) return 1; + + // 2. 在同一过滤状态内,文件夹排在文件前面 + if (a.dir && !b.dir) return -1; + if (!a.dir && b.dir) return 1; + + // 使用全局文件排序函数 + const ka = sortFileByName(a), kb = sortFileByName(b); + for (let i = 0; i < ka.length; ++i) { + if (ka[i] !== kb[i]) return order === 'asc' ? (ka[i] > kb[i] ? 1 : -1) : (ka[i] < kb[i] ? 1 : -1); + } + return 0; + } else { + // 其他模态框:文件夹排在文件前面 + if (a.dir && !b.dir) return -1; + if (!a.dir && b.dir) return 1; + // 使用智能排序函数 + const ka = sortFileByName(a), kb = sortFileByName(b); + for (let i = 0; i < ka.length; ++i) { + if (ka[i] !== kb[i]) return order === 'asc' ? (ka[i] > kb[i] ? 1 : -1) : (ka[i] < kb[i] ? 1 : -1); + } + return 0; } - return 0; } if (field === 'file_name_re') { const aHasValidRename = a.file_name_re && a.file_name_re !== '×' && !a.file_name_re.startsWith('×'); diff --git a/quark_auto_save.py b/quark_auto_save.py index 7f67877..8d9c93d 100644 --- a/quark_auto_save.py +++ b/quark_auto_save.py @@ -128,19 +128,22 @@ def sort_file_by_name(file): month, day = day, month date_value = year * 10000 + month * 100 + day - # 1.6 MM-DD 或 MM.DD 或 MM/DD 或 MM DD格式(无年份) + # 1.6 MM-DD 或 MM.DD 或 MM/DD格式(无年份,不包括空格分隔) if date_value == float('inf'): - match_date_short = re.search(r'(? 12: - month, day = day, month - # 由于没有年份,使用一个较低的基数,确保任何有年份的日期都排在前面 - # 使用20000000作为基准,所以无年份日期都会排在有年份日期之后 - date_value = 20000000 + month * 100 + day + # 验证是否为有效的月日组合 + if ((month >= 1 and month <= 12 and day >= 1 and day <= 31) or + (day >= 1 and day <= 12 and month >= 1 and month <= 31)): + # 检查月份值,如果大于12可能是欧式日期格式(DD/MM) + if month > 12: + month, day = day, month + # 由于没有年份,使用一个较低的基数,确保任何有年份的日期都排在前面 + # 使用20000000作为基准,所以无年份日期都会排在有年份日期之后 + date_value = 20000000 + month * 100 + day # 2. 提取期数/集数 - 第二级排序键 @@ -207,8 +210,19 @@ def sort_file_by_name(file): if file_name_without_ext.isdigit(): episode_value = int(file_name_without_ext) else: + # 预处理:移除分辨率标识(如 720p, 1080P, 2160p 等) + filename_without_resolution = filename + resolution_patterns = [ + r'\b\d+[pP]\b', # 匹配 720p, 1080P, 2160p 等 + r'\b\d+x\d+\b', # 匹配 1920x1080 等 + # 注意:不移除4K/8K,因为剧集匹配规则中有 (\d+)[-_\s]*4[Kk] 模式 + ] + + for pattern in resolution_patterns: + filename_without_resolution = re.sub(pattern, ' ', filename_without_resolution) + # 否则尝试提取任何数字 - any_num_match = re.search(r'(\d+)', filename) + any_num_match = re.search(r'(\d+)', filename_without_resolution) if any_num_match: episode_value = int(any_num_match.group(1)) @@ -252,8 +266,8 @@ def extract_episode_number(filename, episode_patterns=None, config_data=None): r'(?= 0: - # 只处理起始文件之前的文件(不包括起始文件本身) + # 只处理起始文件之前的文件(比起始文件更新的文件,不包括起始文件本身) share_file_list = share_file_list[:start_index] # 添加符合的 @@ -2967,8 +3133,12 @@ class Quark: if start_fid: if share_file["fid"] == start_fid: start_file_found = True - break # 找到起始文件,停止遍历 - # 如果还没找到起始文件,继续添加到转存列表 + # 找到起始文件,但不包含起始文件本身,只处理比它更新的文件 + continue + elif start_file_found: + # 已经找到起始文件,跳过后续(更旧的)文件 + continue + # 如果还没找到起始文件,说明当前文件比起始文件更新,需要处理 else: # 没有设置起始文件,处理所有文件 pass