mirror of
https://github.com/Cp0204/quark-auto-save.git
synced 2026-01-14 00:10:43 +08:00
新增剧集命名模式
This commit is contained in:
parent
0c917d12ad
commit
f82121b7dc
86
app/run.py
86
app/run.py
@ -429,6 +429,92 @@ def get_share_detail():
|
||||
file["file_name_re"] = sequence_pattern.replace("{}", f"{current_sequence:02d}") + file_ext
|
||||
current_sequence += 1
|
||||
|
||||
return share_detail
|
||||
elif regex.get("use_episode_naming") and regex.get("episode_naming"):
|
||||
# 剧集命名模式预览
|
||||
episode_pattern = regex.get("episode_naming")
|
||||
episode_patterns = regex.get("episode_patterns", [])
|
||||
|
||||
# 实现序号提取函数
|
||||
def extract_episode_number(filename):
|
||||
# 优先匹配SxxExx格式
|
||||
match_s_e = re.search(r'[Ss](\d+)[Ee](\d+)', filename)
|
||||
if match_s_e:
|
||||
# 直接返回E后面的集数
|
||||
return int(match_s_e.group(2))
|
||||
|
||||
# 尝试使用每个配置的正则表达式匹配文件名
|
||||
for pattern in episode_patterns:
|
||||
try:
|
||||
pattern_regex = pattern.get("regex", "(\\d+)")
|
||||
match = re.search(pattern_regex, filename)
|
||||
if match:
|
||||
return int(match.group(1))
|
||||
except Exception as e:
|
||||
print(f"Error matching pattern {pattern}: {str(e)}")
|
||||
continue
|
||||
return None
|
||||
|
||||
# 构建剧集命名的正则表达式 (主要用于检测已命名文件)
|
||||
if "[]" in episode_pattern:
|
||||
regex_pattern = re.escape(episode_pattern).replace('\\[\\]', '(\\d+)')
|
||||
else:
|
||||
# 如果输入模式不包含[],则使用简单匹配模式,避免正则表达式错误
|
||||
print(f"⚠️ 剧集命名模式中没有找到 [] 占位符,将使用简单匹配")
|
||||
regex_pattern = "^" + re.escape(episode_pattern) + "(\\d+)$"
|
||||
|
||||
# 实现高级排序算法
|
||||
def extract_sorting_value(file):
|
||||
if file["dir"]: # 跳过文件夹
|
||||
return float('inf')
|
||||
|
||||
filename = file["file_name"]
|
||||
|
||||
# 尝试获取剧集序号
|
||||
episode_num = extract_episode_number(filename)
|
||||
if episode_num is not None:
|
||||
return episode_num
|
||||
|
||||
# 如果无法提取序号,则使用更新时间
|
||||
try:
|
||||
return file.get("last_update_at", 0)
|
||||
except:
|
||||
return 0
|
||||
|
||||
# 过滤出非目录文件,并且排除已经符合命名规则的文件
|
||||
files_to_process = [
|
||||
f for f in share_detail["list"]
|
||||
if not f["dir"] and not re.match(regex_pattern, f["file_name"])
|
||||
]
|
||||
|
||||
# 根据提取的排序值进行排序
|
||||
sorted_files = sorted(files_to_process, key=extract_sorting_value)
|
||||
|
||||
# 应用过滤词过滤
|
||||
filterwords = regex.get("filterwords", "")
|
||||
if filterwords:
|
||||
# 同时支持中英文逗号分隔
|
||||
filterwords = filterwords.replace(",", ",")
|
||||
filterwords_list = [word.strip() for word in filterwords.split(',')]
|
||||
for item in sorted_files:
|
||||
# 被过滤的文件不会有file_name_re,与不匹配正则的文件显示一致
|
||||
if any(word in item['file_name'] for word in filterwords_list):
|
||||
item["filtered"] = True
|
||||
|
||||
# 为每个文件生成新文件名
|
||||
for file in sorted_files:
|
||||
if not file.get("filtered"):
|
||||
# 获取文件扩展名
|
||||
file_ext = os.path.splitext(file["file_name"])[1]
|
||||
# 尝试提取剧集号
|
||||
episode_num = extract_episode_number(file["file_name"])
|
||||
if episode_num is not None:
|
||||
# 生成预览文件名
|
||||
file["file_name_re"] = episode_pattern.replace("[]", f"{episode_num:02d}") + file_ext
|
||||
else:
|
||||
# 无法提取剧集号,标记为无法处理
|
||||
file["file_name_re"] = "❌ 无法识别剧集号"
|
||||
|
||||
return share_detail
|
||||
else:
|
||||
# 普通正则命名预览
|
||||
|
||||
@ -171,6 +171,23 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 剧集识别模块 -->
|
||||
<div class="row title" title="识别文件名中的剧集编号,用于自动重命名">
|
||||
<div class="col-10">
|
||||
<h2 style="display: inline-block;"><i class="bi bi-list-ol"></i> 剧集识别</h2>
|
||||
<span class="badge badge-pill badge-light">
|
||||
<a href="#" target="_blank" @click.prevent="alert('剧集识别说明:\n在任务配置中填写包含E[]的匹配表达式,如「剧名 - S01E[]」,将自动切换至剧集命名模式,自动识别文件名中的编号作为集编号,并替换[]部分。\n\n集编号匹配规则:\n用于识别集编号的正则表达式,多个表达式之间用竖线「|」分隔,每个表达式必须包含一个捕获组,用于提取剧集编号')">?</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">集编号识别规则</span>
|
||||
</div>
|
||||
<input type="text" class="form-control" v-model="episodePatternsText" placeholder="多个正则表达式用竖线「|」分隔,如:(\\d+)|[Ee](\\d+)|第(\\d+)集">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row title" title="API接口,用以第三方添加任务等操作,见Wiki">
|
||||
<div class="col-10">
|
||||
@ -318,15 +335,17 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row" :title="task.use_sequence_naming ? '输入带{}占位符的重命名格式,如剧名 - S01E{}、剧名.S03E{}等,{}将被替换为集序号,目录中的文件夹会被自动过滤' : '可用作筛选,只转存匹配到的文件名的文件,留空则转存所有文件'">
|
||||
<div class="form-group row" :title="task.use_sequence_naming || task.use_episode_naming ? (task.use_sequence_naming ? '输入带{}占位符的重命名格式,如剧名 - S01E{}、剧名.S03E{}等,{}将被替换为集序号(按文件名和上传时间智能排序),目录中的文件夹会被自动过滤' : '输入带[]占位符的重命名格式,如剧名 - S01E[]、剧名.S03E[]等,[]将被替换为从文件名中提取的集编号,目录中的文件夹会被自动过滤') : '可用作筛选,只转存匹配到的文件名的文件,留空则转存所有文件'">
|
||||
<label class="col-sm-2 col-form-label">保存规则</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<button class="btn btn-outline-secondary" type="button" @click="fileSelect.selectDir=true;fileSelect.previewRegex=true;showShareSelect(index)" title="预览正则命名效果">{{ task.use_sequence_naming ? '顺序命名' : '正则命名' }}</button>
|
||||
<button class="btn btn-outline-secondary" type="button" @click="fileSelect.selectDir=true;fileSelect.previewRegex=true;showShareSelect(index)" title="预览正则命名效果">
|
||||
{{ task.use_sequence_naming ? '顺序命名' : (task.use_episode_naming ? '剧集命名' : '正则命名') }}
|
||||
</button>
|
||||
</div>
|
||||
<input type="text" name="pattern[]" class="form-control" v-model="task.pattern" :placeholder="task.use_sequence_naming ? '输入带{}占位符的重命名格式,如剧名 - S01E{}' : '匹配表达式'" list="magicRegex" @input="detectNamingMode(task)">
|
||||
<input v-if="!task.use_sequence_naming" type="text" name="replace[]" class="form-control" v-model="task.replace" placeholder="替换表达式">
|
||||
<input type="text" name="pattern[]" class="form-control" v-model="task.pattern" :placeholder="task.use_sequence_naming ? '输入带{}占位符的重命名格式,如剧名 - S01E{}' : (task.use_episode_naming ? '输入带[]占位符的重命名格式,如剧名 - S01E[]' : '匹配表达式')" list="magicRegex" @input="detectNamingMode(task)">
|
||||
<input v-if="!task.use_sequence_naming && !task.use_episode_naming" type="text" name="replace[]" class="form-control" v-model="task.replace" placeholder="替换表达式">
|
||||
<input v-else type="text" class="form-control" disabled value="" style="background-color: #e9ecef;">
|
||||
<div class="input-group-append" title="保存时只比较文件名的部分,01.mp4 和 01.mkv 视同为同一文件,不重复转存">
|
||||
<div class="input-group-text">
|
||||
@ -435,7 +454,7 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">
|
||||
<b v-if="fileSelect.previewRegex">{{ formData.tasklist[fileSelect.index].use_sequence_naming ? '顺序命名预览' : '正则命名预览' }}</b>
|
||||
<b v-if="fileSelect.previewRegex">{{ formData.tasklist[fileSelect.index].use_sequence_naming ? '顺序命名预览' : (formData.tasklist[fileSelect.index].use_episode_naming ? '剧集命名预览' : '正则命名预览') }}</b>
|
||||
<b v-else-if="fileSelect.selectDir">选择{{fileSelect.selectShare ? '需转存的' : '保存到的'}}文件夹</b>
|
||||
<b v-else>选择起始文件</b>
|
||||
<div v-if="modalLoading" class="spinner-border spinner-border-sm m-1" role="status"></div>
|
||||
@ -462,6 +481,9 @@
|
||||
<div v-if="formData.tasklist[fileSelect.index].use_sequence_naming">
|
||||
<b>顺序命名格式:</b><span class="badge badge-info" v-html="formData.tasklist[fileSelect.index].pattern"></span>
|
||||
</div>
|
||||
<div v-else-if="formData.tasklist[fileSelect.index].use_episode_naming">
|
||||
<b>剧集命名格式:</b><span class="badge badge-info" v-html="formData.tasklist[fileSelect.index].pattern"></span>
|
||||
</div>
|
||||
<div v-else>
|
||||
<b>匹配表达式:</b><span class="badge badge-info" v-html="formData.tasklist[fileSelect.index].pattern"></span>
|
||||
<b>替换表达式:</b><span class="badge badge-info" v-html="formData.tasklist[fileSelect.index].replace"></span>
|
||||
@ -471,7 +493,9 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">文件名</th>
|
||||
<th scope="col" v-if="fileSelect.selectShare">{{ fileSelect.index !== null && formData.tasklist[fileSelect.index] && formData.tasklist[fileSelect.index].use_sequence_naming ? '顺序命名' : '正则命名' }}</th>
|
||||
<th scope="col" v-if="fileSelect.selectShare">{{ fileSelect.index !== null && formData.tasklist[fileSelect.index] ?
|
||||
(formData.tasklist[fileSelect.index].use_sequence_naming ? '顺序命名' :
|
||||
(formData.tasklist[fileSelect.index].use_episode_naming ? '剧集命名' : '正则命名')) : '重命名' }}</th>
|
||||
<template v-if="!fileSelect.previewRegex">
|
||||
<th scope="col">大小</th>
|
||||
<th scope="col">修改日期 ↓</th>
|
||||
@ -519,6 +543,7 @@
|
||||
media_servers: {},
|
||||
tasklist: [],
|
||||
magic_regex: {},
|
||||
episode_patterns: [],
|
||||
source: {
|
||||
cloudsaver: {
|
||||
server: "",
|
||||
@ -540,7 +565,9 @@
|
||||
filterwords: "",
|
||||
runweek: [1, 2, 3, 4, 5, 6, 7],
|
||||
sequence_naming: "",
|
||||
use_sequence_naming: false
|
||||
use_sequence_naming: false,
|
||||
episode_naming: "",
|
||||
use_episode_naming: false
|
||||
},
|
||||
run_log: "",
|
||||
taskDirs: [""],
|
||||
@ -556,7 +583,7 @@
|
||||
isSearching: false,
|
||||
searchTimer: null,
|
||||
},
|
||||
activeTab: 'tasklist',
|
||||
activeTab: 'config',
|
||||
configModified: false,
|
||||
fileSelect: {
|
||||
index: null,
|
||||
@ -569,6 +596,21 @@
|
||||
previewRegex: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
episodePatternsText: {
|
||||
get() {
|
||||
if (!this.formData.episode_patterns) return '';
|
||||
return this.formData.episode_patterns.map(p => p.regex || '').join('|');
|
||||
},
|
||||
set(value) {
|
||||
// 将文本拆分为竖线分隔的表达式,每个创建一个正则对象
|
||||
const patterns = value.split('|').filter(pattern => pattern.trim() !== '');
|
||||
this.formData.episode_patterns = patterns.map(pattern => ({
|
||||
regex: pattern.trim()
|
||||
}));
|
||||
}
|
||||
}
|
||||
},
|
||||
filters: {
|
||||
ts2date: function (value) {
|
||||
const date = new Date(value);
|
||||
@ -613,12 +655,36 @@
|
||||
if (!task.pattern || task._pattern_backup) {
|
||||
task.pattern = task.sequence_naming;
|
||||
}
|
||||
} else if (task.use_episode_naming && task.episode_naming) {
|
||||
// 已经设置过剧集命名的,将剧集命名模式转换为匹配表达式
|
||||
if (!task.pattern || task._pattern_backup) {
|
||||
task.pattern = task.episode_naming;
|
||||
}
|
||||
} else {
|
||||
// 检测是否包含顺序命名模式
|
||||
// 检测是否包含顺序命名或剧集命名模式
|
||||
this.detectNamingMode(task);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 如果没有剧集识别模式,添加默认模式
|
||||
if (!this.formData.episode_patterns || this.formData.episode_patterns.length === 0) {
|
||||
this.formData.episode_patterns = [
|
||||
{ name: 'EP_BASIC', description: '[]', regex: '(\\d+)' },
|
||||
{ name: 'EP_4K', description: '[]-4K', regex: '(\\d+)[-_\\s]*4[Kk]' },
|
||||
{ name: 'EP_HUA', description: '[]话', regex: '(\\d+)话' },
|
||||
{ name: 'EP_E', description: 'E[]', regex: '[Ee](\\d+)' },
|
||||
{ name: 'EP_EP', description: 'EP[]', regex: '[Ee][Pp](\\d+)' },
|
||||
{ name: 'EP_DIHUA', description: '第[]话', regex: '第(\\d+)话' },
|
||||
{ name: 'EP_DIJI', description: '第[]集', regex: '第(\\d+)集' },
|
||||
{ name: 'EP_DIQI', description: '第[]期', regex: '第(\\d+)期' },
|
||||
{ name: 'EP_4KSPACE', description: '[] 4K', regex: '(\\d+)\\s+4[Kk]' },
|
||||
{ name: 'EP_4KUNDER', description: '[]_4K', regex: '(\\d+)[_\\s]4[Kk]' },
|
||||
{ name: 'EP_BRACKET', description: '【[]】', regex: '【(\\d+)】' },
|
||||
{ name: 'EP_SQUAREBRACKET', description: '方括号数字', regex: '\\[(\\d+)\\]' },
|
||||
{ name: 'EP_UNDERSCORE', description: '_[]_', regex: '_?(\\d+)_' }
|
||||
];
|
||||
}
|
||||
}, 500);
|
||||
},
|
||||
beforeDestroy() {
|
||||
@ -678,6 +744,10 @@
|
||||
token: ""
|
||||
};
|
||||
}
|
||||
// 确保剧集识别模式存在
|
||||
if (!config_data.episode_patterns) {
|
||||
config_data.episode_patterns = [];
|
||||
}
|
||||
this.formData = config_data;
|
||||
setTimeout(() => {
|
||||
this.configModified = false;
|
||||
@ -713,6 +783,10 @@
|
||||
if (task.use_sequence_naming && task.pattern && task.pattern.includes('{}')) {
|
||||
task.sequence_naming = task.pattern;
|
||||
}
|
||||
// 如果是剧集命名模式,确保episode_naming字段已正确设置
|
||||
if (task.use_episode_naming && task.pattern && task.pattern.includes('[]')) {
|
||||
task.episode_naming = task.pattern;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -1055,7 +1129,10 @@
|
||||
filterwords: this.formData.tasklist[this.fileSelect.index].filterwords,
|
||||
magic_regex: this.formData.magic_regex,
|
||||
use_sequence_naming: this.formData.tasklist[this.fileSelect.index].use_sequence_naming,
|
||||
sequence_naming: this.formData.tasklist[this.fileSelect.index].sequence_naming
|
||||
sequence_naming: this.formData.tasklist[this.fileSelect.index].sequence_naming,
|
||||
use_episode_naming: this.formData.tasklist[this.fileSelect.index].use_episode_naming,
|
||||
episode_naming: this.formData.tasklist[this.fileSelect.index].episode_naming,
|
||||
episode_patterns: this.formData.episode_patterns
|
||||
}
|
||||
}).then(response => {
|
||||
if (response.data.success) {
|
||||
@ -1133,9 +1210,12 @@
|
||||
return shareurl;
|
||||
},
|
||||
detectNamingMode(task) {
|
||||
// 检测是否为顺序命名模式
|
||||
// 检测是否为顺序命名模式或剧集命名模式
|
||||
const sequencePatterns = ['E{}', 'EP{}', 'S\\d+E{}', '第{}集', '第{}话', '第{}期'];
|
||||
const episodePatterns = ['E[]', 'EP[]', 'S\\d+E[]', '第[]集', '第[]话', '第[]期', '[]'];
|
||||
|
||||
let isSequenceNaming = false;
|
||||
let isEpisodeNaming = false;
|
||||
|
||||
// 保存当前值以支持撤销操作
|
||||
const currentValue = task.pattern;
|
||||
@ -1151,6 +1231,19 @@
|
||||
if (!isSequenceNaming && task.pattern.includes('{}') && (!task.replace || task.replace === '')) {
|
||||
isSequenceNaming = true;
|
||||
}
|
||||
|
||||
// 检查是否包含任何剧集命名模式
|
||||
if (!isSequenceNaming) {
|
||||
isEpisodeNaming = episodePatterns.some(pattern => {
|
||||
const regexPattern = pattern.replace('[]', '\\[\\]');
|
||||
return new RegExp(regexPattern).test(task.pattern);
|
||||
});
|
||||
|
||||
// 或者用户直接输入包含[]的格式,且替换表达式为空
|
||||
if (!isEpisodeNaming && task.pattern.includes('[]') && (!task.replace || task.replace === '')) {
|
||||
isEpisodeNaming = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理模式切换
|
||||
@ -1160,36 +1253,62 @@
|
||||
task._pattern_backup = task.pattern;
|
||||
task._replace_backup = task.replace;
|
||||
task.use_sequence_naming = true;
|
||||
task.use_episode_naming = false;
|
||||
}
|
||||
// 设置序列命名模式
|
||||
task.sequence_naming = task.pattern;
|
||||
} else if (isEpisodeNaming) {
|
||||
// 如果当前不是剧集命名模式,则保存现有的正则表达式
|
||||
if (!task.use_episode_naming) {
|
||||
task._pattern_backup = task.pattern;
|
||||
task._replace_backup = task.replace;
|
||||
task.use_episode_naming = true;
|
||||
task.use_sequence_naming = false;
|
||||
}
|
||||
// 设置剧集命名模式
|
||||
task.episode_naming = task.pattern;
|
||||
} else {
|
||||
// 如果当前是顺序命名模式,但现在检测不到顺序命名模式
|
||||
if (task.use_sequence_naming) {
|
||||
// 如果当前是顺序命名模式或剧集命名模式,但现在检测不到相应的命名模式
|
||||
if (task.use_sequence_naming || task.use_episode_naming) {
|
||||
// 如果用户正在删除内容(当前值为空或比上一次更短)
|
||||
if (!currentValue || (task._lastPatternValue && currentValue.length < task._lastPatternValue.length)) {
|
||||
// 保持当前编辑状态,不切换模式
|
||||
task.sequence_naming = currentValue;
|
||||
if (task.use_sequence_naming) {
|
||||
task.sequence_naming = currentValue;
|
||||
} else if (task.use_episode_naming) {
|
||||
task.episode_naming = currentValue;
|
||||
}
|
||||
|
||||
// 只有当完全删除后才切换回正则模式
|
||||
if (!currentValue) {
|
||||
task.use_sequence_naming = false;
|
||||
task.use_episode_naming = false;
|
||||
if (task._pattern_backup) {
|
||||
task.pattern = "";
|
||||
task.replace = task._replace_backup || "";
|
||||
}
|
||||
task.sequence_naming = null;
|
||||
task.episode_naming = null;
|
||||
}
|
||||
} else if (task._pattern_backup && !task.pattern.includes('{}')) {
|
||||
} else if (task._pattern_backup && (!task.pattern.includes('{}') && !task.pattern.includes('[]'))) {
|
||||
// 正常切换回正则命名模式(非删除操作)
|
||||
task.use_sequence_naming = false;
|
||||
task.use_episode_naming = false;
|
||||
task.pattern = task._pattern_backup;
|
||||
task.replace = task._replace_backup;
|
||||
task._sequence_backup = task.sequence_naming;
|
||||
task.sequence_naming = null;
|
||||
} else if (!task._pattern_backup && !task.pattern.includes('{}')) {
|
||||
if (task.use_sequence_naming) {
|
||||
task._sequence_backup = task.sequence_naming;
|
||||
task.sequence_naming = null;
|
||||
} else if (task.use_episode_naming) {
|
||||
task._episode_backup = task.episode_naming;
|
||||
task.episode_naming = null;
|
||||
}
|
||||
} else if (!task._pattern_backup && (!task.pattern.includes('{}') && !task.pattern.includes('[]'))) {
|
||||
// 没有备份,但需要切换回正则模式
|
||||
task.use_sequence_naming = false;
|
||||
task.use_episode_naming = false;
|
||||
task.sequence_naming = null;
|
||||
task.episode_naming = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1199,6 +1318,15 @@
|
||||
|
||||
// 强制Vue更新视图
|
||||
this.$forceUpdate();
|
||||
},
|
||||
// 增加剧集识别模式
|
||||
addEpisodePattern() {
|
||||
// 此方法保留但不再使用
|
||||
},
|
||||
|
||||
// 移除剧集识别模式
|
||||
removeEpisodePattern(index) {
|
||||
// 此方法保留但不再使用
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -165,6 +165,25 @@ class Config:
|
||||
}
|
||||
if task.get("media_id"):
|
||||
del task["media_id"]
|
||||
|
||||
# 添加剧集识别模式配置
|
||||
if not config_data.get("episode_patterns"):
|
||||
print("🔼 添加剧集识别模式配置")
|
||||
config_data["episode_patterns"] = [
|
||||
{"description": "[]", "regex": "(\\d+)"},
|
||||
{"description": "[]-4K", "regex": "(\\d+)[-_\\s]*4[Kk]"},
|
||||
{"description": "[]话", "regex": "(\\d+)话"},
|
||||
{"description": "E[]", "regex": "[Ee](\\d+)"},
|
||||
{"description": "EP[]", "regex": "[Ee][Pp](\\d+)"},
|
||||
{"description": "第[]话", "regex": "第(\\d+)话"},
|
||||
{"description": "第[]集", "regex": "第(\\d+)集"},
|
||||
{"description": "第[]期", "regex": "第(\\d+)期"},
|
||||
{"description": "[] 4K", "regex": "(\\d+)\\s+4[Kk]"},
|
||||
{"description": "[]_4K", "regex": "(\\d+)[_\\s]4[Kk]"},
|
||||
{"description": "【[]】", "regex": "【(\\d+)】"},
|
||||
{"description": "[[]", "regex": "\\[(\\d+)\\]"},
|
||||
{"description": "_[]_", "regex": "_?(\\d+)_"}
|
||||
]
|
||||
|
||||
|
||||
class Quark:
|
||||
@ -686,6 +705,26 @@ class Quark:
|
||||
# 将{}替换为(\d+)用于匹配
|
||||
regex_pattern = re.escape(sequence_pattern).replace('\\{\\}', '(\\d+)')
|
||||
task["regex_pattern"] = regex_pattern
|
||||
# 支持剧集命名模式
|
||||
elif task.get("use_episode_naming") and task.get("episode_naming"):
|
||||
# 剧集命名模式下已经在do_save中打印了剧集命名信息,这里不再重复打印
|
||||
# 设置正则模式为空
|
||||
task["regex_pattern"] = None
|
||||
# 构建剧集命名的正则表达式
|
||||
episode_pattern = task["episode_naming"]
|
||||
# 先检查是否包含合法的[]字符
|
||||
if "[]" in episode_pattern:
|
||||
# 将[] 替换为 (\d+)
|
||||
# 先将模式字符串进行转义,确保其他特殊字符不会干扰
|
||||
escaped_pattern = re.escape(episode_pattern)
|
||||
# 然后将转义后的 \[ \] 替换为捕获组 (\d+)
|
||||
regex_pattern = escaped_pattern.replace('\\[\\]', '(\\d+)')
|
||||
else:
|
||||
# 如果输入模式不包含[],则使用简单匹配模式,避免正则表达式错误
|
||||
print(f"⚠️ 剧集命名模式中没有找到 [] 占位符,将使用简单匹配")
|
||||
regex_pattern = "^" + re.escape(episode_pattern) + "(\\d+)$"
|
||||
|
||||
task["regex_pattern"] = regex_pattern
|
||||
else:
|
||||
# 正则命名模式
|
||||
pattern, replace = self.magic_regex_func(
|
||||
@ -982,6 +1021,9 @@ class Quark:
|
||||
share_file["original_name"] = share_file["file_name"] # 保存原文件名,用于排序
|
||||
need_save_list.append(share_file)
|
||||
current_sequence += 1
|
||||
else:
|
||||
# print(f"跳过已存在的文件: {save_name}")
|
||||
pass
|
||||
|
||||
# 指定文件开始订阅/到达指定文件(含)结束历遍
|
||||
if share_file["fid"] == task.get("startfid", ""):
|
||||
@ -1019,63 +1061,83 @@ class Quark:
|
||||
if share_file["dir"] and task.get("update_subdir", False):
|
||||
pattern, replace = task["update_subdir"], ""
|
||||
else:
|
||||
pattern, replace = self.magic_regex_func(
|
||||
task.get("pattern", ""), task.get("replace", ""), task["taskname"]
|
||||
)
|
||||
# 正则文件名匹配
|
||||
if re.search(pattern, share_file["file_name"]):
|
||||
# 替换后的文件名
|
||||
save_name = (
|
||||
re.sub(pattern, replace, share_file["file_name"])
|
||||
if replace != ""
|
||||
else share_file["file_name"]
|
||||
)
|
||||
# 忽略后缀
|
||||
if task.get("ignore_extension") and not share_file["dir"]:
|
||||
compare_func = lambda a, b1, b2: (
|
||||
os.path.splitext(a)[0] == os.path.splitext(b1)[0]
|
||||
or os.path.splitext(a)[0] == os.path.splitext(b2)[0]
|
||||
)
|
||||
# 检查是否是剧集命名模式
|
||||
if task.get("use_episode_naming") and task.get("regex_pattern"):
|
||||
# 使用预先准备好的正则表达式
|
||||
pattern = task["regex_pattern"]
|
||||
replace = ""
|
||||
else:
|
||||
compare_func = lambda a, b1, b2: (a == b1 or a == b2)
|
||||
# 判断目标目录文件是否存在
|
||||
file_exists = False
|
||||
for dir_file in dir_file_list:
|
||||
if compare_func(
|
||||
dir_file["file_name"], share_file["file_name"], save_name
|
||||
):
|
||||
file_exists = True
|
||||
# 删除对文件打印部分
|
||||
break
|
||||
|
||||
if not file_exists:
|
||||
# 不打印保存信息
|
||||
share_file["save_name"] = save_name
|
||||
share_file["original_name"] = share_file["file_name"] # 保存原文件名,用于排序
|
||||
need_save_list.append(share_file)
|
||||
elif share_file["dir"]:
|
||||
# 存在并是一个文件夹
|
||||
if task.get("update_subdir", False):
|
||||
if re.search(task["update_subdir"], share_file["file_name"]):
|
||||
print(f"检查子文件夹: {savepath}/{share_file['file_name']}")
|
||||
subdir_tree = self.dir_check_and_save(
|
||||
task,
|
||||
pwd_id,
|
||||
stoken,
|
||||
share_file["fid"],
|
||||
f"{subdir_path}/{share_file['file_name']}",
|
||||
)
|
||||
if subdir_tree.size(1) > 0:
|
||||
# 合并子目录树
|
||||
tree.create_node(
|
||||
"📁" + share_file["file_name"],
|
||||
# 普通正则命名模式
|
||||
pattern, replace = self.magic_regex_func(
|
||||
task.get("pattern", ""), task.get("replace", ""), task["taskname"]
|
||||
)
|
||||
|
||||
# 确保pattern不为空,避免正则表达式错误
|
||||
if not pattern:
|
||||
pattern = ".*"
|
||||
|
||||
# 正则文件名匹配
|
||||
try:
|
||||
if re.search(pattern, share_file["file_name"]):
|
||||
# 替换后的文件名
|
||||
save_name = (
|
||||
re.sub(pattern, replace, share_file["file_name"])
|
||||
if replace != ""
|
||||
else share_file["file_name"]
|
||||
)
|
||||
# 忽略后缀
|
||||
if task.get("ignore_extension") and not share_file["dir"]:
|
||||
compare_func = lambda a, b1, b2: (
|
||||
os.path.splitext(a)[0] == os.path.splitext(b1)[0]
|
||||
or os.path.splitext(a)[0] == os.path.splitext(b2)[0]
|
||||
)
|
||||
else:
|
||||
compare_func = lambda a, b1, b2: (a == b1 or a == b2)
|
||||
# 判断目标目录文件是否存在
|
||||
file_exists = False
|
||||
for dir_file in dir_file_list:
|
||||
if compare_func(
|
||||
dir_file["file_name"], share_file["file_name"], save_name
|
||||
):
|
||||
file_exists = True
|
||||
# print(f"跳过已存在的文件: {dir_file['file_name']}")
|
||||
# 删除对文件打印部分
|
||||
break
|
||||
|
||||
if not file_exists:
|
||||
# 不打印保存信息
|
||||
share_file["save_name"] = save_name
|
||||
share_file["original_name"] = share_file["file_name"] # 保存原文件名,用于排序
|
||||
need_save_list.append(share_file)
|
||||
elif share_file["dir"]:
|
||||
# 存在并是一个文件夹
|
||||
if task.get("update_subdir", False):
|
||||
if re.search(task["update_subdir"], share_file["file_name"]):
|
||||
print(f"检查子文件夹: {savepath}/{share_file['file_name']}")
|
||||
subdir_tree = self.dir_check_and_save(
|
||||
task,
|
||||
pwd_id,
|
||||
stoken,
|
||||
share_file["fid"],
|
||||
parent=pdir_fid,
|
||||
data={
|
||||
"is_dir": share_file["dir"],
|
||||
},
|
||||
f"{subdir_path}/{share_file['file_name']}",
|
||||
)
|
||||
tree.merge(share_file["fid"], subdir_tree, deep=False)
|
||||
if subdir_tree.size(1) > 0:
|
||||
# 合并子目录树
|
||||
tree.create_node(
|
||||
"📁" + share_file["file_name"],
|
||||
share_file["fid"],
|
||||
parent=pdir_fid,
|
||||
data={
|
||||
"is_dir": share_file["dir"],
|
||||
},
|
||||
)
|
||||
tree.merge(share_file["fid"], subdir_tree, deep=False)
|
||||
except Exception as e:
|
||||
print(f"⚠️ 正则表达式错误: {str(e)}, pattern: {pattern}")
|
||||
# 使用安全的默认值
|
||||
share_file["save_name"] = share_file["file_name"]
|
||||
share_file["original_name"] = share_file["file_name"]
|
||||
need_save_list.append(share_file)
|
||||
# 指定文件开始订阅/到达指定文件(含)结束历遍
|
||||
if share_file["fid"] == task.get("startfid", ""):
|
||||
break
|
||||
@ -1084,10 +1146,11 @@ class Quark:
|
||||
fid_token_list = [item["share_fid_token"] for item in need_save_list]
|
||||
if fid_list:
|
||||
# 只在有新文件需要转存时才打印目录文件列表
|
||||
print(f"📂 目标目录:{savepath} ({len(dir_file_list)}个文件)")
|
||||
for file in dir_file_list:
|
||||
print(f" {file['file_name']}")
|
||||
print()
|
||||
# 移除打印目标目录信息和文件列表的代码
|
||||
# print(f"📂 目标目录:{savepath} ({len(dir_file_list)}个文件)")
|
||||
# for file in dir_file_list:
|
||||
# print(f" {file['file_name']}")
|
||||
# print()
|
||||
|
||||
save_file_return = self.save_file(
|
||||
fid_list, fid_token_list, to_pdir_fid, pwd_id, stoken
|
||||
@ -1289,42 +1352,421 @@ class Quark:
|
||||
|
||||
return is_rename_count > 0
|
||||
|
||||
# 非顺序命名模式,使用普通正则重命名
|
||||
pattern, replace = self.magic_regex_func(
|
||||
task.get("pattern", ""), task.get("replace", ""), task["taskname"]
|
||||
)
|
||||
if not pattern or not replace:
|
||||
return 0
|
||||
savepath = re.sub(r"/{2,}", "/", f"/{task['savepath']}{subdir_path}")
|
||||
if not self.savepath_fid.get(savepath):
|
||||
# 路径已存在,直接设置fid
|
||||
self.savepath_fid[savepath] = self.get_fids([savepath])[0]["fid"]
|
||||
dir_file_list = self.ls_dir(self.savepath_fid[savepath])
|
||||
dir_file_name_list = [item["file_name"] for item in dir_file_list]
|
||||
is_rename_count = 0
|
||||
for dir_file in dir_file_list:
|
||||
if dir_file["dir"]:
|
||||
is_rename_count += self.do_rename_task(
|
||||
task, f"{subdir_path}/{dir_file['file_name']}"
|
||||
)
|
||||
if re.search(pattern, dir_file["file_name"]):
|
||||
save_name = (
|
||||
re.sub(pattern, replace, dir_file["file_name"])
|
||||
if replace != ""
|
||||
else dir_file["file_name"]
|
||||
)
|
||||
if save_name != dir_file["file_name"] and (
|
||||
save_name not in dir_file_name_list
|
||||
):
|
||||
rename_return = self.rename(dir_file["fid"], save_name)
|
||||
if rename_return["code"] == 0:
|
||||
print(f"重命名: {dir_file['file_name']} → {save_name}")
|
||||
is_rename_count += 1
|
||||
# 检查是否为剧集命名模式
|
||||
elif task.get("use_episode_naming") and task.get("episode_naming"):
|
||||
# 使用剧集命名模式
|
||||
episode_pattern = task["episode_naming"]
|
||||
regex_pattern = task.get("regex_pattern")
|
||||
|
||||
# 获取目录文件列表 - 添加这行代码初始化dir_file_list
|
||||
savepath = re.sub(r"/{2,}", "/", f"/{task['savepath']}{subdir_path}")
|
||||
if not self.savepath_fid.get(savepath):
|
||||
# 路径已存在,直接设置fid
|
||||
savepath_fids = self.get_fids([savepath])
|
||||
if not savepath_fids:
|
||||
print(f"保存路径不存在,准备新建:{savepath}")
|
||||
mkdir_result = self.mkdir(savepath)
|
||||
if mkdir_result["code"] == 0:
|
||||
self.savepath_fid[savepath] = mkdir_result["data"]["fid"]
|
||||
print(f"保存路径新建成功:{savepath}")
|
||||
else:
|
||||
print(
|
||||
f"重命名: {dir_file['file_name']} → {save_name} 失败,{rename_return['message']}"
|
||||
print(f"保存路径新建失败:{mkdir_result['message']}")
|
||||
return False
|
||||
else:
|
||||
self.savepath_fid[savepath] = savepath_fids[0]["fid"]
|
||||
|
||||
dir_file_list = self.ls_dir(self.savepath_fid[savepath])
|
||||
|
||||
# 构建目标目录中所有文件的查重索引(按大小和修改时间)
|
||||
dir_files_map = {}
|
||||
for dir_file in dir_file_list:
|
||||
if not dir_file["dir"]: # 仅处理文件
|
||||
file_size = dir_file.get("size", 0)
|
||||
file_ext = os.path.splitext(dir_file["file_name"])[1].lower()
|
||||
update_time = dir_file.get("updated_at", 0)
|
||||
|
||||
# 创建大小+扩展名的索引,用于快速查重
|
||||
key = f"{file_size}_{file_ext}"
|
||||
if key not in dir_files_map:
|
||||
dir_files_map[key] = []
|
||||
dir_files_map[key].append({
|
||||
"file_name": dir_file["file_name"],
|
||||
"updated_at": update_time,
|
||||
})
|
||||
|
||||
# 实现序号提取函数
|
||||
def extract_episode_number(filename):
|
||||
# 优先匹配SxxExx格式
|
||||
match_s_e = re.search(r'[Ss](\d+)[Ee](\d+)', filename)
|
||||
if match_s_e:
|
||||
# 直接返回E后面的集数
|
||||
return int(match_s_e.group(2))
|
||||
|
||||
# 其次匹配E01格式
|
||||
match_e = re.search(r'[Ee][Pp]?(\d+)', filename)
|
||||
if match_e:
|
||||
return int(match_e.group(1))
|
||||
|
||||
# 尝试匹配更多格式
|
||||
default_patterns = [
|
||||
r'(\d+)',
|
||||
r'(\d+)[-_\s]*4[Kk]',
|
||||
r'(\d+)话',
|
||||
r'第(\d+)话',
|
||||
r'第(\d+)集',
|
||||
r'第(\d+)期',
|
||||
r'(\d+)\s+4[Kk]',
|
||||
r'(\d+)[_\s]4[Kk]',
|
||||
r'【(\d+)】',
|
||||
r'\[(\d+)\]',
|
||||
r'_?(\d+)_'
|
||||
]
|
||||
|
||||
# 如果配置了自定义规则,优先使用
|
||||
if "config_data" in task and isinstance(task["config_data"].get("episode_patterns"), list) and task["config_data"]["episode_patterns"]:
|
||||
patterns = [p.get("regex", "(\\d+)") for p in task["config_data"]["episode_patterns"]]
|
||||
else:
|
||||
# 尝试从全局配置获取
|
||||
global CONFIG_DATA
|
||||
if isinstance(CONFIG_DATA.get("episode_patterns"), list) and CONFIG_DATA["episode_patterns"]:
|
||||
patterns = [p.get("regex", "(\\d+)") for p in CONFIG_DATA["episode_patterns"]]
|
||||
else:
|
||||
patterns = default_patterns
|
||||
|
||||
# 尝试使用每个正则表达式匹配文件名
|
||||
for pattern_regex in patterns:
|
||||
try:
|
||||
match = re.search(pattern_regex, filename)
|
||||
if match:
|
||||
return int(match.group(1))
|
||||
except:
|
||||
continue
|
||||
return None
|
||||
|
||||
# 找出已命名的文件列表,避免重复转存
|
||||
existing_episode_numbers = set()
|
||||
for dir_file in dir_file_list:
|
||||
if not dir_file["dir"] and regex_pattern:
|
||||
try:
|
||||
matches = re.match(regex_pattern, dir_file["file_name"])
|
||||
if matches:
|
||||
episode_num = int(matches.group(1))
|
||||
existing_episode_numbers.add(episode_num)
|
||||
except:
|
||||
pass
|
||||
|
||||
# 检查是否需要从分享链接获取数据
|
||||
if task.get("shareurl"):
|
||||
try:
|
||||
# 提取链接参数
|
||||
pwd_id, passcode, pdir_fid, paths = self.extract_url(task["shareurl"])
|
||||
if not pwd_id:
|
||||
print(f"提取链接参数失败,请检查分享链接是否有效")
|
||||
return False
|
||||
|
||||
# 获取分享详情
|
||||
is_sharing, stoken = self.get_stoken(pwd_id, passcode)
|
||||
if not is_sharing:
|
||||
print(f"分享详情获取失败:{stoken}")
|
||||
return False
|
||||
|
||||
# 获取分享文件列表
|
||||
share_file_list = self.get_detail(pwd_id, stoken, pdir_fid)["list"]
|
||||
if not share_file_list:
|
||||
print("分享为空,文件已被分享者删除")
|
||||
return False
|
||||
|
||||
# 预先过滤分享文件列表,去除已存在的文件
|
||||
filtered_share_files = []
|
||||
for share_file in share_file_list:
|
||||
if share_file["dir"]:
|
||||
# 处理子目录
|
||||
if task.get("update_subdir") and re.search(task["update_subdir"], share_file["file_name"]):
|
||||
filtered_share_files.append(share_file)
|
||||
continue
|
||||
|
||||
# 检查文件是否已存在(基于大小和修改时间)
|
||||
file_size = share_file.get("size", 0)
|
||||
file_ext = os.path.splitext(share_file["file_name"])[1].lower()
|
||||
share_update_time = share_file.get("last_update_at", 0)
|
||||
|
||||
key = f"{file_size}_{file_ext}"
|
||||
is_duplicate = False
|
||||
|
||||
if key in dir_files_map:
|
||||
for existing_file in dir_files_map[key]:
|
||||
existing_update_time = existing_file.get("updated_at", 0)
|
||||
if (abs(share_update_time - existing_update_time) < 2592000 or
|
||||
abs(1 - (share_update_time / existing_update_time if existing_update_time else 1)) < 0.1):
|
||||
is_duplicate = True
|
||||
break
|
||||
|
||||
# 检查剧集号是否已经存在
|
||||
episode_num = extract_episode_number(share_file["file_name"])
|
||||
if episode_num is not None and episode_num in existing_episode_numbers:
|
||||
# print(f"跳过已存在的剧集号: {episode_num} ({share_file['file_name']})")
|
||||
is_duplicate = True
|
||||
|
||||
# 生成预期的目标文件名并检查是否已存在
|
||||
if episode_num is not None and not is_duplicate:
|
||||
file_ext = os.path.splitext(share_file["file_name"])[1]
|
||||
expected_name = episode_pattern.replace("[]", f"{episode_num:02d}") + file_ext
|
||||
# 检查目标文件名是否存在于目录中
|
||||
if any(dir_file["file_name"] == expected_name for dir_file in dir_file_list):
|
||||
# print(f"跳过已存在的文件名: {expected_name}")
|
||||
is_duplicate = True
|
||||
|
||||
# 只处理非重复文件
|
||||
if not is_duplicate:
|
||||
filtered_share_files.append(share_file)
|
||||
|
||||
# 指定文件开始订阅/到达指定文件(含)结束历遍
|
||||
if share_file["fid"] == task.get("startfid", ""):
|
||||
break
|
||||
|
||||
# 实现高级排序算法
|
||||
def sort_by_episode(file):
|
||||
if file["dir"]:
|
||||
return (float('inf'), 0)
|
||||
|
||||
filename = file["file_name"]
|
||||
|
||||
# 优先匹配S01E01格式
|
||||
match_s_e = re.search(r'[Ss](\d+)[Ee](\d+)', filename)
|
||||
if match_s_e:
|
||||
season = int(match_s_e.group(1))
|
||||
episode = int(match_s_e.group(2))
|
||||
return (season * 1000 + episode, 0)
|
||||
|
||||
# 其他匹配方式
|
||||
episode_num = extract_episode_number(filename)
|
||||
if episode_num is not None:
|
||||
return (episode_num, 0)
|
||||
|
||||
# 无法识别,使用修改时间
|
||||
return (float('inf'), file.get("last_update_at", 0))
|
||||
|
||||
# 过滤出文件并排序
|
||||
files_to_process = [f for f in filtered_share_files if not f["dir"]]
|
||||
sorted_files = sorted(files_to_process, key=sort_by_episode)
|
||||
|
||||
# 要保存的文件列表
|
||||
need_save_list = []
|
||||
|
||||
# 生成文件名并添加到列表
|
||||
for share_file in sorted_files:
|
||||
episode_num = extract_episode_number(share_file["file_name"])
|
||||
if episode_num is not None:
|
||||
# 生成新文件名
|
||||
file_ext = os.path.splitext(share_file["file_name"])[1]
|
||||
save_name = episode_pattern.replace("[]", f"{episode_num:02d}") + file_ext
|
||||
|
||||
# 添加到保存列表
|
||||
share_file["save_name"] = save_name
|
||||
share_file["original_name"] = share_file["file_name"]
|
||||
need_save_list.append(share_file)
|
||||
else:
|
||||
# 无法提取集号,使用原文件名
|
||||
share_file["save_name"] = share_file["file_name"]
|
||||
share_file["original_name"] = share_file["file_name"]
|
||||
need_save_list.append(share_file)
|
||||
|
||||
# 保存文件
|
||||
if need_save_list:
|
||||
fid_list = [item["fid"] for item in need_save_list]
|
||||
fid_token_list = [item["share_fid_token"] for item in need_save_list]
|
||||
save_file_return = self.save_file(
|
||||
fid_list, fid_token_list, self.savepath_fid[savepath], pwd_id, stoken
|
||||
)
|
||||
return is_rename_count > 0
|
||||
if save_file_return["code"] == 0:
|
||||
task_id = save_file_return["data"]["task_id"]
|
||||
query_task_return = self.query_task(task_id)
|
||||
|
||||
if query_task_return["code"] == 0:
|
||||
# 建立目录树
|
||||
tree = Tree()
|
||||
tree.create_node(
|
||||
savepath,
|
||||
"root",
|
||||
data={
|
||||
"is_dir": True,
|
||||
},
|
||||
)
|
||||
|
||||
saved_files = []
|
||||
for index, item in enumerate(need_save_list):
|
||||
icon = (
|
||||
"📁"
|
||||
if item["dir"] == True
|
||||
else "🎞️" if item.get("obj_category") == "video" else ""
|
||||
)
|
||||
saved_files.append(f"{icon}{item['save_name']}")
|
||||
tree.create_node(
|
||||
f"{icon}{item['save_name']}",
|
||||
item["fid"],
|
||||
parent="root",
|
||||
data={
|
||||
"fid": f"{query_task_return['data']['save_as']['save_as_top_fids'][index]}",
|
||||
"path": f"{savepath}/{item['save_name']}",
|
||||
"is_dir": item.get("dir", False),
|
||||
"original_name": item["original_name"],
|
||||
"save_name": item["save_name"]
|
||||
},
|
||||
)
|
||||
|
||||
# 添加成功通知
|
||||
add_notify(f"✅《{task['taskname']}》 添加追更:\n/{task['savepath']}{subdir_path}")
|
||||
# 打印保存文件列表
|
||||
for idx, file_name in enumerate(saved_files):
|
||||
prefix = "├── " if idx < len(saved_files) - 1 else "└── "
|
||||
add_notify(f"{prefix}{file_name}")
|
||||
add_notify("")
|
||||
|
||||
# 进行重命名操作,确保文件按照预览名称保存
|
||||
time.sleep(1) # 等待文件保存完成
|
||||
|
||||
# 刷新目录列表以获取新保存的文件
|
||||
fresh_dir_file_list = self.ls_dir(self.savepath_fid[savepath])
|
||||
renamed_count = 0
|
||||
|
||||
# 创建一个映射来存储原始文件名到保存项的映射
|
||||
original_name_to_item = {}
|
||||
for saved_item in need_save_list:
|
||||
# 使用文件名前缀作为键,处理可能的文件名变化
|
||||
file_prefix = saved_item["original_name"].split(".")[0]
|
||||
original_name_to_item[file_prefix] = saved_item
|
||||
# 同时保存完整文件名的映射
|
||||
original_name_to_item[saved_item["original_name"]] = saved_item
|
||||
|
||||
# 创建一个集合来跟踪已经重命名的文件ID
|
||||
renamed_fids = set()
|
||||
|
||||
# 首先尝试使用剧集号进行智能匹配
|
||||
for dir_file in fresh_dir_file_list:
|
||||
if dir_file["dir"] or dir_file["fid"] in renamed_fids:
|
||||
continue
|
||||
|
||||
# 从文件名中提取剧集号
|
||||
episode_num = extract_episode_number(dir_file["file_name"])
|
||||
if episode_num is None:
|
||||
continue
|
||||
|
||||
# 查找对应的目标文件
|
||||
for saved_item in need_save_list:
|
||||
saved_episode_num = extract_episode_number(saved_item["original_name"])
|
||||
if saved_episode_num == episode_num:
|
||||
# 匹配到对应的剧集号
|
||||
target_name = saved_item["save_name"]
|
||||
# 确保目标名称不重复
|
||||
if target_name not in [f["file_name"] for f in fresh_dir_file_list]:
|
||||
rename_result = self.rename(dir_file["fid"], target_name)
|
||||
if rename_result["code"] == 0:
|
||||
print(f"重命名: {dir_file['file_name']} → {target_name}")
|
||||
renamed_count += 1
|
||||
renamed_fids.add(dir_file["fid"])
|
||||
break
|
||||
else:
|
||||
# 如果目标文件名已存在,尝试加上序号
|
||||
name_base, ext = os.path.splitext(target_name)
|
||||
alt_name = f"{name_base} ({episode_num}){ext}"
|
||||
if alt_name not in [f["file_name"] for f in fresh_dir_file_list]:
|
||||
rename_result = self.rename(dir_file["fid"], alt_name)
|
||||
if rename_result["code"] == 0:
|
||||
print(f"重命名: {dir_file['file_name']} → {alt_name}")
|
||||
renamed_count += 1
|
||||
renamed_fids.add(dir_file["fid"])
|
||||
break
|
||||
|
||||
# 对于未能通过剧集号匹配的文件,尝试使用文件名匹配
|
||||
for dir_file in fresh_dir_file_list:
|
||||
if dir_file["dir"] or dir_file["fid"] in renamed_fids:
|
||||
continue
|
||||
|
||||
# 尝试精确匹配
|
||||
if dir_file["file_name"] in original_name_to_item:
|
||||
saved_item = original_name_to_item[dir_file["file_name"]]
|
||||
target_name = saved_item["save_name"]
|
||||
|
||||
if target_name not in [f["file_name"] for f in fresh_dir_file_list]:
|
||||
rename_result = self.rename(dir_file["fid"], target_name)
|
||||
if rename_result["code"] == 0:
|
||||
print(f"重命名: {dir_file['file_name']} → {target_name}")
|
||||
renamed_count += 1
|
||||
renamed_fids.add(dir_file["fid"])
|
||||
continue
|
||||
|
||||
# 尝试模糊匹配(使用文件名前缀)
|
||||
dir_file_prefix = dir_file["file_name"].split(".")[0]
|
||||
for prefix, saved_item in original_name_to_item.items():
|
||||
if prefix in dir_file_prefix or dir_file_prefix in prefix:
|
||||
# 找到相似的文件名
|
||||
target_name = saved_item["save_name"]
|
||||
if target_name not in [f["file_name"] for f in fresh_dir_file_list]:
|
||||
rename_result = self.rename(dir_file["fid"], target_name)
|
||||
if rename_result["code"] == 0:
|
||||
print(f"重命名: {dir_file['file_name']} → {target_name}")
|
||||
renamed_count += 1
|
||||
renamed_fids.add(dir_file["fid"])
|
||||
original_name_to_item.pop(prefix, None) # 避免重复使用
|
||||
break
|
||||
|
||||
if renamed_count > 0:
|
||||
# print(f"✅ 成功重命名 {renamed_count} 个文件")
|
||||
pass
|
||||
|
||||
return tree
|
||||
else:
|
||||
err_msg = query_task_return["message"]
|
||||
add_notify(f"❌《{task['taskname']}》转存失败:{err_msg}\n")
|
||||
return False
|
||||
else:
|
||||
print(f"❌ 保存文件失败: {save_file_return['message']}")
|
||||
add_notify(f"❌《{task['taskname']}》转存失败:{save_file_return['message']}\n")
|
||||
return False
|
||||
else:
|
||||
# print("没有需要保存的新文件")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"处理分享链接时发生错误: {str(e)}")
|
||||
add_notify(f"❌《{task['taskname']}》处理分享链接时发生错误: {str(e)}\n")
|
||||
return False
|
||||
|
||||
# 对本地已有文件进行重命名(即使没有分享链接或处理失败也执行)
|
||||
is_rename_count = 0
|
||||
renamed_files = []
|
||||
|
||||
# 筛选出需要重命名的文件
|
||||
for dir_file in dir_file_list:
|
||||
if dir_file["dir"]:
|
||||
continue
|
||||
|
||||
# 检查是否需要重命名
|
||||
episode_num = extract_episode_number(dir_file["file_name"])
|
||||
if episode_num is not None:
|
||||
# 检查文件名是否符合指定的剧集命名格式
|
||||
if not re.match(regex_pattern, dir_file["file_name"]):
|
||||
file_ext = os.path.splitext(dir_file["file_name"])[1]
|
||||
new_name = episode_pattern.replace("[]", f"{episode_num:02d}") + file_ext
|
||||
renamed_files.append((dir_file, new_name))
|
||||
|
||||
# 按剧集号排序
|
||||
renamed_files.sort(key=lambda x: extract_episode_number(x[0]["file_name"]) or 0)
|
||||
|
||||
# 执行重命名
|
||||
for dir_file, new_name in renamed_files:
|
||||
# 防止重名
|
||||
if new_name not in [f["file_name"] for f in dir_file_list]:
|
||||
try:
|
||||
rename_return = self.rename(dir_file["fid"], new_name)
|
||||
if rename_return["code"] == 0:
|
||||
print(f"重命名: {dir_file['file_name']} → {new_name}")
|
||||
is_rename_count += 1
|
||||
else:
|
||||
print(f"重命名: {dir_file['file_name']} → {new_name} 失败,{rename_return['message']}")
|
||||
except Exception as e:
|
||||
print(f"重命名出错: {dir_file['file_name']} → {new_name},错误:{str(e)}")
|
||||
|
||||
return is_rename_count > 0
|
||||
|
||||
|
||||
def verify_account(account):
|
||||
@ -1418,6 +1860,8 @@ def do_save(account, tasklist=[]):
|
||||
# 根据命名模式显示不同信息
|
||||
if task.get("use_sequence_naming") and task.get("sequence_naming"):
|
||||
print(f"顺序命名: {task['sequence_naming']}")
|
||||
elif task.get("use_episode_naming") and task.get("episode_naming"):
|
||||
print(f"剧集命名: {task['episode_naming']}")
|
||||
else:
|
||||
# 正则命名模式
|
||||
if task.get("pattern"):
|
||||
@ -1455,6 +1899,12 @@ def do_save(account, tasklist=[]):
|
||||
task["addition"] = merge_dicts(
|
||||
task.get("addition", {}), task_plugins_config
|
||||
)
|
||||
|
||||
# 为任务添加剧集模式配置
|
||||
if task.get("use_episode_naming") and task.get("episode_naming"):
|
||||
task["config_data"] = {
|
||||
"episode_patterns": CONFIG_DATA.get("episode_patterns", [])
|
||||
}
|
||||
# 调用插件
|
||||
if is_new_tree or is_rename:
|
||||
print(f"🧩 调用插件")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user