mirror of
https://github.com/Cp0204/quark-auto-save.git
synced 2026-01-15 17:00:43 +08:00
优化资源搜索和谷歌搜索的逻辑,智能填充增加对中文季数的支持
1. 使用去除季数的任务名称进行搜索,获取更完整的搜索结果 2. 支持解析中文季序号,用于在智能填充时自动识别季序号
This commit is contained in:
parent
7aae96a63e
commit
c2425392a6
53
app/run.py
53
app/run.py
@ -263,6 +263,40 @@ def get_task_suggestions():
|
|||||||
return jsonify({"success": False, "message": "未登录"})
|
return jsonify({"success": False, "message": "未登录"})
|
||||||
query = request.args.get("q", "").lower()
|
query = request.args.get("q", "").lower()
|
||||||
deep = request.args.get("d", "").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:
|
try:
|
||||||
cs_data = config_data.get("source", {}).get("cloudsaver", {})
|
cs_data = config_data.get("source", {}).get("cloudsaver", {})
|
||||||
if (
|
if (
|
||||||
@ -276,23 +310,34 @@ def get_task_suggestions():
|
|||||||
cs_data.get("password", ""),
|
cs_data.get("password", ""),
|
||||||
cs_data.get("token", ""),
|
cs_data.get("token", ""),
|
||||||
)
|
)
|
||||||
search = cs.auto_login_search(query)
|
# 使用处理后的搜索关键词
|
||||||
|
search = cs.auto_login_search(search_query)
|
||||||
if search.get("success"):
|
if search.get("success"):
|
||||||
if search.get("new_token"):
|
if search.get("new_token"):
|
||||||
cs_data["token"] = search.get("new_token")
|
cs_data["token"] = search.get("new_token")
|
||||||
Config.write_json(CONFIG_PATH, config_data)
|
Config.write_json(CONFIG_PATH, config_data)
|
||||||
search_results = cs.clean_search_results(search.get("data"))
|
search_results = cs.clean_search_results(search.get("data"))
|
||||||
|
# 在返回结果中添加实际使用的搜索关键词
|
||||||
return jsonify(
|
return jsonify(
|
||||||
{"success": True, "source": "CloudSaver", "data": search_results}
|
{
|
||||||
|
"success": True,
|
||||||
|
"source": "CloudSaver",
|
||||||
|
"data": search_results
|
||||||
|
}
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return jsonify({"success": True, "message": search.get("message")})
|
return jsonify({"success": True, "message": search.get("message")})
|
||||||
else:
|
else:
|
||||||
base_url = base64.b64decode("aHR0cHM6Ly9zLjkxNzc4OC54eXo=").decode()
|
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)
|
response = requests.get(url)
|
||||||
return jsonify(
|
return jsonify(
|
||||||
{"success": True, "source": "网络公开", "data": response.json()}
|
{
|
||||||
|
"success": True,
|
||||||
|
"source": "网络公开",
|
||||||
|
"data": response.json()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return jsonify({"success": True, "message": f"error: {str(e)}"})
|
return jsonify({"success": True, "message": f"error: {str(e)}"})
|
||||||
|
|||||||
@ -483,7 +483,9 @@
|
|||||||
</span>
|
</span>
|
||||||
<span v-else>正在搜索中...</span>
|
<span v-else>正在搜索中...</span>
|
||||||
</div>
|
</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">
|
<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 }}
|
<span v-html="suggestion.verify ? '✅': ''"></span> {{ suggestion.taskname }}
|
||||||
<small class="text-muted">
|
<small class="text-muted">
|
||||||
@ -497,7 +499,7 @@
|
|||||||
<i v-else class="bi bi-search"></i>
|
<i v-else class="bi bi-search"></i>
|
||||||
</button>
|
</button>
|
||||||
<div class="input-group-text" title="谷歌搜索">
|
<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>
|
||||||
<div class="input-group-text" title="TMDB搜索">
|
<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>
|
<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) {
|
cleanTaskNameForSearch(taskName) {
|
||||||
if (!taskName) return '';
|
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.replace(/\s*[-–—_]\s*$/, '');
|
||||||
// 移除开头和结尾的特殊符号
|
// 移除开头和结尾的特殊符号
|
||||||
cleanedName = cleanedName.trim().replace(/^[-–—_\s]+|[-–—_\s]+$/g, '');
|
cleanedName = cleanedName.trim().replace(/^[-–—_\s]+|[-–—_\s]+$/g, '');
|
||||||
|
|
||||||
return encodeURIComponent(cleanedName);
|
return encodeURIComponent(cleanedName);
|
||||||
},
|
},
|
||||||
changeTab(tab) {
|
changeTab(tab) {
|
||||||
@ -1593,10 +1622,42 @@
|
|||||||
const cleanTaskName = task.taskname.replace(/\s+/g, ' ').trim();
|
const cleanTaskName = task.taskname.replace(/\s+/g, ' ').trim();
|
||||||
|
|
||||||
// 提取任务名称中的分隔符格式
|
// 提取任务名称中的分隔符格式
|
||||||
// 检测任务名称中剧名和季序号之间的分隔符
|
// 使用与cleanTaskNameForSearch相同的季数格式匹配模式
|
||||||
const separatorMatch = cleanTaskName.match(/^(.*?)([\s\.\-_]+)(?:S(\d+)|Season\s*(\d+))/i);
|
|
||||||
let showName, seasonNumber, nameSeparator = ' - '; // 默认分隔符
|
let showName, seasonNumber, nameSeparator = ' - '; // 默认分隔符
|
||||||
let isTVShow = false; // 标记是否为电视节目
|
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) {
|
if (task.savepath) {
|
||||||
@ -1605,25 +1666,34 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 当任务名称包含季信息或保存路径有电视剧关键词时,按电视节目处理
|
// 当任务名称包含季信息或保存路径有电视剧关键词时,按电视节目处理
|
||||||
const hasSeason = separatorMatch && (separatorMatch[3] || separatorMatch[4]);
|
const hasSeason = separatorMatch !== null;
|
||||||
isTVShow = isTVShow || hasSeason;
|
isTVShow = isTVShow || hasSeason;
|
||||||
|
|
||||||
if (separatorMatch) {
|
if (separatorMatch) {
|
||||||
// 提取剧名(去除末尾空格和特殊符号)
|
// 提取剧名(去除末尾空格和特殊符号)
|
||||||
showName = separatorMatch[1].trim().replace(/[\s\.\-_]+$/, '');
|
showName = separatorMatch[1].trim().replace(/[\s\.\-_]+$/, '');
|
||||||
// 提取季序号
|
// 季序号已在上面的循环中提取
|
||||||
seasonNumber = separatorMatch[3] || separatorMatch[4] || '01';
|
|
||||||
// 提取实际使用的分隔符
|
// 提取实际使用的分隔符
|
||||||
const rawSeparator = separatorMatch[2];
|
const rawSeparator = separatorMatch[2];
|
||||||
// 规范化分隔符(如果是连续的空格,转为单个空格)
|
|
||||||
nameSeparator = rawSeparator.replace(/\s+/g, ' ');
|
// 处理特殊情况:如果是"黑镜 - 第五季"这种格式,需要保留完整的" - "分隔符
|
||||||
// 如果只有一个点号,保留点号
|
// 检查原始字符串中剧名后面的分隔符
|
||||||
if (rawSeparator === '.') {
|
const fullSeparator = cleanTaskName.substring(showName.length, cleanTaskName.length - (separatorMatch[3] ? separatorMatch[3].length + 1 : 0)).match(/^([\s\.\-_]+)/);
|
||||||
nameSeparator = '.';
|
|
||||||
}
|
if (fullSeparator && fullSeparator[1]) {
|
||||||
// 如果是单个空格,保留单个空格
|
// 使用完整的分隔符
|
||||||
else if (rawSeparator === ' ') {
|
nameSeparator = fullSeparator[1];
|
||||||
nameSeparator = ' ';
|
} else {
|
||||||
|
// 规范化分隔符(如果是连续的空格,转为单个空格)
|
||||||
|
nameSeparator = rawSeparator.replace(/\s+/g, ' ');
|
||||||
|
// 如果只有一个点号,保留点号
|
||||||
|
if (rawSeparator === '.') {
|
||||||
|
nameSeparator = '.';
|
||||||
|
}
|
||||||
|
// 如果是单个空格,保留单个空格
|
||||||
|
else if (rawSeparator === ' ') {
|
||||||
|
nameSeparator = ' ';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 其他情况保持原样
|
// 其他情况保持原样
|
||||||
} else {
|
} 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) {
|
if (task.pattern) {
|
||||||
// 根据是否为电视节目决定处理方式
|
// 根据是否为电视节目决定处理方式
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user