优化资源搜索和谷歌搜索的逻辑,智能填充增加对中文季数的支持

1. 使用去除季数的任务名称进行搜索,获取更完整的搜索结果
2. 支持解析中文季序号,用于在智能填充时自动识别季序号
This commit is contained in:
x1ao4 2025-05-20 02:01:08 +08:00
parent 7aae96a63e
commit c2425392a6
2 changed files with 175 additions and 22 deletions

View File

@ -263,6 +263,40 @@ def get_task_suggestions():
return jsonify({"success": False, "message": "未登录"})
query = request.args.get("q", "").lower()
deep = request.args.get("d", "").lower()
# 提取剧名,去除季数信息
def extract_show_name(task_name):
# 清理任务名称中的连续空格和特殊符号
clean_name = task_name.replace('\u3000', ' ').replace('\t', ' ')
clean_name = re.sub(r'\s+', ' ', clean_name).strip()
# 匹配常见的季数格式
# 例如:黑镜 - S07、人生若如初见 - S01、折腰.S01、音你而来-S02、快乐的大人 S02
season_patterns = [
r'^(.*?)[\s\.\-_]+S\d+$', # 黑镜 - S07、折腰.S01、音你而来-S02
r'^(.*?)[\s\.\-_]+Season\s*\d+$', # 黑镜 - Season 1
r'^(.*?)\s+S\d+$', # 快乐的大人 S02
r'^(.*?)[\s\.\-_]+S\d+E\d+$', # 处理 S01E01 格式
r'^(.*?)\s+第\s*\d+\s*季$', # 处理 第N季 格式
r'^(.*?)[\s\.\-_]+第\s*\d+\s*季$', # 处理 - 第N季 格式
r'^(.*?)\s+第[一二三四五六七八九十零]+季$', # 处理 第一季、第二季 格式
r'^(.*?)[\s\.\-_]+第[一二三四五六七八九十零]+季$', # 处理 - 第一季、- 第二季 格式
]
for pattern in season_patterns:
match = re.match(pattern, clean_name, re.IGNORECASE)
if match:
show_name = match.group(1).strip()
# 去除末尾可能残留的分隔符
show_name = re.sub(r'[\s\.\-_]+$', '', show_name)
return show_name
# 如果没有匹配到季数格式,返回原名称
return clean_name
# 处理搜索关键词,提取剧名
search_query = extract_show_name(query)
try:
cs_data = config_data.get("source", {}).get("cloudsaver", {})
if (
@ -276,23 +310,34 @@ def get_task_suggestions():
cs_data.get("password", ""),
cs_data.get("token", ""),
)
search = cs.auto_login_search(query)
# 使用处理后的搜索关键词
search = cs.auto_login_search(search_query)
if search.get("success"):
if search.get("new_token"):
cs_data["token"] = search.get("new_token")
Config.write_json(CONFIG_PATH, config_data)
search_results = cs.clean_search_results(search.get("data"))
# 在返回结果中添加实际使用的搜索关键词
return jsonify(
{"success": True, "source": "CloudSaver", "data": search_results}
{
"success": True,
"source": "CloudSaver",
"data": search_results
}
)
else:
return jsonify({"success": True, "message": search.get("message")})
else:
base_url = base64.b64decode("aHR0cHM6Ly9zLjkxNzc4OC54eXo=").decode()
url = f"{base_url}/task_suggestions?q={query}&d={deep}"
# 使用处理后的搜索关键词
url = f"{base_url}/task_suggestions?q={search_query}&d={deep}"
response = requests.get(url)
return jsonify(
{"success": True, "source": "网络公开", "data": response.json()}
{
"success": True,
"source": "网络公开",
"data": response.json()
}
)
except Exception as e:
return jsonify({"success": True, "message": f"error: {str(e)}"})

View File

