mirror of
https://github.com/HiMeditator/auto-caption.git
synced 2026-02-04 04:14:42 +08:00
feat(translation): 添加非实时翻译功能用户界面组件
This commit is contained in:
@@ -153,4 +153,18 @@
|
|||||||
### 优化体验
|
### 优化体验
|
||||||
|
|
||||||
- 优化软件用户界面的部分组件
|
- 优化软件用户界面的部分组件
|
||||||
- 更清晰的日志输出
|
- 更清晰的日志输出
|
||||||
|
|
||||||
|
|
||||||
|
## v0.8.0
|
||||||
|
|
||||||
|
2025-09-??
|
||||||
|
|
||||||
|
### 新增功能
|
||||||
|
|
||||||
|
- 字幕引擎添加超时关闭功能:如果在规定时间字幕引擎没有启动成功会自动关闭、在字幕引擎启动过程中也可选择关闭字幕引擎
|
||||||
|
- 添加非实时翻译功能:支持调用 Ollama 本地模型进行翻译、支持调用 Google 翻译 API 进行翻译
|
||||||
|
|
||||||
|
### 优化体验
|
||||||
|
|
||||||
|
- 带有额外信息的标签颜色改为与主题色一致
|
||||||
@@ -92,6 +92,17 @@ Python 端监听到的音频流转换为的字幕数据。
|
|||||||
|
|
||||||
Python 端打印的提示信息,会计入日志。
|
Python 端打印的提示信息,会计入日志。
|
||||||
|
|
||||||
|
### `warn`
|
||||||
|
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
command: "warn",
|
||||||
|
content: string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Python 端打印的警告信息,会计入日志。
|
||||||
|
|
||||||
### `error`
|
### `error`
|
||||||
|
|
||||||
```js
|
```js
|
||||||
@@ -101,7 +112,7 @@ Python 端打印的提示信息,会计入日志。
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Python 端打印的错误信息,该错误信息需要在前端弹窗显示。
|
Python 端打印的错误信息,该错误信息会在前端弹窗显示。
|
||||||
|
|
||||||
### `usage`
|
### `usage`
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import time
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from vosk import Model, KaldiRecognizer, SetLogLevel
|
from vosk import Model, KaldiRecognizer, SetLogLevel
|
||||||
from utils import stdout_cmd, stdout_obj, google_translate
|
from utils import stdout_cmd, stdout_obj, google_translate, ollama_translate
|
||||||
|
|
||||||
|
|
||||||
class VoskRecognizer:
|
class VoskRecognizer:
|
||||||
@@ -14,8 +14,10 @@ class VoskRecognizer:
|
|||||||
初始化参数:
|
初始化参数:
|
||||||
model_path: Vosk 识别模型路径
|
model_path: Vosk 识别模型路径
|
||||||
target: 翻译目标语言
|
target: 翻译目标语言
|
||||||
|
trans_model: 翻译模型名称
|
||||||
|
ollama_name: Ollama 模型名称
|
||||||
"""
|
"""
|
||||||
def __init__(self, model_path: str, target: str | None):
|
def __init__(self, model_path: str, target: str | None, trans_model: str, ollama_name: str):
|
||||||
SetLogLevel(-1)
|
SetLogLevel(-1)
|
||||||
if model_path.startswith('"'):
|
if model_path.startswith('"'):
|
||||||
model_path = model_path[1:]
|
model_path = model_path[1:]
|
||||||
@@ -23,8 +25,12 @@ class VoskRecognizer:
|
|||||||
model_path = model_path[:-1]
|
model_path = model_path[:-1]
|
||||||
self.model_path = model_path
|
self.model_path = model_path
|
||||||
self.target = target
|
self.target = target
|
||||||
|
if trans_model == 'google':
|
||||||
|
self.trans_func = google_translate
|
||||||
|
else:
|
||||||
|
self.trans_func = ollama_translate
|
||||||
|
self.ollama_name = ollama_name
|
||||||
self.time_str = ''
|
self.time_str = ''
|
||||||
self.trans_time = time.time()
|
|
||||||
self.cur_id = 0
|
self.cur_id = 0
|
||||||
self.prev_content = ''
|
self.prev_content = ''
|
||||||
|
|
||||||
@@ -58,8 +64,8 @@ class VoskRecognizer:
|
|||||||
if self.target:
|
if self.target:
|
||||||
self.trans_time = time.time()
|
self.trans_time = time.time()
|
||||||
th = threading.Thread(
|
th = threading.Thread(
|
||||||
target=google_translate,
|
target=self.trans_func,
|
||||||
args=(caption['text'], self.target, self.time_str)
|
args=(self.ollama_name, self.target, caption['text'], self.time_str)
|
||||||
)
|
)
|
||||||
th.start()
|
th.start()
|
||||||
else:
|
else:
|
||||||
@@ -75,13 +81,6 @@ class VoskRecognizer:
|
|||||||
self.prev_content = content
|
self.prev_content = content
|
||||||
|
|
||||||
stdout_obj(caption)
|
stdout_obj(caption)
|
||||||
if self.target and time.time() - self.trans_time > 2.0:
|
|
||||||
self.trans_time = time.time()
|
|
||||||
th = threading.Thread(
|
|
||||||
target=google_translate,
|
|
||||||
args=(caption['text'], self.target, self.time_str)
|
|
||||||
)
|
|
||||||
th.start()
|
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
"""停止 Vosk 引擎"""
|
"""停止 Vosk 引擎"""
|
||||||
|
|||||||
@@ -44,10 +44,13 @@ def main_gummy(s: str, t: str, a: int, c: int, k: str):
|
|||||||
engine.stop()
|
engine.stop()
|
||||||
|
|
||||||
|
|
||||||
def main_vosk(a: int, c: int, m: str, t: str):
|
def main_vosk(a: int, c: int, m: str, t: str, tm: str, on: str):
|
||||||
global thread_data
|
global thread_data
|
||||||
stream = AudioStream(a, c)
|
stream = AudioStream(a, c)
|
||||||
engine = VoskRecognizer(m, None if t == 'none' else t)
|
engine = VoskRecognizer(
|
||||||
|
m, None if t == 'none' else t,
|
||||||
|
tm, on
|
||||||
|
)
|
||||||
|
|
||||||
stream.open_stream()
|
stream.open_stream()
|
||||||
engine.start()
|
engine.start()
|
||||||
@@ -78,6 +81,8 @@ if __name__ == "__main__":
|
|||||||
parser.add_argument('-k', '--api_key', default='', help='API KEY for Gummy model')
|
parser.add_argument('-k', '--api_key', default='', help='API KEY for Gummy model')
|
||||||
# vosk only
|
# vosk only
|
||||||
parser.add_argument('-m', '--model_path', default='', help='The path to the vosk model.')
|
parser.add_argument('-m', '--model_path', default='', help='The path to the vosk model.')
|
||||||
|
parser.add_argument('-tm', '--translation_model', default='', help='Google translate API KEY')
|
||||||
|
parser.add_argument('-on', '--ollama_name', default='', help='Ollama model name for translation')
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
if int(args.port) == 0:
|
if int(args.port) == 0:
|
||||||
@@ -98,7 +103,9 @@ if __name__ == "__main__":
|
|||||||
int(args.audio_type),
|
int(args.audio_type),
|
||||||
int(args.chunk_rate),
|
int(args.chunk_rate),
|
||||||
args.model_path,
|
args.model_path,
|
||||||
args.target_language
|
args.target_language,
|
||||||
|
args.translation_model,
|
||||||
|
args.ollama_name
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
raise ValueError('Invalid caption engine specified.')
|
raise ValueError('Invalid caption engine specified.')
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ from ollama import chat
|
|||||||
from ollama import ChatResponse
|
from ollama import ChatResponse
|
||||||
import asyncio
|
import asyncio
|
||||||
from googletrans import Translator
|
from googletrans import Translator
|
||||||
from .sysout import stdout, stdout_obj
|
from .sysout import stdout_cmd, stdout_obj
|
||||||
|
|
||||||
lang_map = {
|
lang_map = {
|
||||||
'en': 'English',
|
'en': 'English',
|
||||||
@@ -13,38 +13,29 @@ lang_map = {
|
|||||||
'ru': 'Russian',
|
'ru': 'Russian',
|
||||||
'ja': 'Japanese',
|
'ja': 'Japanese',
|
||||||
'ko': 'Korean',
|
'ko': 'Korean',
|
||||||
'zh': 'Chinese'
|
'zh-cn': 'Chinese'
|
||||||
}
|
}
|
||||||
|
|
||||||
def ollama_translate(model: str, target: str, text: str, chunk_size = 3):
|
def ollama_translate(model: str, target: str, text: str, time_s: str):
|
||||||
stream = chat(
|
response: ChatResponse = chat(
|
||||||
model=model,
|
model=model,
|
||||||
messages=[
|
messages=[
|
||||||
{"role": "system", "content": f"/no_think Translate the following content into {lang_map[target]}, and do not output any additional information."},
|
{"role": "system", "content": f"/no_think Translate the following content into {lang_map[target]}, and do not output any additional information."},
|
||||||
{"role": "user", "content": text}
|
{"role": "user", "content": text}
|
||||||
],
|
]
|
||||||
stream=True
|
|
||||||
)
|
)
|
||||||
chunk_content = ""
|
content = response.message.content or ""
|
||||||
in_thinking = False
|
if content.startswith('<think>'):
|
||||||
count = 0
|
index = content.find('</think>')
|
||||||
for chunk in stream:
|
if index != -1:
|
||||||
if count == 0 and chunk['message']['content'].startswith("<think>"):
|
content = content[index+8:]
|
||||||
in_thinking = True
|
stdout_obj({
|
||||||
if in_thinking:
|
"command": "translation",
|
||||||
if "</think>" in chunk['message']['content']:
|
"time_s": time_s,
|
||||||
in_thinking = False
|
"translation": content.strip()
|
||||||
continue
|
})
|
||||||
chunk_content += ' '.join(chunk['message']['content'].split('\n'))
|
|
||||||
count += 1
|
|
||||||
if count % chunk_size == 0:
|
|
||||||
print(chunk_content, end='')
|
|
||||||
chunk_content = ""
|
|
||||||
count = 0
|
|
||||||
if chunk_content:
|
|
||||||
print(chunk_content)
|
|
||||||
|
|
||||||
def google_translate(text: str, target: str, time_s: str):
|
def google_translate(model: str, target: str, text: str, time_s: str):
|
||||||
translator = Translator()
|
translator = Translator()
|
||||||
try:
|
try:
|
||||||
res = asyncio.run(translator.translate(text, dest=target))
|
res = asyncio.run(translator.translate(text, dest=target))
|
||||||
@@ -54,4 +45,4 @@ def google_translate(text: str, target: str, time_s: str):
|
|||||||
"translation": res.text
|
"translation": res.text
|
||||||
})
|
})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
stdout(f"Google Translation Request failed: {str(e)}")
|
stdout_cmd("warn", f"Google translation request failed, please check your network connection...")
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ export interface Controls {
|
|||||||
engineEnabled: boolean,
|
engineEnabled: boolean,
|
||||||
sourceLang: string,
|
sourceLang: string,
|
||||||
targetLang: string,
|
targetLang: string,
|
||||||
|
transModel: string,
|
||||||
|
ollamaName: string,
|
||||||
engine: string,
|
engine: string,
|
||||||
audio: 0 | 1,
|
audio: 0 | 1,
|
||||||
translation: boolean,
|
translation: boolean,
|
||||||
@@ -46,11 +48,6 @@ export interface CaptionItem {
|
|||||||
translation: string
|
translation: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CaptionTranslation {
|
|
||||||
time_s: string,
|
|
||||||
translation: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SoftwareLogItem {
|
export interface SoftwareLogItem {
|
||||||
type: "INFO" | "WARN" | "ERROR",
|
type: "INFO" | "WARN" | "ERROR",
|
||||||
index: number,
|
index: number,
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
import {
|
import {
|
||||||
UILanguage, UITheme, Styles, Controls,
|
UILanguage, UITheme, Styles, Controls,
|
||||||
CaptionItem, CaptionTranslation,
|
CaptionItem, FullConfig, SoftwareLogItem
|
||||||
FullConfig, SoftwareLogItem
|
|
||||||
} from '../types'
|
} from '../types'
|
||||||
import { Log } from './Log'
|
import { Log } from './Log'
|
||||||
import { app, BrowserWindow } from 'electron'
|
import { app, BrowserWindow } from 'electron'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
|
|
||||||
|
interface CaptionTranslation {
|
||||||
|
time_s: string,
|
||||||
|
translation: string
|
||||||
|
}
|
||||||
|
|
||||||
const defaultStyles: Styles = {
|
const defaultStyles: Styles = {
|
||||||
lineBreak: 1,
|
lineBreak: 1,
|
||||||
fontFamily: 'sans-serif',
|
fontFamily: 'sans-serif',
|
||||||
@@ -32,6 +36,8 @@ const defaultStyles: Styles = {
|
|||||||
const defaultControls: Controls = {
|
const defaultControls: Controls = {
|
||||||
sourceLang: 'en',
|
sourceLang: 'en',
|
||||||
targetLang: 'zh',
|
targetLang: 'zh',
|
||||||
|
transModel: 'ollama',
|
||||||
|
ollamaName: '',
|
||||||
engine: 'gummy',
|
engine: 'gummy',
|
||||||
audio: 0,
|
audio: 0,
|
||||||
engineEnabled: false,
|
engineEnabled: false,
|
||||||
|
|||||||
@@ -81,7 +81,9 @@ export class CaptionEngine {
|
|||||||
}
|
}
|
||||||
else if(allConfig.controls.engine === 'vosk'){
|
else if(allConfig.controls.engine === 'vosk'){
|
||||||
this.command.push('-e', 'vosk')
|
this.command.push('-e', 'vosk')
|
||||||
this.command.push('-m', `"${allConfig.controls.modelPath}"`)
|
this.command.push('-m', `"${allConfig.controls.modelPath}"`)
|
||||||
|
this.command.push('-tm', allConfig.controls.transModel)
|
||||||
|
this.command.push('-on', allConfig.controls.ollamaName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Log.info('Engine Path:', this.appPath)
|
Log.info('Engine Path:', this.appPath)
|
||||||
@@ -257,6 +259,9 @@ function handleEngineData(data: any) {
|
|||||||
else if(data.command === 'info') {
|
else if(data.command === 'info') {
|
||||||
Log.info('Engine Info:', data.content)
|
Log.info('Engine Info:', data.content)
|
||||||
}
|
}
|
||||||
|
else if(data.command === 'warn') {
|
||||||
|
Log.warn('Engine Warn:', data.content)
|
||||||
|
}
|
||||||
else if(data.command === 'error') {
|
else if(data.command === 'error') {
|
||||||
Log.error('Engine Error:', data.content)
|
Log.error('Engine Error:', data.content)
|
||||||
controlWindow.sendErrorMessage(/*i18n('engine.error') +*/ data.content)
|
controlWindow.sendErrorMessage(/*i18n('engine.error') +*/ data.content)
|
||||||
|
|||||||
@@ -5,6 +5,14 @@
|
|||||||
<a @click="applyChange">{{ $t('engine.applyChange') }}</a> |
|
<a @click="applyChange">{{ $t('engine.applyChange') }}</a> |
|
||||||
<a @click="cancelChange">{{ $t('engine.cancelChange') }}</a>
|
<a @click="cancelChange">{{ $t('engine.cancelChange') }}</a>
|
||||||
</template>
|
</template>
|
||||||
|
<div class="input-item">
|
||||||
|
<span class="input-label">{{ $t('engine.captionEngine') }}</span>
|
||||||
|
<a-select
|
||||||
|
class="input-area"
|
||||||
|
v-model:value="currentEngine"
|
||||||
|
:options="captionEngine"
|
||||||
|
></a-select>
|
||||||
|
</div>
|
||||||
<div class="input-item">
|
<div class="input-item">
|
||||||
<span class="input-label">{{ $t('engine.sourceLang') }}</span>
|
<span class="input-label">{{ $t('engine.sourceLang') }}</span>
|
||||||
<a-select
|
<a-select
|
||||||
@@ -22,14 +30,28 @@
|
|||||||
:options="langList.filter((item) => item.value !== 'auto')"
|
:options="langList.filter((item) => item.value !== 'auto')"
|
||||||
></a-select>
|
></a-select>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-item">
|
<div class="input-item" v-if="transModel">
|
||||||
<span class="input-label">{{ $t('engine.captionEngine') }}</span>
|
<span class="input-label">{{ $t('engine.transModel') }}</span>
|
||||||
<a-select
|
<a-select
|
||||||
class="input-area"
|
class="input-area"
|
||||||
v-model:value="currentEngine"
|
v-model:value="currentTransModel"
|
||||||
:options="captionEngine"
|
:options="transModel"
|
||||||
></a-select>
|
></a-select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="input-item" v-if="transModel && currentTransModel === 'ollama'">
|
||||||
|
<a-popover placement="right">
|
||||||
|
<template #content>
|
||||||
|
<p class="label-hover-info">{{ $t('engine.ollamaNote') }}</p>
|
||||||
|
</template>
|
||||||
|
<span class="input-label info-label"
|
||||||
|
:style="{color: uiColor}"
|
||||||
|
>{{ $t('engine.ollama') }}</span>
|
||||||
|
</a-popover>
|
||||||
|
<a-input
|
||||||
|
class="input-area"
|
||||||
|
v-model:value="currentOllamaName"
|
||||||
|
></a-input>
|
||||||
|
</div>
|
||||||
<div class="input-item">
|
<div class="input-item">
|
||||||
<span class="input-label">{{ $t('engine.audioType') }}</span>
|
<span class="input-label">{{ $t('engine.audioType') }}</span>
|
||||||
<a-select
|
<a-select
|
||||||
@@ -80,11 +102,13 @@
|
|||||||
|
|
||||||
<a-card size="small" :title="$t('engine.showMore')" v-show="showMore" style="margin-top:10px;">
|
<a-card size="small" :title="$t('engine.showMore')" v-show="showMore" style="margin-top:10px;">
|
||||||
<div class="input-item">
|
<div class="input-item">
|
||||||
<a-popover>
|
<a-popover placement="right">
|
||||||
<template #content>
|
<template #content>
|
||||||
<p class="label-hover-info">{{ $t('engine.apikeyInfo') }}</p>
|
<p class="label-hover-info">{{ $t('engine.apikeyInfo') }}</p>
|
||||||
</template>
|
</template>
|
||||||
<span class="input-label info-label">{{ $t('engine.apikey') }}</span>
|
<span class="input-label info-label"
|
||||||
|
:style="{color: uiColor}"
|
||||||
|
>{{ $t('engine.apikey') }}</span>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
<a-input
|
<a-input
|
||||||
class="input-area"
|
class="input-area"
|
||||||
@@ -93,14 +117,17 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-item">
|
<div class="input-item">
|
||||||
<a-popover>
|
<a-popover placement="right">
|
||||||
<template #content>
|
<template #content>
|
||||||
<p class="label-hover-info">{{ $t('engine.modelPathInfo') }}</p>
|
<p class="label-hover-info">{{ $t('engine.modelPathInfo') }}</p>
|
||||||
</template>
|
</template>
|
||||||
<span class="input-label info-label">{{ $t('engine.modelPath') }}</span>
|
<span class="input-label info-label"
|
||||||
|
:style="{color: uiColor}"
|
||||||
|
>{{ $t('engine.modelPath') }}</span>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
<span
|
<span
|
||||||
class="input-folder"
|
class="input-folder"
|
||||||
|
:style="{color: uiColor}"
|
||||||
@click="selectFolderPath"
|
@click="selectFolderPath"
|
||||||
><span><FolderOpenOutlined /></span></span>
|
><span><FolderOpenOutlined /></span></span>
|
||||||
<a-input
|
<a-input
|
||||||
@@ -110,13 +137,13 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-item">
|
<div class="input-item">
|
||||||
<a-popover>
|
<a-popover placement="right">
|
||||||
<template #content>
|
<template #content>
|
||||||
<p class="label-hover-info">{{ $t('engine.startTimeoutInfo') }}</p>
|
<p class="label-hover-info">{{ $t('engine.startTimeoutInfo') }}</p>
|
||||||
</template>
|
</template>
|
||||||
<span
|
<span
|
||||||
class="input-label info-label"
|
class="input-label info-label"
|
||||||
style="vertical-align: middle;"
|
:style="{color: uiColor, verticalAlign: 'middle'}"
|
||||||
>{{ $t('engine.startTimeout') }}</span>
|
>{{ $t('engine.startTimeout') }}</span>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
<a-input-number
|
<a-input-number
|
||||||
@@ -134,12 +161,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, watch } from 'vue'
|
import { ref, computed, watch, h } from 'vue'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { useGeneralSettingStore } from '@renderer/stores/generalSetting'
|
import { useGeneralSettingStore } from '@renderer/stores/generalSetting'
|
||||||
import { useEngineControlStore } from '@renderer/stores/engineControl'
|
import { useEngineControlStore } from '@renderer/stores/engineControl'
|
||||||
import { notification } from 'ant-design-vue'
|
import { notification } from 'ant-design-vue'
|
||||||
import { FolderOpenOutlined ,InfoCircleOutlined } from '@ant-design/icons-vue';
|
import { ExclamationCircleOutlined, FolderOpenOutlined ,InfoCircleOutlined } from '@ant-design/icons-vue';
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
@@ -148,11 +175,16 @@ const showMore = ref(false)
|
|||||||
const engineControl = useEngineControlStore()
|
const engineControl = useEngineControlStore()
|
||||||
const { captionEngine, audioType, changeSignal } = storeToRefs(engineControl)
|
const { captionEngine, audioType, changeSignal } = storeToRefs(engineControl)
|
||||||
|
|
||||||
|
const generalSetting = useGeneralSettingStore()
|
||||||
|
const { uiColor } = storeToRefs(generalSetting)
|
||||||
|
|
||||||
const currentSourceLang = ref('auto')
|
const currentSourceLang = ref('auto')
|
||||||
const currentTargetLang = ref('zh')
|
const currentTargetLang = ref('zh')
|
||||||
const currentEngine = ref<string>('gummy')
|
const currentEngine = ref<string>('gummy')
|
||||||
const currentAudio = ref<0 | 1>(0)
|
const currentAudio = ref<0 | 1>(0)
|
||||||
const currentTranslation = ref<boolean>(false)
|
const currentTranslation = ref<boolean>(true)
|
||||||
|
const currentTransModel = ref('ollama')
|
||||||
|
const currentOllamaName = ref('')
|
||||||
const currentAPI_KEY = ref<string>('')
|
const currentAPI_KEY = ref<string>('')
|
||||||
const currentModelPath = ref<string>('')
|
const currentModelPath = ref<string>('')
|
||||||
const currentCustomized = ref<boolean>(false)
|
const currentCustomized = ref<boolean>(false)
|
||||||
@@ -169,9 +201,33 @@ const langList = computed(() => {
|
|||||||
return []
|
return []
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const transModel = computed(() => {
|
||||||
|
for(let item of captionEngine.value){
|
||||||
|
if(item.value === currentEngine.value) {
|
||||||
|
return item.transModel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
})
|
||||||
|
|
||||||
function applyChange(){
|
function applyChange(){
|
||||||
|
if(
|
||||||
|
currentTranslation.value && transModel.value &&
|
||||||
|
currentTransModel.value === 'ollama' && !currentOllamaName.value.trim()
|
||||||
|
) {
|
||||||
|
notification.open({
|
||||||
|
message: t('noti.ollamaNameNull'),
|
||||||
|
description: t('noti.ollamaNameNullNote'),
|
||||||
|
duration: null,
|
||||||
|
icon: () => h(ExclamationCircleOutlined, { style: 'color: #ff4d4f' })
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
engineControl.sourceLang = currentSourceLang.value
|
engineControl.sourceLang = currentSourceLang.value
|
||||||
engineControl.targetLang = currentTargetLang.value
|
engineControl.targetLang = currentTargetLang.value
|
||||||
|
engineControl.transModel = currentTransModel.value
|
||||||
|
engineControl.ollamaName = currentOllamaName.value
|
||||||
engineControl.engine = currentEngine.value
|
engineControl.engine = currentEngine.value
|
||||||
engineControl.audio = currentAudio.value
|
engineControl.audio = currentAudio.value
|
||||||
engineControl.translation = currentTranslation.value
|
engineControl.translation = currentTranslation.value
|
||||||
@@ -194,6 +250,8 @@ function applyChange(){
|
|||||||
function cancelChange(){
|
function cancelChange(){
|
||||||
currentSourceLang.value = engineControl.sourceLang
|
currentSourceLang.value = engineControl.sourceLang
|
||||||
currentTargetLang.value = engineControl.targetLang
|
currentTargetLang.value = engineControl.targetLang
|
||||||
|
currentTransModel.value = engineControl.transModel
|
||||||
|
currentOllamaName.value = engineControl.ollamaName
|
||||||
currentEngine.value = engineControl.engine
|
currentEngine.value = engineControl.engine
|
||||||
currentAudio.value = engineControl.audio
|
currentAudio.value = engineControl.audio
|
||||||
currentTranslation.value = engineControl.translation
|
currentTranslation.value = engineControl.translation
|
||||||
@@ -243,8 +301,8 @@ watch(currentEngine, (val) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.info-label {
|
.info-label {
|
||||||
color: #1677ff;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-folder {
|
.input-folder {
|
||||||
@@ -255,20 +313,12 @@ watch(currentEngine, (val) => {
|
|||||||
transition: all 0.25s;
|
transition: all 0.25s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-folder>span {
|
|
||||||
padding: 0 2px;
|
|
||||||
border: 2px solid #1677ff;
|
|
||||||
color: #1677ff;
|
|
||||||
border-radius: 30%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-folder:hover {
|
.input-folder:hover {
|
||||||
transform: scale(1.1);
|
transform: scale(1.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.customize-note {
|
.customize-note {
|
||||||
padding: 10px 10px 0;
|
padding: 10px 10px 0;
|
||||||
color: red;
|
|
||||||
max-width: min(40vw, 480px);
|
max-width: min(40vw, 480px);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ export const engines = {
|
|||||||
{ value: 'ru', label: '俄语' },
|
{ value: 'ru', label: '俄语' },
|
||||||
{ value: 'es', label: '西班牙语' },
|
{ value: 'es', label: '西班牙语' },
|
||||||
{ value: 'it', label: '意大利语' },
|
{ value: 'it', label: '意大利语' },
|
||||||
|
],
|
||||||
|
transModel: [
|
||||||
|
{ value: 'ollama', label: 'Ollama 本地模型' },
|
||||||
|
{ value: 'google', label: 'Google API 调用' },
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -64,6 +68,10 @@ export const engines = {
|
|||||||
{ value: 'ru', label: 'Russian' },
|
{ value: 'ru', label: 'Russian' },
|
||||||
{ value: 'es', label: 'Spanish' },
|
{ value: 'es', label: 'Spanish' },
|
||||||
{ value: 'it', label: 'Italian' },
|
{ value: 'it', label: 'Italian' },
|
||||||
|
],
|
||||||
|
transModel: [
|
||||||
|
{ value: 'ollama', label: 'Ollama Local Model' },
|
||||||
|
{ value: 'google', label: 'Google API Call' },
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -98,8 +106,11 @@ export const engines = {
|
|||||||
{ value: 'ru', label: 'ロシア語' },
|
{ value: 'ru', label: 'ロシア語' },
|
||||||
{ value: 'es', label: 'スペイン語' },
|
{ value: 'es', label: 'スペイン語' },
|
||||||
{ value: 'it', label: 'イタリア語' },
|
{ value: 'it', label: 'イタリア語' },
|
||||||
|
],
|
||||||
|
transModel: [
|
||||||
|
{ value: 'ollama', label: 'Ollama ローカルモデル' },
|
||||||
|
{ value: 'google', label: 'Google API 呼び出し' },
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,9 @@ export default {
|
|||||||
"changeInfo": "If the caption engine is already running, you need to restart it for the changes to take effect.",
|
"changeInfo": "If the caption engine is already running, you need to restart it for the changes to take effect.",
|
||||||
"styleChange": "Caption Style Changed",
|
"styleChange": "Caption Style Changed",
|
||||||
"styleInfo": "Caption style changes have been saved and applied.",
|
"styleInfo": "Caption style changes have been saved and applied.",
|
||||||
"engineStartTimeout": "Caption engine startup timeout, automatically force stopped"
|
"engineStartTimeout": "Caption engine startup timeout, automatically force stopped",
|
||||||
|
"ollamaNameNull": "'Ollama' Field is Empty",
|
||||||
|
"ollamaNameNullNote": "When selecting Ollama model as the translation model, the 'Ollama' field cannot be empty and must be filled with the name of a locally configured Ollama model."
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
"title": "General Settings",
|
"title": "General Settings",
|
||||||
@@ -47,6 +49,9 @@ export default {
|
|||||||
"cancelChange": "Cancel Changes",
|
"cancelChange": "Cancel Changes",
|
||||||
"sourceLang": "Source",
|
"sourceLang": "Source",
|
||||||
"transLang": "Translation",
|
"transLang": "Translation",
|
||||||
|
"transModel": "Model",
|
||||||
|
"ollama": "Ollama",
|
||||||
|
"ollamaNote": "To use for translation, the name of the local Ollama model that will call the service on the default port. It is recommended to use a non-inference model with less than 1B parameters.",
|
||||||
"captionEngine": "Engine",
|
"captionEngine": "Engine",
|
||||||
"audioType": "Audio Type",
|
"audioType": "Audio Type",
|
||||||
"systemOutput": "System Audio Output (Speaker)",
|
"systemOutput": "System Audio Output (Speaker)",
|
||||||
|
|||||||
@@ -28,7 +28,9 @@ export default {
|
|||||||
"changeInfo": "字幕エンジンがすでに起動している場合、変更を有効にするには再起動が必要です。",
|
"changeInfo": "字幕エンジンがすでに起動している場合、変更を有効にするには再起動が必要です。",
|
||||||
"styleChange": "字幕のスタイルが変更されました",
|
"styleChange": "字幕のスタイルが変更されました",
|
||||||
"styleInfo": "字幕のスタイル変更が保存され、適用されました",
|
"styleInfo": "字幕のスタイル変更が保存され、適用されました",
|
||||||
"engineStartTimeout": "字幕エンジンの起動がタイムアウトしました。自動的に強制停止しました"
|
"engineStartTimeout": "字幕エンジンの起動がタイムアウトしました。自動的に強制停止しました",
|
||||||
|
"ollamaNameNull": "Ollama フィールドが空です",
|
||||||
|
"ollamaNameNullNote": "Ollama モデルを翻訳モデルとして選択する場合、Ollama フィールドは空にできません。ローカルで設定された Ollama モデルの名前を入力してください。"
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
"title": "一般設定",
|
"title": "一般設定",
|
||||||
@@ -47,6 +49,9 @@ export default {
|
|||||||
"cancelChange": "変更をキャンセル",
|
"cancelChange": "変更をキャンセル",
|
||||||
"sourceLang": "ソース言語",
|
"sourceLang": "ソース言語",
|
||||||
"transLang": "翻訳言語",
|
"transLang": "翻訳言語",
|
||||||
|
"transModel": "翻訳モデル",
|
||||||
|
"ollama": "Ollama",
|
||||||
|
"ollamaNote": "翻訳に使用する、デフォルトポートでサービスを呼び出すローカルOllamaモデルの名前。1B 未満のパラメータを持つ非推論モデルの使用を推奨します。",
|
||||||
"captionEngine": "エンジン",
|
"captionEngine": "エンジン",
|
||||||
"audioType": "オーディオ",
|
"audioType": "オーディオ",
|
||||||
"systemOutput": "システムオーディオ出力(スピーカー)",
|
"systemOutput": "システムオーディオ出力(スピーカー)",
|
||||||
|
|||||||
@@ -28,7 +28,9 @@ export default {
|
|||||||
"changeInfo": "如果字幕引擎已经启动,需要重启字幕引擎修改才会生效",
|
"changeInfo": "如果字幕引擎已经启动,需要重启字幕引擎修改才会生效",
|
||||||
"styleChange": "字幕样式已修改",
|
"styleChange": "字幕样式已修改",
|
||||||
"styleInfo": "字幕样式修改已经保存并生效",
|
"styleInfo": "字幕样式修改已经保存并生效",
|
||||||
"engineStartTimeout": "字幕引擎启动超时,已自动强制停止"
|
"engineStartTimeout": "字幕引擎启动超时,已自动强制停止",
|
||||||
|
"ollamaNameNull": "Ollama 字段为空",
|
||||||
|
"ollamaNameNullNote": "选择 Ollama 模型作为翻译模型时,Ollama 字段不能为空,需要填写本地已经配置好的 Ollama 模型的名称。"
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
"title": "通用设置",
|
"title": "通用设置",
|
||||||
@@ -47,6 +49,9 @@ export default {
|
|||||||
"cancelChange": "取消更改",
|
"cancelChange": "取消更改",
|
||||||
"sourceLang": "源语言",
|
"sourceLang": "源语言",
|
||||||
"transLang": "翻译语言",
|
"transLang": "翻译语言",
|
||||||
|
"transModel": "翻译模型",
|
||||||
|
"ollama": "Ollama",
|
||||||
|
"ollamaNote": "要使用的进行翻译的本地 Ollama 模型的名称,将调用默认端口的服务,建议使用参数量小于 1B 的非推理模型。",
|
||||||
"captionEngine": "字幕引擎",
|
"captionEngine": "字幕引擎",
|
||||||
"audioType": "音频类型",
|
"audioType": "音频类型",
|
||||||
"systemOutput": "系统音频输出(扬声器)",
|
"systemOutput": "系统音频输出(扬声器)",
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ export const useEngineControlStore = defineStore('engineControl', () => {
|
|||||||
const engineEnabled = ref(false)
|
const engineEnabled = ref(false)
|
||||||
const sourceLang = ref<string>('en')
|
const sourceLang = ref<string>('en')
|
||||||
const targetLang = ref<string>('zh')
|
const targetLang = ref<string>('zh')
|
||||||
|
const transModel = ref<string>('ollama')
|
||||||
|
const ollamaName = ref<string>('')
|
||||||
const engine = ref<string>('gummy')
|
const engine = ref<string>('gummy')
|
||||||
const audio = ref<0 | 1>(0)
|
const audio = ref<0 | 1>(0)
|
||||||
const translation = ref<boolean>(true)
|
const translation = ref<boolean>(true)
|
||||||
@@ -37,6 +39,8 @@ export const useEngineControlStore = defineStore('engineControl', () => {
|
|||||||
engineEnabled: engineEnabled.value,
|
engineEnabled: engineEnabled.value,
|
||||||
sourceLang: sourceLang.value,
|
sourceLang: sourceLang.value,
|
||||||
targetLang: targetLang.value,
|
targetLang: targetLang.value,
|
||||||
|
transModel: transModel.value,
|
||||||
|
ollamaName: ollamaName.value,
|
||||||
engine: engine.value,
|
engine: engine.value,
|
||||||
audio: audio.value,
|
audio: audio.value,
|
||||||
translation: translation.value,
|
translation: translation.value,
|
||||||
@@ -68,6 +72,8 @@ export const useEngineControlStore = defineStore('engineControl', () => {
|
|||||||
}
|
}
|
||||||
sourceLang.value = controls.sourceLang
|
sourceLang.value = controls.sourceLang
|
||||||
targetLang.value = controls.targetLang
|
targetLang.value = controls.targetLang
|
||||||
|
transModel.value = controls.transModel
|
||||||
|
ollamaName.value = controls.ollamaName
|
||||||
engine.value = controls.engine
|
engine.value = controls.engine
|
||||||
audio.value = controls.audio
|
audio.value = controls.audio
|
||||||
engineEnabled.value = controls.engineEnabled
|
engineEnabled.value = controls.engineEnabled
|
||||||
@@ -132,6 +138,8 @@ export const useEngineControlStore = defineStore('engineControl', () => {
|
|||||||
engineEnabled, // 字幕引擎是否启用
|
engineEnabled, // 字幕引擎是否启用
|
||||||
sourceLang, // 源语言
|
sourceLang, // 源语言
|
||||||
targetLang, // 目标语言
|
targetLang, // 目标语言
|
||||||
|
transModel, // 翻译模型
|
||||||
|
ollamaName, // Ollama 模型
|
||||||
engine, // 字幕引擎
|
engine, // 字幕引擎
|
||||||
audio, // 选择音频
|
audio, // 选择音频
|
||||||
translation, // 是否启用翻译
|
translation, // 是否启用翻译
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ export interface Controls {
|
|||||||
engineEnabled: boolean,
|
engineEnabled: boolean,
|
||||||
sourceLang: string,
|
sourceLang: string,
|
||||||
targetLang: string,
|
targetLang: string,
|
||||||
|
transModel: string,
|
||||||
|
ollamaName: string,
|
||||||
engine: string,
|
engine: string,
|
||||||
audio: 0 | 1,
|
audio: 0 | 1,
|
||||||
translation: boolean,
|
translation: boolean,
|
||||||
|
|||||||
Reference in New Issue
Block a user