mirror of
https://github.com/d0zingcat/MoviePilot-Plugins.git
synced 2026-05-13 23:16:47 +00:00
Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -13,65 +13,72 @@
|
||||
- 支持批量翻译以提高效率
|
||||
- 支持使用滑动窗口配置上下文提高翻译连贯性
|
||||
- 支持多种字幕提取语言偏好设置
|
||||
- 支持监听媒体入库事件自动执行字幕生成
|
||||
- 支持手动触发字幕生成任务
|
||||
- 支持任务队列机制,确保并发安全
|
||||
- 支持任务状态列表展示(等待中 / 进行中 / 已完成 / 失败)
|
||||
|
||||
## 配置说明
|
||||
|
||||
### 基础配置
|
||||
|
||||
| 配置项 | 说明 | 默认值 |
|
||||
|--------|------|--------|
|
||||
| 立即运行一次 | 保存配置后是否立即执行一次任务 | 否 |
|
||||
| 本地字幕提取策略 | 设置字幕提取的优先级策略 | 优先原音字幕 |
|
||||
| 翻译为中文 | 是否在需要时使用大模型将字幕翻译成中文 | 是 |
|
||||
| 发送通知 | 是否发送任务执行通知 | 否 |
|
||||
| 配置项 | 说明 | 默认值 |
|
||||
|----------|------------------------|--------|
|
||||
| 启用插件 | 是否启用插件 | 否 |
|
||||
| 清除历史记录 | 清除已完成的任务记录(完成、跳过或失败) | 否 |
|
||||
| 媒体入库自动执行 | 监听到媒体入库事件后自动执行字幕生成 | 是 |
|
||||
| 手动执行一次 | 保存配置后立即执行一次任务 | 否 |
|
||||
| 发送通知 | 是否发送任务执行通知 | 否 |
|
||||
| 文件大小(MB) | 最小处理的视频文件大小,小于该值的文件不处理 | 10 |
|
||||
| 字幕源语言偏好 | 设置字幕提取的优先级策略 | 优先原音字幕 |
|
||||
| 翻译为中文 | 是否使用大模型将字幕翻译成中文 | 是 |
|
||||
|
||||
### ASR配置
|
||||
### ASR配置(语音识别)
|
||||
|
||||
| 配置项 | 说明 | 默认值 |
|
||||
|--------|------|--------|
|
||||
| 允许从音轨提取字幕 | 是否允许从视频音轨中提取字幕 | 是 |
|
||||
| ASR引擎 | 语音识别引擎 | faster-whisper |
|
||||
| 模型 | 使用的模型大小 | base |
|
||||
| 使用代理下载模型 | 是否使用代理下载模型 | 是 |
|
||||
| 配置项 | 说明 | 默认值 |
|
||||
|---------------------|------------------|------|
|
||||
| 允许从音轨提取字幕 | 是否允许从视频音轨中提取字幕 | 是 |
|
||||
| faster-whisper 模型选择 | 使用的 Whisper 模型大小 | base |
|
||||
| 使用代理下载模型 | 是否使用代理下载模型 | 是 |
|
||||
|
||||
### 翻译配置
|
||||
|
||||
| 配置项 | 说明 | 默认值 |
|
||||
|--------|------|--------|
|
||||
| 启用批量翻译 | 是否启用批量翻译以提高效率 | 是 |
|
||||
| 每批翻译行数 | 每批处理的字幕行数 | 20 |
|
||||
| 上下文窗口大小 | 翻译时考虑的上下文行数 | 5 |
|
||||
| llm请求重试次数 | 翻译失败时的重试次数 | 3 |
|
||||
| 配置项 | 说明 | 默认值 |
|
||||
|-----------|----------------------|-----|
|
||||
| 启用批量翻译 | 是否启用批量翻译以提高效率 | 是 |
|
||||
| 每批翻译行数 | 每批处理的字幕行数 | 20 |
|
||||
| 上下文窗口大小 | 翻译时考虑的上下文行数 | 5 |
|
||||
| LLM请求重试次数 | 翻译失败时的重试次数 | 3 |
|
||||
| 翻译英文时合并整句 | 对英文字幕先合并单词再翻译,提升翻译质量 | 否 |
|
||||
|
||||
### 其他配置
|
||||
|
||||
| 配置项 | 说明 | 默认值 |
|
||||
|--------|------|--------|
|
||||
| 媒体路径 | 要处理的媒体文件或文件夹绝对路径,每行一个 | 空 |
|
||||
| 文件大小(MB) | 最小处理文件大小 | 10 |
|
||||
### 手动运行配置
|
||||
|
||||
| 配置项 | 说明 | 默认值 |
|
||||
|------|-----------------------|-----|
|
||||
| 媒体路径 | 要处理的媒体文件或文件夹绝对路径,每行一个 | 空 |
|
||||
|
||||
## 字幕提取策略说明
|
||||
|
||||
字幕提取优先级:外挂字幕 > 内嵌字幕 > 音轨识别
|
||||
|
||||
字幕提取策略的选择主要取决于视频源语言和大模型的翻译能力。对于包含多语言字幕的非英语视频,建议根据以下原则选择策略:
|
||||
|
||||
1. 仅英文字幕
|
||||
- 仅使用英文字幕作为翻译源
|
||||
- 当视频无英文字幕时,使用ASR提取
|
||||
- 适用于大模型仅支持中英互译的场景
|
||||
- 仅使用英文字幕作为翻译源
|
||||
- 当视频无英文字幕时,使用ASR提取
|
||||
- 适用于大模型仅支持中英互译的场景
|
||||
|
||||
2. 优先英文字幕
|
||||
- 优先使用英文字幕作为翻译源
|
||||
- 无英文字幕时,使用其他语言字幕
|
||||
- 当所有字幕都不存在时,使用ASR提取
|
||||
- 适用于大模型在英译中任务上表现更好的场景
|
||||
- 优先使用英文字幕作为翻译源
|
||||
- 无英文字幕时,使用其他语言字幕
|
||||
- 当所有字幕都不存在时,使用ASR提取
|
||||
- 适用于大模型在英译中任务上表现更好的场景
|
||||
|
||||
3. 优先原音字幕
|
||||
- 优先使用视频原始语言的字幕
|
||||
- 无原音字幕时,使用英文字幕
|
||||
- 当所有字幕都不存在时,使用ASR提取
|
||||
- 适用于大模型支持多语言翻译且翻译质量较好的场景
|
||||
- 优先使用视频原始语言的字幕
|
||||
- 无原音字幕时,使用英文字幕
|
||||
- 当所有字幕都不存在时,使用ASR提取
|
||||
- 适用于大模型支持多语言翻译且翻译质量较好的场景
|
||||
|
||||
## 注意事项
|
||||
|
||||
@@ -79,10 +86,13 @@
|
||||
2. 首次使用音轨识别功能时,会自动从HuggingFace下载模型。开启"使用代理下载模型"选项会使用MP配置的代理。
|
||||
3. 媒体路径支持单个文件或文件夹的绝对路径。选择文件夹时会递归处理其中的所有视频文件,外挂字幕将从媒体文件同级目录中查找
|
||||
4. 批量翻译通过一次处理多行字幕来减少API调用次数,提高效率。如果翻译结果与原文行数不匹配,系统会自动降级为逐行翻译
|
||||
5. 上下文窗口大小和批量翻译行数需要根据大模型的推理能力来调整。当模型能力不足时,过大的批量或上下文窗口可能会影响翻译质量
|
||||
5. 上下文窗口大小和批量翻译行数需要根据大模型的推理能力来调整。当模型能力不足时,过大的批量或上下文窗口可能会影响翻译质量
|
||||
6. 翻译后的中文字幕会打上“机翻”标签。
|
||||
7. 插件运行时会启动一个后台线程用于消费任务队列,插件关闭时会清空队列并终止当前任务。
|
||||
|
||||
|
||||
## todo
|
||||
- 监听媒体入库事件自动调用字幕生成
|
||||
- 任务完成后调用媒体库刷新
|
||||
- 历史任务管理与展示
|
||||
|
||||
- 独立的大模型调用
|
||||
- 工作流/api接口
|
||||
- 任务完成后调用媒体库刷新
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,4 @@
|
||||
import base64
|
||||
import json
|
||||
import requests
|
||||
|
||||
@@ -15,10 +16,20 @@ class NtfyClient:
|
||||
headers = {
|
||||
"Title": title.encode(encoding='utf-8'),
|
||||
"Markdown": "true" if format_as_markdown else "false",
|
||||
"Icon": "https://movie-pilot.org/images/logo.png",
|
||||
}
|
||||
|
||||
if self._token:
|
||||
headers["Authorization"] = "Bearer " + self._token
|
||||
elif self._user and self._password:
|
||||
authStr = self._user + ":" + self._password
|
||||
headers["Authorization"] = "Basic " + base64.b64encode(authStr.encode('utf-8')).decode('utf-8')
|
||||
|
||||
if self._actions:
|
||||
headers["Actions"] = self._actions.encode('utf-8')
|
||||
|
||||
response = json.loads(
|
||||
requests.post(url=self.url, data=message.encode(encoding='utf-8'), headers=headers, auth=self._auth).text
|
||||
requests.post(url=self.url, data=message.encode(encoding='utf-8'), headers=headers).text
|
||||
)
|
||||
return response
|
||||
|
||||
@@ -28,11 +39,16 @@ class NtfyClient:
|
||||
server: str = "https://ntfy.sh",
|
||||
user: str = "",
|
||||
password: str = "",
|
||||
token: str = "",
|
||||
actions: str = "",
|
||||
):
|
||||
self._server = server
|
||||
self._topic = topic
|
||||
self.__set_url(server, topic)
|
||||
self._auth = (user, password)
|
||||
self._user = user
|
||||
self._password = password
|
||||
self._token = token
|
||||
self._actions = actions
|
||||
|
||||
def __set_url(self, server, topic):
|
||||
self.url = server.strip("/") + "/" + topic
|
||||
@@ -46,7 +62,7 @@ class NtfyMsg(_PluginBase):
|
||||
# 插件图标
|
||||
plugin_icon = "Ntfy_A.png"
|
||||
# 插件版本
|
||||
plugin_version = "1.0"
|
||||
plugin_version = "1.1"
|
||||
# 插件作者
|
||||
plugin_author = "lethargicScribe"
|
||||
# 作者主页
|
||||
@@ -64,6 +80,8 @@ class NtfyMsg(_PluginBase):
|
||||
_topic = None
|
||||
_user = None
|
||||
_password = None
|
||||
_token = None
|
||||
_actions = None
|
||||
_msgtypes = []
|
||||
|
||||
def init_plugin(self, config: dict = None):
|
||||
@@ -74,6 +92,8 @@ class NtfyMsg(_PluginBase):
|
||||
self._topic = config.get("topic")
|
||||
self._user = config.get("user")
|
||||
self._password = config.get("password")
|
||||
self._token = config.get("token")
|
||||
self._actions = config.get("actions")
|
||||
|
||||
def get_state(self) -> bool:
|
||||
return self._enabled and (True if self._server and self._topic else False)
|
||||
@@ -194,6 +214,45 @@ class NtfyMsg(_PluginBase):
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VRow',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'md': 6
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VTextField',
|
||||
'props': {
|
||||
'model': 'token',
|
||||
'label': '访问令牌',
|
||||
'placeholder': 'ntfytoken',
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'md': 6
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VTextField',
|
||||
'props': {
|
||||
'model': 'actions',
|
||||
'label': '用户动作',
|
||||
'placeholder': 'ntfyactions',
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VRow',
|
||||
'content': [
|
||||
@@ -217,6 +276,48 @@ class NtfyMsg(_PluginBase):
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VRow',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VAlert',
|
||||
'props': {
|
||||
'type': 'info',
|
||||
'variant': 'tonal',
|
||||
'text': '用户或Token创建参考:https://docs.ntfy.sh/config/#access-control'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VRow',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VAlert',
|
||||
'props': {
|
||||
'type': 'info',
|
||||
'variant': 'tonal',
|
||||
'text': '用户动作创建参考:https://docs.ntfy.sh/publish/?h=action#using-a-header'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
], {
|
||||
@@ -226,6 +327,8 @@ class NtfyMsg(_PluginBase):
|
||||
'topic': 'MoviePilot',
|
||||
'user': '',
|
||||
'password': '',
|
||||
'token': '',
|
||||
'actions': '',
|
||||
}
|
||||
|
||||
def get_page(self) -> List[dict]:
|
||||
@@ -266,7 +369,11 @@ class NtfyMsg(_PluginBase):
|
||||
try:
|
||||
if not self._server or not self._topic:
|
||||
return False, "参数未配置"
|
||||
ntfy = NtfyClient(server=self._server, topic=self._topic, user=self._user, password=self._password)
|
||||
ntfy = NtfyClient(
|
||||
server=self._server, topic=self._topic,
|
||||
user=self._user, password=self._password,
|
||||
token=self._token, actions=self._actions
|
||||
)
|
||||
ntfy.send(title=title, message=text, format_as_markdown=True)
|
||||
|
||||
except Exception as msg_e:
|
||||
|
||||
Reference in New Issue
Block a user