@ -483,7 +483,9 @@
</span>
<span v-else>正在搜索中...</span>
</div>
<div class="dropdown-item text-muted" v-else style="font-size:14px; padding-left: 8px; text-align: left;">{{ smart_param.taskSuggestions.message ? smart_param.taskSuggestions.message : smart_param.taskSuggestions.data && smart_param.taskSuggestions.data.length ? `以下资源由 ${smart_param.taskSuggestions.source} 搜索提供(仅显示有效链接),如有侵权请联系资源发布方` : "未搜索到有效资源" }}</div>
<div class="dropdown-item text-muted" v-else style="font-size:14px; padding-left: 8px; text-align: left;">
{{ smart_param.taskSuggestions.message ? smart_param.taskSuggestions.message : smart_param.taskSuggestions.data && smart_param.taskSuggestions.data.length ? `以下资源由 ${smart_param.taskSuggestions.source} 搜索提供(仅显示有效链接),如有侵权请联系资源发布方` : "未搜索到有效资源" }}
</div>
<div v-for="suggestion in smart_param.taskSuggestions.data || []" :key="suggestion.taskname" class="dropdown-item cursor-pointer" @click.prevent="selectSuggestion(index, suggestion)" style="font-size: 14px;" :title="suggestion.content">
<span v-html="suggestion.verify ? '✅': ''"></span> {{ suggestion.taskname }}
<small class="text-muted">
@ -497,7 +499,7 @@
<i v-else class="bi bi-search"></i>
</button>
<div class="input-group-text" title="谷歌搜索">
<a target="_blank" :href="`https://www.google.com/search?q=%22pan.quark.cn/s%22+${task.taskname}`"><i class="bi bi-google"></i></a>
<a target="_blank" :href="`https://www.google.com/search?q=%22pan.quark.cn/s%22+${cleanTaskNameForSearch(task.taskname)}`"><i class="bi bi-google"></i></a>
</div>
<div class="input-group-text" title="TMDB搜索">
<a target="_blank" :href="`https://www.themoviedb.org/search?query=${cleanTaskNameForSearch(task.taskname)}`"><img src="./static/TMDB.svg" class="tmdb-icon"></a>
@ -1340,12 +1342,39 @@
},
cleanTaskNameForSearch(taskName) {
if (!taskName) return '';
// 移除 Sxx 格式(包括 S01、S01E01 等)
let cleanedName = taskName.replace(/\s*[-—_]\s*S\d+(\w\d+)?/i, '');
// 清理任务名称中的连续空格和特殊符号
let cleanedName = taskName.replace(/\u3000/g, ' ').replace(/\t/g, ' ');
cleanedName = cleanedName.replace(/\s+/g, ' ').trim();
// 匹配常见的季数格式
const seasonPatterns = [
/^(.*?)[\s\.\-_]+S\d+$/i, // 黑镜 - S07、折腰.S01、音你而来-S02
/^(.*?)[\s\.\-_]+Season\s*\d+$/i, // 黑镜 - Season 1
/^(.*?)\s+S\d+$/i, // 快乐的大人 S02
/^(.*?)[\s\.\-_]+S\d+E\d+$/i, // 处理 S01E01 格式
/^(.*?)\s+第\s*\d+\s*季$/i, // 处理 第N季 格式
/^(.*?)[\s\.\-_]+第\s*\d+\s*季$/i, // 处理 - 第N季 格式
/^(.*?)\s+第[一二三四五六七八九十零]+季$/i, // 处理 第一季、第二季 格式
/^(.*?)[\s\.\-_]+第[一二三四五六七八九十零]+季$/i, // 处理 - 第一季、- 第二季 格式
];
for (const pattern of seasonPatterns) {
const match = cleanedName.match(pattern);
if (match) {
let showName = match[1].trim();
// 去除末尾可能残留的分隔符
showName = showName.replace(/[\s\.\-_]+$/, '');
return encodeURIComponent(showName);
}
}
// 如果没有匹配到季数格式,执行原有的清理逻辑
// 移除孤立的特殊符号
cleanedName = cleanedName.replace(/\s*[-—_]\s*$/, '');
// 移除开头和结尾的特殊符号
cleanedName = cleanedName.trim().replace(/^[-—_\s]+|[-—_\s]+$/g, '');
return encodeURIComponent(cleanedName);
},
changeTab(tab) {
@ -1593,10 +1622,42 @@
const cleanTaskName = task.taskname.replace(/\s+/g, ' ').trim();
// 提取任务名称中的分隔符格式
// 检测任务名称中剧名和季序号之间的分隔符
const separatorMatch = cleanTaskName.match(/^(.*?)([\s\.\-_]+)(?:S(\d+)|Season\s*(\d+))/i);
// 使用与cleanTaskNameForSearch相同的季数格式匹配模式
let showName, seasonNumber, nameSeparator = ' - '; // 默认分隔符
let isTVShow = false; // 标记是否为电视节目
let separatorMatch = null;
// 匹配常见的季数格式
const seasonPatterns = [
/^(.*?)([\s\.\-_]+)S(\d+)$/i, // 黑镜 - S07、折腰.S01、音你而来-S02
/^(.*?)([\s\.\-_]+)Season\s*(\d+)$/i, // 黑镜 - Season 1
/^(.*?)(\s+)S(\d+)$/i, // 快乐的大人 S02
/^(.*?)([\s\.\-_]+)S(\d+)E\d+$/i, // 处理 S01E01 格式
/^(.*?)(\s+)第\s*(\d+)\s*季$/i, // 处理 第N季 格式
/^(.*?)([\s\.\-_]+)第\s*(\d+)\s*季$/i, // 处理 - 第N季 格式
/^(.*?)(\s+)第([一二三四五六七八九十零]+)季$/i, // 处理 第一季、第二季 格式
/^(.*?)([\s\.\-_]+)第([一二三四五六七八九十零]+)季$/i, // 处理 - 第一季、- 第二季 格式
];
// 尝试匹配各种格式
for (const pattern of seasonPatterns) {
const match = cleanTaskName.match(pattern);
if (match) {
separatorMatch = match;
// 根据不同的格式,确定季序号的位置
if (match[3] && match[3].match(/[一二三四五六七八九十零]/)) {
// 将中文数字转换为阿拉伯数字
const chineseToNumber = {
'零': '0', '一': '1', '二': '2', '三': '3', '四': '4',
'五': '5', '六': '6', '七': '7', '八': '8', '九': '9', '十': '10'
};
seasonNumber = chineseToNumber[match[3]] || '01';
} else {
seasonNumber = match[3] || '01';
}
break;
}
}
// 判断保存路径是否包含电视节目相关的关键词
if (task.savepath) {
@ -1605,25 +1666,34 @@
}
// 当任务名称包含季信息或保存路径有电视剧关键词时,按电视节目处理
const hasSeason = separatorMatch && (separatorMatch[3] || separatorMatch[4]);
const hasSeason = separatorMatch !== null;
isTVShow = isTVShow || hasSeason;
if (separatorMatch) {
// 提取剧名(去除末尾空格和特殊符号)
showName = separatorMatch[1].trim().replace(/[\s\.\-_]+$/, '');
// 提取季序号
seasonNumber = separatorMatch[3] || separatorMatch[4] || '01';
// 季序号已在上面的循环中提取
// 提取实际使用的分隔符
const rawSeparator = separatorMatch[2];
// 规范化分隔符(如果是连续的空格,转为单个空格)
nameSeparator = rawSeparator.replace(/\s+/g, ' ');
// 如果只有一个点号,保留点号
if (rawSeparator === '.') {
nameSeparator = '.';
}
// 如果是单个空格,保留单个空格
else if (rawSeparator === ' ') {
nameSeparator = ' ';
// 处理特殊情况:如果是"黑镜 - 第五季"这种格式,需要保留完整的" - "分隔符
// 检查原始字符串中剧名后面的分隔符
const fullSeparator = cleanTaskName.substring(showName.length, cleanTaskName.length - (separatorMatch[3] ? separatorMatch[3].length + 1 : 0)).match(/^([\s\.\-_]+)/);
if (fullSeparator && fullSeparator[1]) {
// 使用完整的分隔符
nameSeparator = fullSeparator[1];
} else {
// 规范化分隔符(如果是连续的空格,转为单个空格)
nameSeparator = rawSeparator.replace(/\s+/g, ' ');
// 如果只有一个点号,保留点号
if (rawSeparator === '.') {
nameSeparator = '.';
}
// 如果是单个空格,保留单个空格
else if (rawSeparator === ' ') {
nameSeparator = ' ';
}
}
// 其他情况保持原样
} else {
@ -1672,6 +1742,44 @@
}
}
// 额外处理:检查保存路径最末级是否包含中文季序号,如果包含则转换为标准格式
if (task.savepath) {
const pathParts = task.savepath.split('/');
if (pathParts.length > 0) {
const lastPart = pathParts[pathParts.length - 1];
// 匹配中文季序号格式
const chineseSeasonMatch = lastPart.match(/^(.*?)([\s\.\-_]+)第([一二三四五六七八九十零]+)季$/);
if (chineseSeasonMatch) {
const showName = chineseSeasonMatch[1].trim();
const separator = chineseSeasonMatch[2];
const chineseSeason = chineseSeasonMatch[3];
// 将中文数字转换为阿拉伯数字
const chineseToNumber = {
'零': '0', '一': '1', '二': '2', '三': '3', '四': '4',
'五': '5', '六': '6', '七': '7', '八': '8', '九': '9', '十': '10'
};
const seasonNumber = chineseToNumber[chineseSeason] || '1';
// 更新最末级目录为标准格式
pathParts[pathParts.length - 1] = showName + separator + 'S' + seasonNumber.padStart(2, '0');
task.savepath = pathParts.join('/');
}
// 匹配阿拉伯数字季序号格式
const arabicSeasonMatch = lastPart.match(/^(.*?)([\s\.\-_]+)第(\d+)季$/);
if (arabicSeasonMatch) {
const showName = arabicSeasonMatch[1].trim();
const separator = arabicSeasonMatch[2];
const seasonNumber = arabicSeasonMatch[3];
// 更新最末级目录为标准格式
pathParts[pathParts.length - 1] = showName + separator + 'S' + seasonNumber.padStart(2, '0');
task.savepath = pathParts.join('/');
}
}
}
// 更新命名规则 - 始终使用当前任务名称中的信息
if (task.pattern) {
// 根据是否为电视节目决定处理方式