mirror of
https://github.com/HiMeditator/auto-caption.git
synced 2026-02-04 04:14:42 +08:00
feat: 完全实现多语言支持、优化软件体验
- 完成多语言的剩余内容的翻译 - 重构配置管理,前端页面实现更快速的配置载入 - 为字幕引擎添加更严格的状态限制,防止出现僵尸进程
This commit is contained in:
15
CHANGELOG.md
15
CHANGELOG.md
@@ -25,5 +25,18 @@
|
||||
|
||||
2025-07-
|
||||
|
||||
> 预计为稳定版,之后除非大改,否则版本号第一位不再改变。
|
||||
本版本为正式版。
|
||||
|
||||
### 新增功能
|
||||
|
||||
- 添加多界面语言支持(中文、英语、日语)
|
||||
|
||||
### 提升体验
|
||||
|
||||
- 优化界面布局
|
||||
- 添加更多可保存和载入的配置项
|
||||
|
||||
### 新增文档
|
||||
|
||||
### 修复bug
|
||||
|
||||
|
||||
4
TODO.md
Normal file
4
TODO.md
Normal file
@@ -0,0 +1,4 @@
|
||||
- [x] 添加英语和日语语言支持
|
||||
- [ ] 优化长字幕显示效果
|
||||
- [ ] 修复字幕引擎空置报错的问题
|
||||
- [ ] 添加更多字幕引擎
|
||||
@@ -18,6 +18,41 @@
|
||||
|
||||
## 前端 <=> 后端
|
||||
|
||||
### `both.window.mounted`
|
||||
|
||||
**介绍:**前端窗口挂载完毕,请求最新的配置数据
|
||||
|
||||
**发起方:**前端
|
||||
|
||||
**接收方:**后端
|
||||
|
||||
**数据类型:**
|
||||
|
||||
- 发送:无数据
|
||||
- 接收:`FullConfig`
|
||||
|
||||
## 前端 ==> 后端
|
||||
|
||||
### `control.uiLanguage.change`
|
||||
|
||||
**介绍:**前端修改字界面语言,将修改同步给后端
|
||||
|
||||
**发起方:**前端控制窗口
|
||||
|
||||
**接收方:**后端控制窗口实例
|
||||
|
||||
**数据类型:**`UILanguage`
|
||||
|
||||
### `control.leftBarWidth.change`
|
||||
|
||||
**介绍:**前端修改边栏宽度,将修改同步给后端
|
||||
|
||||
**发起方:**前端控制窗口
|
||||
|
||||
**接收方:**后端控制窗口实例
|
||||
|
||||
**数据类型:**`number`
|
||||
|
||||
### `control.captionLog.clear`
|
||||
|
||||
**介绍:**清空字幕记录
|
||||
@@ -26,12 +61,7 @@
|
||||
|
||||
**接收方:**后端控制窗口实例
|
||||
|
||||
**数据类型:**
|
||||
|
||||
- 发送:无数据
|
||||
- 接收:`CaptionItem[]`
|
||||
|
||||
## 前端 ==> 后端
|
||||
**数据类型:**无数据
|
||||
|
||||
### `control.styles.change`
|
||||
|
||||
@@ -135,19 +165,19 @@
|
||||
|
||||
## 后端 ==> 前端
|
||||
|
||||
### `control.engine.already`
|
||||
### `caption.uiLanguage.set`
|
||||
|
||||
**介绍:**引擎已经启动
|
||||
**介绍:**后端将最新界面语言发送给前端,前端进行设置
|
||||
|
||||
**发起方:**后端
|
||||
|
||||
**接收方:**前端控制窗口
|
||||
**接收方:**字幕窗口
|
||||
|
||||
**数据类型:**无数据
|
||||
**数据类型:**`UILanguage`
|
||||
|
||||
### `control.engine.started`
|
||||
|
||||
**介绍:**引擎启动
|
||||
**介绍:**引擎启动成功
|
||||
|
||||
**发起方:**后端
|
||||
|
||||
@@ -185,7 +215,7 @@
|
||||
|
||||
**数据类型:**`Controls`
|
||||
|
||||
### `caption.styles.set`
|
||||
### `both.styles.set`
|
||||
|
||||
**介绍:**后端将最新字幕样式发送给前端,前端进行设置
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ import path from 'path'
|
||||
import { is } from '@electron-toolkit/utils'
|
||||
import icon from '../../resources/icon.png?asset'
|
||||
import { controlWindow } from './ControlWindow'
|
||||
import { allConfig } from './utils/AllConfig'
|
||||
|
||||
class CaptionWindow {
|
||||
window: BrowserWindow | undefined;
|
||||
@@ -27,13 +26,6 @@ class CaptionWindow {
|
||||
}
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
if (this.window) {
|
||||
allConfig.sendStyles(this.window);
|
||||
allConfig.sendCaptionLog(this.window, 'set');
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
this.window.on('ready-to-show', () => {
|
||||
this.window?.show()
|
||||
})
|
||||
@@ -57,7 +49,6 @@ class CaptionWindow {
|
||||
}
|
||||
|
||||
public handleMessage() {
|
||||
// 激活控制窗口
|
||||
ipcMain.on('caption.controlWindow.activate', () => {
|
||||
if(!controlWindow.window){
|
||||
controlWindow.createWindow()
|
||||
@@ -67,21 +58,18 @@ class CaptionWindow {
|
||||
}
|
||||
})
|
||||
|
||||
// 字幕窗口高度发生变化
|
||||
ipcMain.on('caption.windowHeight.change', (_, height) => {
|
||||
if(this.window){
|
||||
this.window.setSize(this.window.getSize()[0], height)
|
||||
}
|
||||
})
|
||||
|
||||
// 关闭字幕窗口
|
||||
ipcMain.on('caption.window.close', () => {
|
||||
if(this.window){
|
||||
this.window.close()
|
||||
}
|
||||
})
|
||||
|
||||
// 是否固定在最前面
|
||||
ipcMain.on('caption.pin.set', (_, pinned) => {
|
||||
if(this.window){
|
||||
this.window.setAlwaysOnTop(pinned)
|
||||
|
||||
@@ -28,14 +28,6 @@ class ControlWindow {
|
||||
|
||||
allConfig.readConfig()
|
||||
|
||||
setTimeout(() => {
|
||||
if (this.window) {
|
||||
allConfig.sendStyles(this.window)
|
||||
allConfig.sendControls(this.window)
|
||||
allConfig.sendCaptionLog(this.window, 'set')
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
this.window.on('ready-to-show', () => {
|
||||
this.window?.show()
|
||||
})
|
||||
@@ -58,12 +50,21 @@ class ControlWindow {
|
||||
}
|
||||
|
||||
public handleMessage() {
|
||||
// 控制窗口初始化完毕
|
||||
ipcMain.handle('control.window.mounted', async () => {
|
||||
return allConfig.getControlWindowConfig()
|
||||
ipcMain.handle('both.window.mounted', () => {
|
||||
return allConfig.getFullConfig()
|
||||
})
|
||||
|
||||
ipcMain.on('control.uiLanguage.change', (_, args) => {
|
||||
allConfig.uiLanguage = args
|
||||
if(captionWindow.window){
|
||||
captionWindow.window.webContents.send('caption.uiLanguage.set', args)
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('control.leftBarWidth.change', (_, args) => {
|
||||
allConfig.leftBarWidth = args
|
||||
})
|
||||
|
||||
// 样式变更
|
||||
ipcMain.on('control.styles.change', (_, args) => {
|
||||
allConfig.setStyles(args)
|
||||
if(captionWindow.window){
|
||||
@@ -71,7 +72,6 @@ class ControlWindow {
|
||||
}
|
||||
})
|
||||
|
||||
// 样式重置
|
||||
ipcMain.on('control.styles.reset', () => {
|
||||
allConfig.resetStyles()
|
||||
if(this.window){
|
||||
@@ -82,7 +82,6 @@ class ControlWindow {
|
||||
}
|
||||
})
|
||||
|
||||
// 激活字幕窗口
|
||||
ipcMain.on('control.captionWindow.activate', () => {
|
||||
if(!captionWindow.window){
|
||||
captionWindow.createWindow()
|
||||
@@ -92,31 +91,20 @@ class ControlWindow {
|
||||
}
|
||||
})
|
||||
|
||||
// 字幕引擎配置更新
|
||||
ipcMain.on('control.controls.change', (_, args) => {
|
||||
allConfig.setControls(args)
|
||||
})
|
||||
|
||||
// 启动字幕引擎
|
||||
ipcMain.on('control.engine.start', () => {
|
||||
if(allConfig.controls.engineEnabled){
|
||||
this.window?.webContents.send('control.engine.already')
|
||||
}
|
||||
else {
|
||||
captionEngine.start()
|
||||
}
|
||||
captionEngine.start()
|
||||
})
|
||||
|
||||
// 停止字幕引擎
|
||||
ipcMain.on('control.engine.stop', () => {
|
||||
captionEngine.stop()
|
||||
this.window?.webContents.send('control.engine.stopped')
|
||||
})
|
||||
|
||||
// 清空字幕记录
|
||||
ipcMain.handle('control.captionLog.clear', () => {
|
||||
ipcMain.on('control.captionLog.clear', () => {
|
||||
allConfig.captionLog.splice(0)
|
||||
return allConfig.captionLog
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ export interface Styles {
|
||||
fontColor: string,
|
||||
background: string,
|
||||
opacity: number,
|
||||
showPreview: boolean,
|
||||
transDisplay: boolean,
|
||||
transFontFamily: string,
|
||||
transFontSize: number,
|
||||
@@ -32,9 +33,10 @@ export interface CaptionItem {
|
||||
translation: string
|
||||
}
|
||||
|
||||
export interface ControlWindowConfig {
|
||||
export interface FullConfig {
|
||||
uiLanguage: UILanguage,
|
||||
leftBarWidth: number,
|
||||
styles: Styles,
|
||||
controls: Controls
|
||||
controls: Controls,
|
||||
captionLog: CaptionItem[]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {
|
||||
UILanguage, Styles, CaptionItem, Controls,
|
||||
ControlWindowConfig
|
||||
FullConfig
|
||||
} from '../types'
|
||||
import { app, BrowserWindow } from 'electron'
|
||||
import * as path from 'path'
|
||||
@@ -12,6 +12,7 @@ const defaultStyles: Styles = {
|
||||
fontColor: '#000000',
|
||||
background: '#dbe2ef',
|
||||
opacity: 80,
|
||||
showPreview: true,
|
||||
transDisplay: true,
|
||||
transFontFamily: 'sans-serif',
|
||||
transFontSize: 24,
|
||||
@@ -55,6 +56,7 @@ class AllConfig {
|
||||
public writeConfig() {
|
||||
const config = {
|
||||
uiLanguage: this.uiLanguage,
|
||||
leftBarWidth: this.leftBarWidth,
|
||||
controls: this.controls,
|
||||
styles: this.styles
|
||||
}
|
||||
@@ -63,12 +65,13 @@ class AllConfig {
|
||||
console.log('[INFO] Write Config to:', configPath)
|
||||
}
|
||||
|
||||
public getControlWindowConfig(): ControlWindowConfig {
|
||||
public getFullConfig(): FullConfig {
|
||||
return {
|
||||
uiLanguage: this.uiLanguage,
|
||||
leftBarWidth: this.leftBarWidth,
|
||||
styles: this.styles,
|
||||
controls: this.controls
|
||||
controls: this.controls,
|
||||
captionLog: this.captionLog
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,12 +89,12 @@ class AllConfig {
|
||||
}
|
||||
|
||||
public sendStyles(window: BrowserWindow) {
|
||||
window.webContents.send('caption.styles.set', this.styles)
|
||||
window.webContents.send('both.styles.set', this.styles)
|
||||
console.log(`[INFO] Send Styles to #${window.id}:`, this.styles)
|
||||
}
|
||||
|
||||
public setControls(args: Controls) {
|
||||
const engineEnabled = args.engineEnabled
|
||||
const engineEnabled = this.controls.engineEnabled
|
||||
for(let key in this.controls){
|
||||
if(key in args) {
|
||||
this.controls[key] = args[key]
|
||||
@@ -108,7 +111,11 @@ class AllConfig {
|
||||
|
||||
public updateCaptionLog(log: CaptionItem) {
|
||||
let command: 'add' | 'upd' = 'add'
|
||||
if(this.captionLog.length && this.captionLog[this.captionLog.length - 1].index === log.index) {
|
||||
if(
|
||||
this.captionLog.length &&
|
||||
this.captionLog[this.captionLog.length - 1].index === log.index &&
|
||||
this.captionLog[this.captionLog.length - 1].time_s === log.time_s
|
||||
) {
|
||||
this.captionLog.splice(this.captionLog.length - 1, 1, log)
|
||||
command = 'upd'
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ export class CaptionEngine {
|
||||
appPath: string = ''
|
||||
command: string[] = []
|
||||
process: any | undefined
|
||||
processStatus: 'running' | 'stopping' | 'stopped' = 'stopped'
|
||||
|
||||
private getApp(): boolean {
|
||||
if (allConfig.controls.customized && allConfig.controls.customizedApp) {
|
||||
@@ -60,7 +61,9 @@ export class CaptionEngine {
|
||||
}
|
||||
|
||||
public start() {
|
||||
if (this.process) { this.stop() }
|
||||
if (this.processStatus!== 'stopped') {
|
||||
return
|
||||
}
|
||||
if(!this.getApp()){ return }
|
||||
|
||||
try {
|
||||
@@ -72,13 +75,16 @@ export class CaptionEngine {
|
||||
return
|
||||
}
|
||||
|
||||
console.log('[INFO] Caption Engine Started')
|
||||
this.processStatus = 'running'
|
||||
console.log('[INFO] Caption Engine Started, PID:', this.process.pid)
|
||||
|
||||
allConfig.controls.engineEnabled = true
|
||||
|
||||
if(controlWindow.window){
|
||||
allConfig.sendControls(controlWindow.window)
|
||||
controlWindow.window.webContents.send('control.engine.started')
|
||||
controlWindow.window.webContents.send(
|
||||
'control.engine.started',
|
||||
this.process.pid
|
||||
)
|
||||
}
|
||||
|
||||
this.process.stdout.on('data', (data: any) => {
|
||||
@@ -107,12 +113,17 @@ export class CaptionEngine {
|
||||
allConfig.controls.engineEnabled = false
|
||||
if(controlWindow.window){
|
||||
allConfig.sendControls(controlWindow.window)
|
||||
controlWindow.window.webContents.send('control.engine.stopped')
|
||||
}
|
||||
this.processStatus = 'stopped'
|
||||
console.log('[INFO] Caption engine process stopped')
|
||||
});
|
||||
}
|
||||
|
||||
public stop() {
|
||||
if(this.processStatus !== 'running') return
|
||||
if (this.process) {
|
||||
console.log('[INFO] Trying to stop process, PID:', this.process.pid)
|
||||
if (process.platform === "win32" && this.process.pid) {
|
||||
exec(`taskkill /pid ${this.process.pid} /t /f`, (error) => {
|
||||
if (error) {
|
||||
@@ -124,12 +135,8 @@ export class CaptionEngine {
|
||||
this.process.kill('SIGKILL');
|
||||
}
|
||||
}
|
||||
this.process = undefined;
|
||||
allConfig.controls.engineEnabled = false;
|
||||
console.log('[INFO] Caption engine process stopped')
|
||||
if(controlWindow.window) {
|
||||
allConfig.sendControls(controlWindow.window)
|
||||
}
|
||||
this.processStatus = 'stopping'
|
||||
console.log('[INFO] Caption engine process stopping')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,20 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted } from 'vue'
|
||||
import { FullConfig } from './types'
|
||||
import { useCaptionLogStore } from './stores/captionLog'
|
||||
import { useCaptionStyleStore } from './stores/captionStyle'
|
||||
import { useEngineControlStore } from './stores/engineControl'
|
||||
import { useGeneralSettingStore } from './stores/generalSetting'
|
||||
|
||||
onMounted(() => {
|
||||
// window.electron.ipcRenderer.invoke('').then((data: any) => {
|
||||
|
||||
// })
|
||||
window.electron.ipcRenderer.invoke('both.window.mounted').then((data: FullConfig) => {
|
||||
console.log(data)
|
||||
useGeneralSettingStore().uiLanguage = data.uiLanguage
|
||||
useGeneralSettingStore().leftBarWidth = data.leftBarWidth
|
||||
useCaptionStyleStore().setStyles(data.styles)
|
||||
useEngineControlStore().setControls(data.controls)
|
||||
useCaptionLogStore().captionData = data.captionLog
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -54,8 +54,8 @@
|
||||
|
||||
<div class="input-item">
|
||||
<span class="input-label">{{ $t('style.preview') }}</span>
|
||||
<a-switch v-model:checked="displayPreview" />
|
||||
<sapn style="display:inline-block;width:20px;"></sapn>
|
||||
<a-switch v-model:checked="currentPreview" />
|
||||
<span style="display:inline-block;width:20px;"></span>
|
||||
<div style="display: inline-block;">
|
||||
<span class="switch-label">{{ $t('style.translation') }}</span>
|
||||
<a-switch v-model:checked="currentTransDisplay" />
|
||||
@@ -99,7 +99,7 @@
|
||||
|
||||
<Teleport to="body">
|
||||
<div
|
||||
v-if="displayPreview"
|
||||
v-if="currentPreview"
|
||||
class="preview-container"
|
||||
:style="{
|
||||
backgroundColor: addOpicityToColor(currentBackground, currentOpacity)
|
||||
@@ -129,6 +129,10 @@
|
||||
import { ref, watch } from 'vue'
|
||||
import { useCaptionStyleStore } from '@renderer/stores/captionStyle'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { notification } from 'ant-design-vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const captionStyle = useCaptionStyleStore()
|
||||
const { changeSignal } = storeToRefs(captionStyle)
|
||||
@@ -138,11 +142,11 @@ const currentFontSize = ref<number>(24)
|
||||
const currentFontColor = ref<string>('#000000')
|
||||
const currentBackground = ref<string>('#dbe2ef')
|
||||
const currentOpacity = ref<number>(50)
|
||||
const currentPreview = ref<boolean>(true)
|
||||
const currentTransDisplay = ref<boolean>(true)
|
||||
const currentTransFontFamily = ref<string>('sans-serif')
|
||||
const currentTransFontSize = ref<number>(24)
|
||||
const currentTransFontColor = ref<string>('#000000')
|
||||
const displayPreview = ref<boolean>(true)
|
||||
|
||||
function addOpicityToColor(color: string, opicity: number) {
|
||||
const opicityValue = Math.round(opicity * 255 / 100);
|
||||
@@ -162,13 +166,18 @@ function applyStyle(){
|
||||
captionStyle.fontColor = currentFontColor.value;
|
||||
captionStyle.background = currentBackground.value;
|
||||
captionStyle.opacity = currentOpacity.value;
|
||||
|
||||
captionStyle.showPreview = currentPreview.value;
|
||||
captionStyle.transDisplay = currentTransDisplay.value;
|
||||
captionStyle.transFontFamily = currentTransFontFamily.value;
|
||||
captionStyle.transFontSize = currentTransFontSize.value;
|
||||
captionStyle.transFontColor = currentTransFontColor.value;
|
||||
|
||||
captionStyle.sendStylesChange();
|
||||
|
||||
notification.open({
|
||||
message: t('noti.engineChange'),
|
||||
description: t('noti.changeInfo')
|
||||
});
|
||||
}
|
||||
|
||||
function backStyle(){
|
||||
@@ -177,7 +186,7 @@ function backStyle(){
|
||||
currentFontColor.value = captionStyle.fontColor;
|
||||
currentBackground.value = captionStyle.background;
|
||||
currentOpacity.value = captionStyle.opacity;
|
||||
|
||||
currentPreview.value = captionStyle.showPreview;
|
||||
currentTransDisplay.value = captionStyle.transDisplay;
|
||||
currentTransFontFamily.value = captionStyle.transFontFamily;
|
||||
currentTransFontSize.value = captionStyle.transFontSize;
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
<div class="input-item">
|
||||
<span class="input-label">{{ $t('engine.enableTranslation') }}</span>
|
||||
<a-switch v-model:checked="currentTranslation" />
|
||||
<sapn style="display:inline-block;width:20px;"></sapn>
|
||||
<span style="display:inline-block;width:20px;"></span>
|
||||
<div style="display: inline-block;">
|
||||
<span class="switch-label">{{ $t('engine.customEngine') }}</span>
|
||||
<a-switch v-model:checked="currentCustomized" />
|
||||
@@ -82,6 +82,10 @@ import { storeToRefs } from 'pinia'
|
||||
import { useEngineControlStore } from '@renderer/stores/engineControl'
|
||||
import { notification } from 'ant-design-vue'
|
||||
import { InfoCircleOutlined } from '@ant-design/icons-vue';
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const engineControl = useEngineControlStore()
|
||||
const { captionEngine, audioType, changeSignal } = storeToRefs(engineControl)
|
||||
|
||||
@@ -118,8 +122,8 @@ function applyChange(){
|
||||
engineControl.sendControlsChange()
|
||||
|
||||
notification.open({
|
||||
message: '字幕控制已更改',
|
||||
description: '如果字幕引擎已经启动,需要关闭后重启才会生效'
|
||||
message: t('noti.engineChange'),
|
||||
description: t('noti.changeInfo')
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -31,11 +31,12 @@
|
||||
>{{ $t('status.openCaption') }}</a-button>
|
||||
<a-button
|
||||
class="control-button"
|
||||
:disabled="engineEnabled"
|
||||
@click="startEngine"
|
||||
>{{ engineEnabled ? $t('status.restartEngine') : $t('status.startEngine') }}</a-button>
|
||||
<!-- TODO 添加重启字幕引擎功能 -->
|
||||
>{{ $t('status.startEngine') }}</a-button>
|
||||
<a-button
|
||||
danger class="control-button"
|
||||
:disabled="!engineEnabled"
|
||||
@click="stopEngine"
|
||||
>{{ $t('status.stopEngine') }}</a-button>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
<template>
|
||||
<a-card size="small" :title="$t('general.title')">
|
||||
<template #extra>
|
||||
<a-popover>
|
||||
<template #content>
|
||||
<p class="general-note">{{ $t('general.note') }}</p>
|
||||
</template>
|
||||
<a><InfoCircleOutlined /></a>
|
||||
</a-popover>
|
||||
</template>
|
||||
|
||||
<div>
|
||||
<div class="input-item">
|
||||
<span class="input-label">{{ $t('general.uiLanguage') }}</span>
|
||||
@@ -9,6 +18,7 @@
|
||||
<a-radio-button value="ja">日本語</a-radio-button>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
|
||||
<div class="input-item">
|
||||
<span class="input-label">{{ $t('general.barWidth') }}</span>
|
||||
<a-input
|
||||
@@ -24,6 +34,7 @@
|
||||
<script setup lang="ts">
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useGeneralSettingStore } from '@renderer/stores/generalSetting'
|
||||
import { InfoCircleOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
const generalSettingStore = useGeneralSettingStore()
|
||||
const { uiLanguage, leftBarWidth } = storeToRefs(generalSettingStore)
|
||||
@@ -35,4 +46,9 @@ const { uiLanguage, leftBarWidth } = storeToRefs(generalSettingStore)
|
||||
.span-input {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.general-note {
|
||||
padding: 10px 10px 0;
|
||||
max-width: min(36vw, 400px);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,16 +1,36 @@
|
||||
export default {
|
||||
example: {
|
||||
"original": "这是字幕样式预览。",
|
||||
"translation": "(Translation) This is a preview of subtitle styles."
|
||||
"translation": "(Translation) This is a preview of caption styles."
|
||||
},
|
||||
noti: {
|
||||
"restarted": "Caption Engine Restarted Successfully",
|
||||
"started": "Caption Engine Started Successfully",
|
||||
"sLang": "Source language: ",
|
||||
"trans": ", translation: ",
|
||||
"engine": ", caption engine: ",
|
||||
"audio": ", audio type: ",
|
||||
"sysout": "system audio output (speaker)",
|
||||
"sysin": "system audio input (microphone)",
|
||||
"tLang": ", target language: ",
|
||||
"custom": "Type: Custom engine, engine path: ",
|
||||
"args": ", command arguments: ",
|
||||
"pidInfo": ", caption engine process PID: ",
|
||||
"stopped": "Caption Engine Stopped",
|
||||
"stoppedInfo": "The caption engine has stopped. You can click the 'Start Caption Engine' button to restart it.",
|
||||
"error": "An error occurred",
|
||||
"engineChange": "Cpation Engine Configuration Changed",
|
||||
"changeInfo": "If the caption engine is already running, you need to restart it for the changes to take effect."
|
||||
},
|
||||
general: {
|
||||
"title": "General Settings",
|
||||
"uiLanguage": "Language",
|
||||
"barWidth": "Width"
|
||||
"barWidth": "Width",
|
||||
"note": "General Settings take effect immediately. Please note that changes to the Caption Engine Settings and Caption Style Settings will only take effect after clicking Apply."
|
||||
},
|
||||
engine: {
|
||||
"title": "Caption Engine Settings",
|
||||
"applyChange": "Apply Chnages",
|
||||
"applyChange": "Apply Changes",
|
||||
"cancelChange": "Cancel Changes",
|
||||
"sourceLang": "Source",
|
||||
"transLang": "Translation",
|
||||
|
||||
@@ -3,10 +3,30 @@ export default {
|
||||
"original": "这是字幕样式预览。",
|
||||
"translation": "(翻訳)これは字幕のスタイルのプレビューです。"
|
||||
},
|
||||
noti: {
|
||||
"restarted": "字幕エンジンが再起動しました",
|
||||
"started": "字幕エンジンを開始しました",
|
||||
"sLang": "ソース言語:",
|
||||
"trans": "、翻訳する:",
|
||||
"engine": "、字幕エンジン:",
|
||||
"audio": "、オーディオタイプ:",
|
||||
"sysout": "システムオーディオ出力(スピーカー)",
|
||||
"sysin": "システムオーディオ入力(マイク)",
|
||||
"tLang": "、翻訳先の言語:",
|
||||
"custom": "タイプ:カスタムエンジン、エンジンパス:",
|
||||
"args": "、コマンド引数:",
|
||||
"pidInfo": "、字幕エンジンプロセス PID:",
|
||||
"stopped": "字幕エンジンが停止しました",
|
||||
"stoppedInfo": "字幕エンジンが停止しました。再起動するには「字幕エンジンを開始」ボタンをクリックしてください。",
|
||||
"error": "エラーが発生しました",
|
||||
"engineChange": "字幕エンジンの設定が変更されました",
|
||||
"changeInfo": "字幕エンジンがすでに起動している場合、変更を有効にするには再起動が必要です。"
|
||||
},
|
||||
general: {
|
||||
"title": "一般設定",
|
||||
"uiLanguage": "言語設定",
|
||||
"barWidth": "左側の幅"
|
||||
"barWidth": "左側の幅",
|
||||
"note": "一般設定はすぐに有効になります。字幕エンジンの設定と字幕スタイルの設定を変更した場合は、適用ボタンをクリックしてから有効になりますのでご注意ください。"
|
||||
},
|
||||
engine: {
|
||||
"title": "字幕エンジン設定",
|
||||
|
||||
@@ -1,12 +1,34 @@
|
||||
export default {
|
||||
example: {
|
||||
"original": "This is a preview of subtitle styles.",
|
||||
"original": "This is a preview of caption styles.",
|
||||
"translation": "(翻译)这是字幕样式预览。"
|
||||
},
|
||||
noti: {
|
||||
"restarted": "字幕引擎重启成功",
|
||||
"started": "字幕引擎启动成功",
|
||||
"sLang": "源语言:",
|
||||
"trans": ",是否翻译:",
|
||||
"engine": ",字幕引擎:",
|
||||
"audio": ",音频类型:",
|
||||
"sysout": "系统音频输出(扬声器)",
|
||||
"sysin": "系统音频输入(麦克风)",
|
||||
"tLang": ",翻译语言:",
|
||||
"custom": "类型:自定义引擎,引擎路径:",
|
||||
"args": ",命令参数:",
|
||||
"pidInfo": ",字幕引擎进程 PID:",
|
||||
"stopped": "字幕引擎停止",
|
||||
"stoppedInfo": "字幕引擎已经停止,可点击“启动字幕引擎”按钮重新启动",
|
||||
"error": "发生错误",
|
||||
"engineChange": "字幕引擎配置已更改",
|
||||
"changeInfo": "如果字幕引擎已经启动,需要重启字幕引擎修改才会生效",
|
||||
"styleChange": "字幕样式修改已更改",
|
||||
"styleInfo": "字幕样式修改已经生效"
|
||||
},
|
||||
general: {
|
||||
"title": "通用设置",
|
||||
"uiLanguage": "界面语言",
|
||||
"barWidth": "左侧宽度"
|
||||
"barWidth": "左侧宽度",
|
||||
"note": "通用设置修改后立即生效。注意字幕引擎设置和字幕样式的设置修改后需要点击应用后才会生效。"
|
||||
},
|
||||
engine: {
|
||||
"title": "字幕引擎设置",
|
||||
|
||||
@@ -6,9 +6,8 @@ export const useCaptionLogStore = defineStore('captionLog', () => {
|
||||
const captionData = ref<CaptionItem[]>([])
|
||||
|
||||
function clear() {
|
||||
window.electron.ipcRenderer.invoke('control.captionLog.clear').then((data: CaptionItem[]) => {
|
||||
captionData.value = data
|
||||
})
|
||||
captionData.value = []
|
||||
window.electron.ipcRenderer.send('control.captionLog.clear')
|
||||
}
|
||||
|
||||
window.electron.ipcRenderer.on('both.captionLog.add', (_, log) => {
|
||||
|
||||
@@ -8,7 +8,7 @@ export const useCaptionStyleStore = defineStore('captionStyle', () => {
|
||||
const fontColor = ref<string>('#000000')
|
||||
const background = ref<string>('#dbe2ef')
|
||||
const opacity = ref<number>(80)
|
||||
|
||||
const showPreview = ref<boolean>(true)
|
||||
const transDisplay = ref<boolean>(true)
|
||||
const transFontFamily = ref<string>('sans-serif')
|
||||
const transFontSize = ref<number>(24)
|
||||
@@ -33,6 +33,7 @@ export const useCaptionStyleStore = defineStore('captionStyle', () => {
|
||||
fontColor: fontColor.value,
|
||||
background: background.value,
|
||||
opacity: opacity.value,
|
||||
showPreview: showPreview.value,
|
||||
transDisplay: transDisplay.value,
|
||||
transFontFamily: transFontFamily.value,
|
||||
transFontSize: transFontSize.value,
|
||||
@@ -45,17 +46,22 @@ export const useCaptionStyleStore = defineStore('captionStyle', () => {
|
||||
window.electron.ipcRenderer.send('control.styles.reset')
|
||||
}
|
||||
|
||||
window.electron.ipcRenderer.on('caption.styles.set', (_, args: Styles) => {
|
||||
function setStyles(args: Styles){
|
||||
fontFamily.value = args.fontFamily
|
||||
fontSize.value = args.fontSize
|
||||
fontColor.value = args.fontColor
|
||||
background.value = args.background
|
||||
opacity.value = args.opacity
|
||||
showPreview.value = args.showPreview
|
||||
transDisplay.value = args.transDisplay
|
||||
transFontFamily.value = args.transFontFamily
|
||||
transFontSize.value = args.transFontSize
|
||||
transFontColor.value = args.transFontColor
|
||||
changeSignal.value = true
|
||||
}
|
||||
|
||||
window.electron.ipcRenderer.on('both.styles.set', (_, args: Styles) => {
|
||||
setStyles(args)
|
||||
})
|
||||
|
||||
return {
|
||||
@@ -64,11 +70,13 @@ export const useCaptionStyleStore = defineStore('captionStyle', () => {
|
||||
fontColor, // 字体颜色
|
||||
background, // 背景颜色
|
||||
opacity, // 背景透明度
|
||||
showPreview, // 是否显示预览
|
||||
transDisplay, // 是否显示翻译
|
||||
transFontFamily, // 翻译字体族
|
||||
transFontSize, // 翻译字体大小
|
||||
transFontColor, // 翻译字体颜色
|
||||
backgroundRGBA, // 带透明度的背景颜色
|
||||
setStyles, // 设置样式
|
||||
sendStylesChange, // 发送样式改变
|
||||
sendStylesReset, // 恢复默认样式
|
||||
changeSignal // 样式改变信号
|
||||
|
||||
@@ -4,12 +4,15 @@ import { defineStore } from 'pinia'
|
||||
import { notification } from 'ant-design-vue'
|
||||
import { ExclamationCircleOutlined } from '@ant-design/icons-vue'
|
||||
import { h } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { Controls } from '@renderer/types'
|
||||
import { engines, audioTypes } from '@renderer/i18n'
|
||||
import { useGeneralSettingStore } from './generalSetting'
|
||||
|
||||
export const useEngineControlStore = defineStore('engineControl', () => {
|
||||
const { t } = useI18n()
|
||||
|
||||
const captionEngine = ref(engines[useGeneralSettingStore().uiLanguage])
|
||||
const audioType = ref(audioTypes[useGeneralSettingStore().uiLanguage])
|
||||
|
||||
@@ -40,7 +43,7 @@ export const useEngineControlStore = defineStore('engineControl', () => {
|
||||
window.electron.ipcRenderer.send('control.controls.change', controls)
|
||||
}
|
||||
|
||||
window.electron.ipcRenderer.on('control.controls.set', (_, controls: Controls) => {
|
||||
function setControls(controls: Controls) {
|
||||
sourceLang.value = controls.sourceLang
|
||||
targetLang.value = controls.targetLang
|
||||
engine.value = controls.engine
|
||||
@@ -51,38 +54,36 @@ export const useEngineControlStore = defineStore('engineControl', () => {
|
||||
customizedApp.value = controls.customizedApp
|
||||
customizedCommand.value = controls.customizedCommand
|
||||
changeSignal.value = true
|
||||
}
|
||||
|
||||
window.electron.ipcRenderer.on('control.controls.set', (_, controls: Controls) => {
|
||||
setControls(controls)
|
||||
})
|
||||
|
||||
window.electron.ipcRenderer.on('control.engine.already', () => {
|
||||
// TODO 修改为重启
|
||||
notification.open({
|
||||
message: '字幕引擎已经启动',
|
||||
description: '字幕引擎已经启动,请勿重复启动'
|
||||
});
|
||||
})
|
||||
|
||||
window.electron.ipcRenderer.on('control.engine.started', () => {
|
||||
window.electron.ipcRenderer.on('control.engine.started', (_, args) => {
|
||||
const str0 =
|
||||
`原语言:${sourceLang.value},是否翻译:${translation.value?'是':'否'},` +
|
||||
`字幕引擎:${engine.value},音频类型:${audio.value ? '输入音频' : '输出音频'}` +
|
||||
(translation.value ? `,翻译语言:${targetLang.value}` : '');
|
||||
const str1 = `类型:自定义引擎,引擎路径:${customizedApp.value},命令参数:${customizedCommand.value}`;
|
||||
`${t('noti.sLang')}${sourceLang.value}${t('noti.trans')}${translation.value?'yes':'no'}` +
|
||||
`${t('noti.engine')}${engine.value}${t('noti.audio')}${audio.value?t('noti.sysin'):t('noti.sysout')}` +
|
||||
(translation.value ? `${t('noti.tLang')}${targetLang.value}` : '');
|
||||
const str1 = `${t('noti.custom')}${customizedApp.value}${t('noti.args')}${customizedCommand.value}`;
|
||||
notification.open({
|
||||
message: '字幕引擎启动',
|
||||
description: (customized.value && customizedApp.value) ? str1 : str0
|
||||
message: t('noti.started'),
|
||||
description:
|
||||
((customized.value && customizedApp.value) ? str1 : str0) +
|
||||
`${t('noti.pidInfo')}${args}`
|
||||
});
|
||||
})
|
||||
|
||||
window.electron.ipcRenderer.on('control.engine.stopped', () => {
|
||||
notification.open({
|
||||
message: '字幕引擎停止',
|
||||
description: '可点击“启动字幕引擎”按钮重新启动'
|
||||
message: t('noti.stopped'),
|
||||
description: t('noti.stoppedInfo')
|
||||
});
|
||||
})
|
||||
|
||||
window.electron.ipcRenderer.on('control.error.occurred', (_, message) => {
|
||||
notification.open({
|
||||
message: '发生错误',
|
||||
message: t('noti.error'),
|
||||
description: message,
|
||||
duration: null,
|
||||
placement: 'topLeft',
|
||||
@@ -102,7 +103,8 @@ export const useEngineControlStore = defineStore('engineControl', () => {
|
||||
customized, // 是否使用自定义字幕引擎
|
||||
customizedApp, // 自定义字幕引擎的应用程序
|
||||
customizedCommand, // 自定义字幕引擎的命令
|
||||
sendControlsChange, // 发送最新控制消息到后端
|
||||
setControls, // 设置引擎配置
|
||||
sendControlsChange, // 发送最新控制消息到后端
|
||||
changeSignal, // 配置改变信号
|
||||
}
|
||||
})
|
||||
|
||||
@@ -12,9 +12,17 @@ export const useGeneralSettingStore = defineStore('generalSetting', () => {
|
||||
|
||||
watch(uiLanguage, (newValue) => {
|
||||
i18n.global.locale.value = newValue
|
||||
console.log(newValue)
|
||||
useEngineControlStore().captionEngine = engines[newValue]
|
||||
useEngineControlStore().audioType = audioTypes[newValue]
|
||||
window.electron.ipcRenderer.send('control.uiLanguage.change', newValue)
|
||||
})
|
||||
|
||||
watch(leftBarWidth, (newValue) => {
|
||||
window.electron.ipcRenderer.send('control.leftBarWidth.change', newValue)
|
||||
})
|
||||
|
||||
window.electron.ipcRenderer.on('caption.uiLanguage.set', (_, args: UILanguage) => {
|
||||
uiLanguage.value = args
|
||||
})
|
||||
|
||||
return {
|
||||
|
||||
@@ -18,6 +18,7 @@ export interface Styles {
|
||||
fontColor: string,
|
||||
background: string,
|
||||
opacity: number,
|
||||
showPreview: boolean,
|
||||
transDisplay: boolean,
|
||||
transFontFamily: string,
|
||||
transFontSize: number,
|
||||
@@ -32,9 +33,10 @@ export interface CaptionItem {
|
||||
translation: string
|
||||
}
|
||||
|
||||
export interface ControlWindowConfig {
|
||||
export interface FullConfig {
|
||||
uiLanguage: UILanguage,
|
||||
leftBarWidth: number,
|
||||
styles: Styles,
|
||||
controls: Controls
|
||||
controls: Controls,
|
||||
captionLog: CaptionItem[]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user