diff --git a/package.json b/package.json index 8b4db13..a1b6e2d 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "name": "AI字幕自动生成(v2)", "description": "使用whisper自动生成视频文件字幕,使用大模型翻译字幕成中文。", "labels": "字幕", - "version": "2.2", + "version": "2.3", "icon": "autosubtitles.jpeg", "author": "TimoYoung", "level": 1, @@ -37,7 +37,8 @@ "v1.2": "fix openai_proxy打开时,翻译失败的问题,优化日志输出", "v2.0": "1.引入任务队列 2.支持监听媒体入库自动生成字幕 3.增加任务状态展示界面", "v2.1": "支持清除历史记录", - "v2.2": "fix" + "v2.2": "fix", + "v2.3": "支持独立的大模型调用配置" } }, "CustomSites": { diff --git a/plugins/autosubv2/README.md b/plugins/autosubv2/README.md index 2dd8981..783207a 100644 --- a/plugins/autosubv2/README.md +++ b/plugins/autosubv2/README.md @@ -41,15 +41,28 @@ | faster-whisper 模型选择 | 使用的 Whisper 模型大小 | base | | 使用代理下载模型 | 是否使用代理下载模型 | 是 | -### 翻译配置 +### 翻译接口配置 -| 配置项 | 说明 | 默认值 | -|-----------|----------------------|-----| -| 启用批量翻译 | 是否启用批量翻译以提高效率 | 是 | -| 每批翻译行数 | 每批处理的字幕行数 | 20 | -| 上下文窗口大小 | 翻译时考虑的上下文行数 | 5 | -| LLM请求重试次数 | 翻译失败时的重试次数 | 3 | -| 翻译英文时合并整句 | 对英文字幕先合并单词再翻译,提升翻译质量 | 否 | +> 可选使用 ChatGPT 插件配置 或 自定义 OpenAI 接口参数 + +| 配置项 | 说明 | 默认值 | +|------------------|--------------------------------------|-------------------------| +| 复用ChatGPT插件配置 | 是否直接使用系统中已配置的 ChatGPT 插件参数 | 否 | +| 使用代理服务器 | 是否通过 MP 配置的代理访问 OpenAI 接口 | 否 | +| 兼容模式 | 是否启用兼容模式(绕过 `/v1` 路径拼接) | 否 | +| OpenAI API URL | 自定义 OpenAI 接口地址 | https://api.openai.com | +| API 密钥 | OpenAI 的 API Key | 无 | +| 自定义模型 | 使用的 LLM 模型名称(如 gpt-3.5-turbo) | gpt-3.5-turbo | + +### 翻译参数配置 + +| 配置项 | 说明 | 默认值 | +|---------------|------------------------------------|-----| +| 启用批量翻译 | 是否启用批量翻译以提高效率 | 是 | +| 每批翻译行数 | 每批处理的字幕行数 | 10 | +| 上下文窗口大小 | 翻译时考虑的上下文行数 | 5 | +| LLM请求重试次数 | 翻译失败时的重试次数 | 3 | +| 翻译英文时合并整句 | 对英文字幕先合并单词再翻译,提升翻译质量 | 否 | ### 手动运行配置 @@ -80,19 +93,37 @@ - 当所有字幕都不存在时,使用ASR提取 - 适用于大模型支持多语言翻译且翻译质量较好的场景 +## 翻译方式说明 + +插件支持两种方式调用大模型进行翻译: + +1. **复用 ChatGPT 插件配置** + - 开启“复用ChatGPT插件配置”后,自动使用系统中维护的 ChatGPT 插件参数 + - 包括 API Key、API URL、是否使用代理等 + - 适合已有 ChatGPT 插件的用户快速部署 + +2. **自定义 OpenAI 接口参数** + - 关闭“复用ChatGPT插件配置”后,可独立配置: + - API 地址(支持反代) + - API Key + - 使用的模型 + - 是否使用代理 + - 是否启用兼容模式(避免 `/v1` 路径冲突) + + +--- + ## 注意事项 -1. 翻译功能依赖OpenAI插件配置,使用前请确保已正确配置 -2. 首次使用音轨识别功能时,会自动从HuggingFace下载模型。开启"使用代理下载模型"选项会使用MP配置的代理。 -3. 媒体路径支持单个文件或文件夹的绝对路径。选择文件夹时会递归处理其中的所有视频文件,外挂字幕将从媒体文件同级目录中查找 -4. 批量翻译通过一次处理多行字幕来减少API调用次数,提高效率。如果翻译结果与原文行数不匹配,系统会自动降级为逐行翻译 -5. 上下文窗口大小和批量翻译行数需要根据大模型的推理能力来调整。当模型能力不足时,过大的批量或上下文窗口可能会影响翻译质量 +1. 翻译功能依赖大模型配置,使用前请确保已正确配置 OpenAI Key 或 ChatGPT 插件。 +2. 首次使用音轨识别功能时,会自动从 HuggingFace 下载模型。开启"使用代理下载模型"选项会使用 MP 配置的代理。 +3. 媒体路径支持单个文件或文件夹的绝对路径。选择文件夹时会递归处理其中的所有视频文件,外挂字幕将从媒体文件同级目录中查找。 +4. 批量翻译通过一次处理多行字幕来减少 API 调用次数,提高效率。如果翻译结果与原文行数不匹配,系统会自动降级为逐行翻译。 +5. 上下文窗口大小和批量翻译行数需要根据大模型的推理能力来调整。当模型能力不足时,过大的批量或上下文窗口可能会影响翻译质量。 6. 翻译后的中文字幕会打上“机翻”标签。 7. 插件运行时会启动一个后台线程用于消费任务队列,插件关闭时会清空队列并终止当前任务。 - ## todo -- 独立的大模型调用 -- 工作流/api接口 +- 工作流/API接口 - 任务完成后调用媒体库刷新 \ No newline at end of file diff --git a/plugins/autosubv2/__init__.py b/plugins/autosubv2/__init__.py index f361c24..464170c 100644 --- a/plugins/autosubv2/__init__.py +++ b/plugins/autosubv2/__init__.py @@ -66,7 +66,7 @@ class AutoSubv2(_PluginBase): # 主题色 plugin_color = "#2C4F7E" # 插件版本 - plugin_version = "2.2" + plugin_version = "2.3" # 插件作者 plugin_author = "TimoYoung" # 作者主页 @@ -128,20 +128,33 @@ class AutoSubv2(_PluginBase): self._huggingface_proxy = config.get('proxy', True) self._translate_zh = config.get('translate_zh', False) if self._translate_zh: - chatgpt = self.get_config("ChatGPT") - if not chatgpt: - logger.error(f"翻译依赖于ChatGPT,请先维护ChatGPT插件") - return - openai_key = chatgpt and chatgpt.get("openai_key") - openai_url = chatgpt and chatgpt.get("openai_url") - openai_proxy = chatgpt and chatgpt.get("proxy") - openai_model = chatgpt and chatgpt.get("model") - if not openai_key: - logger.error(f"翻译依赖于ChatGPT,请先维护openai_key") - return + use_chatgpt = config.get('use_chatgpt', True) + if use_chatgpt: + chatgpt = self.get_config("ChatGPT") + if not chatgpt: + logger.error(f"翻译依赖于ChatGPT,请先维护ChatGPT插件") + return + openai_key_str = chatgpt and chatgpt.get("openai_key") + openai_url = chatgpt and chatgpt.get("openai_url") + openai_proxy = chatgpt and chatgpt.get("proxy") + openai_model = chatgpt and chatgpt.get("model") + compatible = chatgpt and chatgpt.get("compatible") + if not openai_key_str: + logger.error(f"请先在ChatGPT插件中维护openai_key") + return + openai_key = [key.strip() for key in openai_key_str.split(',') if key.strip()][0] + else: + openai_key = config.get('openai_key') + if not openai_key: + logger.error(f"翻译依赖于OpenAI,请先维护openai_key") + return + openai_url = config.get('openai_url', "https://api.openai.com") + openai_proxy = config.get('openai_proxy', False) + openai_model = config.get('openai_model', "gpt-3.5-turbo") + compatible = config.get('compatible', False) self._openai = OpenAi(api_key=openai_key, api_url=openai_url, proxy=settings.PROXY if openai_proxy else None, - model=openai_model) + model=openai_model, compatible=bool(compatible)) self._enable_batch = config.get('enable_batch', True) self._batch_size = int(config.get('batch_size')) if config.get('batch_size') else 10 self._context_window = int(config.get('context_window')) if config.get('context_window') else 5 @@ -1143,7 +1156,7 @@ class AutoSubv2(_PluginBase): 'props': { 'model': 'translate_zh', 'label': '翻译成中文', - 'hint': '需要配置ChatGPT插件' + 'hint': '使用大模型翻译成中文字幕' } } ] @@ -1210,7 +1223,143 @@ class AutoSubv2(_PluginBase): 'content': [ { 'component': 'VExpansionPanelTitle', - 'text': '大模型翻译设置' + 'text': '大模型接口设置' + }, + { + 'component': 'VExpansionPanelText', + 'content': [ + { + 'component': 'VRow', + 'content': [ + { + 'component': 'VCol', + 'props': {'cols': 12, 'md': 4}, + 'content': [ + { + 'component': 'VSwitch', + 'props': { + 'model': 'use_chatgpt', + 'label': '复用ChatGPT插件配置' + } + } + ] + }, + { + 'component': 'VTextField', + 'props': { + 'model': 'use_chatgpt_trigger', + 'class': 'd-none', + 'text': 'trigger', + 'change': 'use_chatgpt_trigger = use_chatgpt ? 1 : 0' + } + }, + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 4, + }, + 'content': [ + { + 'component': 'VSwitch', + 'props': { + 'model': 'openai_proxy', + 'label': '使用代理服务器', + 'v-show': '!use_chatgpt', + 'v-if': '!use_chatgpt' + } + } + ] + }, + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 4, + }, + 'content': [ + { + 'component': 'VSwitch', + 'props': { + 'model': 'compatible', + 'label': '兼容模式', + 'v-show': '!use_chatgpt' + } + } + ] + } + ] + }, + { + 'component': 'VRow', + 'content': [ + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 4 + }, + 'content': [ + { + 'component': 'VTextField', + 'props': { + 'model': 'openai_url', + 'label': 'OpenAI API Url', + 'placeholder': 'https://api.openai.com', + 'v-show': '!use_chatgpt' + } + } + ] + }, + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 4 + }, + 'content': [ + { + 'component': 'VTextField', + 'props': { + 'model': 'openai_key', + 'label': 'API密钥', + 'placeholder': 'sk-xxx', + 'v-show': '!use_chatgpt' + } + } + ] + }, + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 4 + }, + 'content': [ + { + 'component': 'VTextField', + 'props': { + 'model': 'openai_model', + 'label': '自定义模型', + 'placeholder': 'gpt-3.5-turbo', + 'v-show': '!use_chatgpt' + } + } + ] + } + ] + } + ] + } + ] + }, + { + 'component': 'VExpansionPanel', + 'props': {'v-show': 'translate_zh'}, + 'content': [ + { + 'component': 'VExpansionPanelTitle', + 'text': '翻译参数设置' }, { 'component': 'VExpansionPanelText', @@ -1292,27 +1441,6 @@ class AutoSubv2(_PluginBase): ] } ] - }, - { - 'component': 'VRow', - 'content': [ - { - 'component': 'VCol', - 'props': { - 'cols': 12, - }, - 'content': [ - { - 'component': 'VAlert', - 'props': { - 'type': 'info', - 'variant': 'tonal', - 'text': '翻译依赖 ChatGPT 插件配置' - } - } - ] - } - ] } ] } @@ -1341,7 +1469,7 @@ class AutoSubv2(_PluginBase): { 'component': 'a', 'props': { - 'href': 'https://github.com/TimoYoung/MoviePilot-Plugins/blob/main/plugins/autosubv2/README.md', + 'href': 'https://github.com/jxxghp/MoviePilot-Plugins/blob/main/plugins/autosubv2/README.md', 'target': '_blank' }, 'content': [ @@ -1372,6 +1500,13 @@ class AutoSubv2(_PluginBase): "enable_asr": True, "faster_whisper_model": "base", "proxy": True, + "use_chatgpt": True, + "use_chatgpt_trigger": 0, + "openai_proxy": False, + "compatible": False, + "openai_url": "https://api.openai.com", + "openai_key": None, + "openai_model": "gpt-3.5-turbo", "context_window": 5, "max_retries": 3, "enable_merge": False, diff --git a/plugins/autosubv2/translate/openai_translate.py b/plugins/autosubv2/translate/openai_translate.py index 9654a94..dae55a1 100644 --- a/plugins/autosubv2/translate/openai_translate.py +++ b/plugins/autosubv2/translate/openai_translate.py @@ -12,10 +12,11 @@ class OpenAi: _api_url: str = None _model: str = "gpt-3.5-turbo" - def __init__(self, api_key: str = None, api_url: str = None, proxy: dict = None, model: str = None): + def __init__(self, api_key: str = None, api_url: str = None, proxy: dict = None, model: str = None, + compatible: bool = False): self._api_key = api_key self._api_url = api_url - openai.api_base = self._api_url + "/v1" + openai.api_base = self._api_url if compatible else self._api_url + "/v1" openai.api_key = self._api_key if proxy and proxy.get("https"): openai.proxy = proxy.get("https")