Merge pull request #799 from TimoYoung/main

This commit is contained in:
jxxghp
2025-06-06 19:03:12 +08:00
committed by GitHub
4 changed files with 225 additions and 57 deletions

View File

@@ -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": {

View File

@@ -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接口
- 任务完成后调用媒体库刷新

View File

@@ -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,

View File

@@ -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")