From 0c6274395d58d8b2a80ebe734b43b3799f8ab588 Mon Sep 17 00:00:00 2001 From: x1ao4 Date: Mon, 19 May 2025 17:02:50 +0800 Subject: [PATCH 1/7] =?UTF-8?q?=E8=A7=84=E8=8C=83=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E7=9A=84=E6=97=A5=E5=BF=97=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/alist.py | 30 +++++++++++++++--------------- plugins/alist_strm.py | 14 +++++++------- plugins/alist_strm_gen.py | 22 +++++++++++----------- plugins/aria2.py | 10 +++++----- plugins/emby.py | 18 +++++++++--------- plugins/plex.py | 18 +++++++++--------- quark_auto_save.py | 5 +++-- 7 files changed, 59 insertions(+), 58 deletions(-) diff --git a/plugins/alist.py b/plugins/alist.py index 5721bef..5d3a306 100644 --- a/plugins/alist.py +++ b/plugins/alist.py @@ -52,13 +52,13 @@ class Alist: response = response.json() if response.get("code") == 200: print( - f"Alist刷新: {response.get('data',[])[1].get('value','')} {response.get('data',[])[0].get('value','')}" + f"AList 刷新: {response.get('data',[])[1].get('value','')} {response.get('data',[])[0].get('value','')}" ) return True else: - print(f"Alist刷新: 连接失败❌ {response.get('message')}") + print(f"AList 刷新: 连接失败 ❌ {response.get('message')}") except requests.exceptions.RequestException as e: - print(f"获取Alist信息出错: {e}") + print(f"获取 AList 信息出错: {e}") return False def storage_id_to_path(self, storage_id): @@ -69,7 +69,7 @@ class Alist: storage_mount_path, quark_root_dir = match.group(1), match.group(2) file_list = self.get_file_list(storage_mount_path) if file_list.get("code") != 200: - print(f"Alist刷新: 获取挂载路径失败❌ {file_list.get('message')}") + print(f"AList 刷新: 获取挂载路径失败 ❌ {file_list.get('message')}") return False, (None, None) # 2. 检查是否数字,调用 Alist API 获取存储信息 elif re.match(r"^\d+$", storage_id): @@ -84,12 +84,12 @@ class Alist: ) elif storage_info["driver"] == "QuarkTV": print( - f"Alist刷新: [QuarkTV]驱动⚠️ storage_id请手动填入 /Alist挂载路径:/Quark目录路径" + f"AList 刷新: [QuarkTV] 驱动 ⚠️ storage_id 请手动填入 /Alist挂载路径:/Quark目录路径" ) else: - print(f"Alist刷新: 不支持[{storage_info['driver']}]驱动 ❌") + print(f"AList 刷新: 不支持 [{storage_info['driver']}] 驱动 ❌") else: - print(f"Alist刷新: storage_id[{storage_id}]格式错误❌") + print(f"AList 刷新: storage_id [{storage_id}] 格式错误 ❌") # 返回结果 if storage_mount_path and quark_root_dir: return True, (storage_mount_path, quark_root_dir) @@ -107,28 +107,28 @@ class Alist: if data.get("code") == 200: return data.get("data", []) else: - print(f"Alist刷新: 存储{storage_id}连接失败❌ {data.get('message')}") + print(f"AList 刷新: 存储 {storage_id} 连接失败 ❌ {data.get('message')}") except Exception as e: - print(f"Alist刷新: 获取Alist存储出错 {e}") + print(f"AList 刷新: 获取 AList 存储出错 {e}") return [] def refresh(self, path): data = self.get_file_list(path, True) if data.get("code") == 200: - print(f"📁 Alist刷新:目录[{path}] 成功✅") + print(f"📁 AList 刷新: 目录 [{path}] 成功 ✅") return data.get("data") elif "object not found" in data.get("message", ""): # 如果是根目录就不再往上查找 if path == "/" or path == self.storage_mount_path: - print(f"📁 Alist刷新:根目录不存在,请检查 Alist 配置") + print(f"📁 AList 刷新: 根目录不存在,请检查 AList 配置") return False # 获取父目录 parent_path = os.path.dirname(path) - print(f"📁 Alist刷新:[{path}] 不存在,转父目录 [{parent_path}]") + print(f"📁 AList 刷新: [{path}] 不存在,转父目录 [{parent_path}]") # 递归刷新父目录 return self.refresh(parent_path) else: - print(f"📁 Alist刷新:失败❌ {data.get('message')}") + print(f"📁 AList 刷新: 失败 ❌ {data.get('message')}") def get_file_list(self, path, force_refresh=False): url = f"{self.url}/api/fs/list" @@ -145,7 +145,7 @@ class Alist: response.raise_for_status() return response.json() except Exception as e: - print(f"📁 Alist刷新: 获取文件列表出错❌ {e}") + print(f"📁 AList 刷新: 获取文件列表出错 ❌ {e}") return {} def get_root_folder_full_path(self, cookie, pdir_fid): @@ -178,5 +178,5 @@ class Alist: path = f"{path}/{item['file_name']}" return path except Exception as e: - print(f"Alist刷新: 获取Quark路径出错 {e}") + print(f"AList 刷新: 获取 Quark 路径出错 {e}") return "" diff --git a/plugins/alist_strm.py b/plugins/alist_strm.py index cb50b7d..0d7c3f3 100644 --- a/plugins/alist_strm.py +++ b/plugins/alist_strm.py @@ -2,7 +2,7 @@ import re import requests """ - 配合 alist-strm 项目,触发特定配置运行 + 配合 Alist-Strm 项目,触发特定配置运行 https://github.com/tefuirZ/alist-strm """ @@ -47,12 +47,12 @@ class Alist_strm: for item in matchs if item[0] in config_id_str.split(",") ] - print(f"alist-strm配置运行: {config_name}") + print(f"Alist-Strm 配置运行: {config_name}") return True else: - print(f"alist-strm配置运行: 匹配失败❌,请检查网络连通和cookie有效性") + print(f"Alist-Strm 配置运行: 匹配失败 ❌ 请检查网络连通和cookie有效性") except Exception as e: - print(f"获取alist-strm配置信息出错: {e}") + print(f"获取 Alist-Strm 配置信息出错: {e}") return False def run_selected_configs(self, selected_configs_str): @@ -61,7 +61,7 @@ class Alist_strm: try: selected_configs = [int(x.strip()) for x in selected_configs_str.split(",")] except ValueError: - print("🔗 alist-strm配置运行: 出错❌ id应以,分割") + print("🔗 Alist-Strm 配置运行: 出错 ❌ ID 应以 , 分割") return False data = [("selected_configs", config_id) for config_id in selected_configs] data.append(("action", "run_selected")) @@ -73,10 +73,10 @@ class Alist_strm: match = re.search(r'role="alert">\s*([^<]+)\s* Date: Mon, 19 May 2025 19:37:17 +0800 Subject: [PATCH 2/7] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E7=9A=84=E6=99=BA=E8=83=BD=E5=A1=AB=E5=85=85?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/templates/index.html | 100 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 3 deletions(-) diff --git a/app/templates/index.html b/app/templates/index.html index ee5a2a0..5700e92 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -1529,7 +1529,22 @@ addTask() { if (!this.formData.tasklist) this.formData.tasklist = []; - newTask = { ...this.newTask } + let newTask = { ...this.newTask }; + + // 如果有上一个任务,继承保存路径和命名规则 + if (this.formData.tasklist.length > 0) { + const lastTask = this.formData.tasklist[this.formData.tasklist.length - 1]; + newTask.savepath = lastTask.savepath || ""; + newTask.pattern = lastTask.pattern || ""; + + // 继承命名规则选择模式 + newTask.use_sequence_naming = lastTask.use_sequence_naming || false; + newTask.use_episode_naming = lastTask.use_episode_naming || false; + newTask.sequence_naming = lastTask.sequence_naming || ""; + newTask.episode_naming = lastTask.episode_naming || ""; + newTask.replace = lastTask.replace || ""; + } + this.formData.tasklist.push(newTask) const index = this.formData.tasklist.length - 1; @@ -1573,8 +1588,87 @@ this.smart_param.searchTimer = setTimeout(() => { this.searchSuggestions(index, task.taskname, 0); }, 1000); - if (this.smart_param.savepath) - task.savepath = this.smart_param.savepath.replace('TASKNAME', task.taskname); + + // 清理任务名称中的连续空格和特殊符号 + const cleanTaskName = task.taskname.replace(/\s+/g, ' ').trim(); + + // 提取任务名称中的分隔符格式 + // 检测任务名称中剧名和季序号之间的分隔符 + const separatorMatch = cleanTaskName.match(/^(.*?)([\s\.\-_]+)(?:S(\d+)|Season\s*(\d+))/i); + let showName, seasonNumber, nameSeparator = ' - '; // 默认分隔符 + + 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 = ' '; + } + // 其他情况保持原样 + } else { + // 如果没有匹配到季序号格式,默认使用整个任务名作为剧名,季序号为01 + showName = cleanTaskName; + seasonNumber = '01'; + } + + // 更新保存路径 - 无论是否使用智能路径,都确保倒数第二级目录更新 + if (task.savepath) { + // 分割保存路径为各级目录 + const pathParts = task.savepath.split('/'); + + if (pathParts.length >= 2) { + // 如果智能路径已设置,使用原有逻辑更新最后一级 + if (this.smart_param.savepath) { + // 更新最后一级目录,但保留前面的路径结构 + const newPath = this.smart_param.savepath.replace('TASKNAME', task.taskname); + const newPathParts = newPath.split('/'); + pathParts[pathParts.length - 1] = newPathParts[newPathParts.length - 1]; + } else { + // 直接使用任务名称中提取的信息构建最后一级 + pathParts[pathParts.length - 1] = showName + nameSeparator + 'S' + seasonNumber.padStart(2, '0'); + } + + // 处理倒数第二级目录(剧名+年份)- 无论是否使用智能路径,都更新 + if (pathParts.length >= 3) { + const parentDir = pathParts[pathParts.length - 2]; + // 提取年份信息 + const yearMatch = parentDir.match(/\((\d{4})\)|\((\d{4})\)|[\s\-_]+(\d{4})(?:[\s\-_]+|$)/); + const year = yearMatch ? (yearMatch[1] || yearMatch[2] || yearMatch[3] || '2025') : '2025'; + + // 重建倒数第二级目录,使用新的剧名和原有的年份 + pathParts[pathParts.length - 2] = showName + ' (' + year + ')'; + } + + // 更新保存路径 + task.savepath = pathParts.join('/'); + } + } + + // 更新命名规则 - 始终使用当前任务名称中的信息 + if (task.pattern) { + // 处理剧集命名模式 + if (task.use_episode_naming) { + // 直接使用任务名称中提取的信息构建命名规则 + task.pattern = showName + nameSeparator + 'S' + seasonNumber.padStart(2, '0') + 'E[]'; + task.episode_naming = task.pattern; + } + // 处理顺序命名模式 + else if (task.use_sequence_naming) { + // 直接使用任务名称中提取的信息构建命名规则 + task.pattern = showName + nameSeparator + 'S' + seasonNumber.padStart(2, '0') + 'E{}'; + task.sequence_naming = task.pattern; + } + } }, removeTask(index) { if (confirm("确认删除任务 [#" + (index + 1) + ": " + this.formData.tasklist[index].taskname + "] 吗?")) From 7aae96a63e8b2216738b4c542c29695ee22e1d04 Mon Sep 17 00:00:00 2001 From: x1ao4 Date: Tue, 20 May 2025 00:17:44 +0800 Subject: [PATCH 3/7] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=99=BA=E8=83=BD?= =?UTF-8?q?=E5=A1=AB=E5=85=85=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=A0=B9=E6=8D=AE?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E8=B7=AF=E5=BE=84=E5=92=8C=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E5=90=8D=E7=A7=B0=E6=99=BA=E8=83=BD=E5=88=87=E6=8D=A2=E5=A1=AB?= =?UTF-8?q?=E5=85=85=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为电视节目和其他内容使用不同的智能填充模式 --- app/templates/index.html | 79 +++++++++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 14 deletions(-) diff --git a/app/templates/index.html b/app/templates/index.html index 5700e92..31c46bf 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -1596,6 +1596,17 @@ // 检测任务名称中剧名和季序号之间的分隔符 const separatorMatch = cleanTaskName.match(/^(.*?)([\s\.\-_]+)(?:S(\d+)|Season\s*(\d+))/i); let showName, seasonNumber, nameSeparator = ' - '; // 默认分隔符 + let isTVShow = false; // 标记是否为电视节目 + + // 判断保存路径是否包含电视节目相关的关键词 + if (task.savepath) { + const tvKeywords = ["影视", "电视", "节目", "剧", "动漫", "动画", "番", "综艺", "真人秀", "TV", "Tv", "tv", "Series", "series", "Show", "show"]; + isTVShow = tvKeywords.some(keyword => task.savepath.includes(keyword)); + } + + // 当任务名称包含季信息或保存路径有电视剧关键词时,按电视节目处理 + const hasSeason = separatorMatch && (separatorMatch[3] || separatorMatch[4]); + isTVShow = isTVShow || hasSeason; if (separatorMatch) { // 提取剧名(去除末尾空格和特殊符号) @@ -1634,12 +1645,19 @@ const newPathParts = newPath.split('/'); pathParts[pathParts.length - 1] = newPathParts[newPathParts.length - 1]; } else { - // 直接使用任务名称中提取的信息构建最后一级 - pathParts[pathParts.length - 1] = showName + nameSeparator + 'S' + seasonNumber.padStart(2, '0'); + // 根据是否为电视节目决定处理方式 + if (isTVShow) { + // 电视节目格式:剧名 + 分隔符 + S季序号 + pathParts[pathParts.length - 1] = showName + nameSeparator + 'S' + seasonNumber.padStart(2, '0'); + } else { + // 非电视节目直接使用任务名称 + pathParts[pathParts.length - 1] = cleanTaskName; + } } // 处理倒数第二级目录(剧名+年份)- 无论是否使用智能路径,都更新 - if (pathParts.length >= 3) { + if (pathParts.length >= 3 && isTVShow) { + // 只有电视节目才更新倒数第二级目录 const parentDir = pathParts[pathParts.length - 2]; // 提取年份信息 const yearMatch = parentDir.match(/\((\d{4})\)|\((\d{4})\)|[\s\-_]+(\d{4})(?:[\s\-_]+|$)/); @@ -1656,17 +1674,50 @@ // 更新命名规则 - 始终使用当前任务名称中的信息 if (task.pattern) { - // 处理剧集命名模式 - if (task.use_episode_naming) { - // 直接使用任务名称中提取的信息构建命名规则 - task.pattern = showName + nameSeparator + 'S' + seasonNumber.padStart(2, '0') + 'E[]'; - task.episode_naming = task.pattern; - } - // 处理顺序命名模式 - else if (task.use_sequence_naming) { - // 直接使用任务名称中提取的信息构建命名规则 - task.pattern = showName + nameSeparator + 'S' + seasonNumber.padStart(2, '0') + 'E{}'; - task.sequence_naming = task.pattern; + // 根据是否为电视节目决定处理方式 + if (isTVShow) { + // 处理剧集命名模式 + if (task.use_episode_naming) { + // 直接使用任务名称中提取的信息构建命名规则 + task.pattern = showName + nameSeparator + 'S' + seasonNumber.padStart(2, '0') + 'E[]'; + task.episode_naming = task.pattern; + } + // 处理顺序命名模式 + else if (task.use_sequence_naming) { + // 直接使用任务名称中提取的信息构建命名规则 + task.pattern = showName + nameSeparator + 'S' + seasonNumber.padStart(2, '0') + 'E{}'; + task.sequence_naming = task.pattern; + } + } else { + // 非电视节目,仅在使用剧集或顺序命名时更新 + if (task.use_episode_naming) { + // 如果原模式是纯E[],直接使用任务名称 + if (task.pattern.trim() === 'E[]') { + task.pattern = cleanTaskName + 'E[]'; + } else if (task.pattern.includes('[]')) { + // 尝试保留原有格式,但更新剧名部分 + const patternParts = task.pattern.split(/E\[\]/i); + if (patternParts.length > 0) { + task.pattern = cleanTaskName + 'E[]' + (patternParts[1] || ''); + } else { + task.pattern = cleanTaskName + 'E[]'; + } + } + task.episode_naming = task.pattern; + } else if (task.use_sequence_naming) { + // 同样处理顺序命名 + if (task.pattern.trim() === 'E{}' || task.pattern.trim() === '{}') { + task.pattern = cleanTaskName + 'E{}'; + } else if (task.pattern.includes('{}')) { + const patternParts = task.pattern.split(/E\{\}/i); + if (patternParts.length > 0) { + task.pattern = cleanTaskName + 'E{}' + (patternParts[1] || ''); + } else { + task.pattern = cleanTaskName + 'E{}'; + } + } + task.sequence_naming = task.pattern; + } } } }, From c2425392a64de970a49894cf30f145f05c8922cc Mon Sep 17 00:00:00 2001 From: x1ao4 Date: Tue, 20 May 2025 02:01:08 +0800 Subject: [PATCH 4/7] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=B5=84=E6=BA=90?= =?UTF-8?q?=E6=90=9C=E7=B4=A2=E5=92=8C=E8=B0=B7=E6=AD=8C=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E7=9A=84=E9=80=BB=E8=BE=91=EF=BC=8C=E6=99=BA=E8=83=BD=E5=A1=AB?= =?UTF-8?q?=E5=85=85=E5=A2=9E=E5=8A=A0=E5=AF=B9=E4=B8=AD=E6=96=87=E5=AD=A3?= =?UTF-8?q?=E6=95=B0=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 使用去除季数的任务名称进行搜索,获取更完整的搜索结果 2. 支持解析中文季序号,用于在智能填充时自动识别季序号 --- app/run.py | 53 ++++++++++++-- app/templates/index.html | 144 ++++++++++++++++++++++++++++++++++----- 2 files changed, 175 insertions(+), 22 deletions(-) diff --git a/app/run.py b/app/run.py index 0de4a43..64791a0 100644 --- a/app/run.py +++ b/app/run.py @@ -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)}"}) diff --git a/app/templates/index.html b/app/templates/index.html index 31c46bf..dc47931 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -483,7 +483,9 @@ 正在搜索中... - +