mirror of
https://github.com/HiMeditator/auto-caption.git
synced 2026-02-04 04:14:42 +08:00
feat: 新增配置保存和读取、新增文档
- 添加配置数据文件保存和载入 - 添加字幕样式恢复默认的选项 - 添加用户说明文档 - 添加字幕引擎说明文档
This commit is contained in:
21
CHANGELOG.md
Normal file
21
CHANGELOG.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
## v0.0.1
|
||||||
|
|
||||||
|
2025-06-22
|
||||||
|
|
||||||
|
发布第一版软件。
|
||||||
|
|
||||||
|
## v0.1.0
|
||||||
|
|
||||||
|
2025-06-26
|
||||||
|
|
||||||
|
### 新增功能
|
||||||
|
|
||||||
|
- 添加错误通知
|
||||||
|
- 添加默认引擎的环境变量检查
|
||||||
|
- 添加配置数据文件保存和载入
|
||||||
|
- 添加字幕样式恢复默认的选项
|
||||||
|
|
||||||
|
### 新增文档
|
||||||
|
|
||||||
|
- 添加用户说明文档
|
||||||
|
- 添加字幕引擎说明文档
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## 📥 下载
|
## 📥 下载
|
||||||
|
|
||||||
@@ -35,6 +35,8 @@
|
|||||||
|
|
||||||
## 🚀 项目运行
|
## 🚀 项目运行
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
### 安装依赖
|
### 安装依赖
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -45,6 +47,8 @@ npm install
|
|||||||
|
|
||||||
> #### 背景介绍
|
> #### 背景介绍
|
||||||
>
|
>
|
||||||
|
> 如果你是开发者,想开发自定义字幕引擎,请查看[字幕引擎说明文档](./assets/engine-manual_zh.md)。
|
||||||
|
>
|
||||||
> 所谓的字幕引擎实际上是一个子程序,它会实时获取系统音频输入(录音)或输出(播放声音)的流式数据,并调用音频转文字的模型生成对应音频的字幕。生成的字幕通过 IPC 输出为转换为字符串的 JSON 数据,并返回给主程序。主程序读取字幕数据,处理后显示在窗口上。
|
> 所谓的字幕引擎实际上是一个子程序,它会实时获取系统音频输入(录音)或输出(播放声音)的流式数据,并调用音频转文字的模型生成对应音频的字幕。生成的字幕通过 IPC 输出为转换为字符串的 JSON 数据,并返回给主程序。主程序读取字幕数据,处理后显示在窗口上。
|
||||||
>
|
>
|
||||||
>目前项目默认使用[阿里云 Gummy 模型](https://help.aliyun.com/zh/model-studio/gummy-speech-recognition-translation/),需要获取阿里云百炼平台的 API KEY 并配置到环境变量中才能正常使用该模型,相关教程:[获取API KEY](https://help.aliyun.com/zh/model-studio/get-api-key)、[将API Key配置到环境变量](https://help.aliyun.com/zh/model-studio/configure-api-key-through-environment-variables)。
|
>目前项目默认使用[阿里云 Gummy 模型](https://help.aliyun.com/zh/model-studio/gummy-speech-recognition-translation/),需要获取阿里云百炼平台的 API KEY 并配置到环境变量中才能正常使用该模型,相关教程:[获取API KEY](https://help.aliyun.com/zh/model-studio/get-api-key)、[将API Key配置到环境变量](https://help.aliyun.com/zh/model-studio/configure-api-key-through-environment-variables)。
|
||||||
|
|||||||
10
README_en.md
10
README_en.md
@@ -8,7 +8,11 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
## ⚠️ Attention
|
||||||
|
|
||||||
|
**The current software interface language is Chinese. English adaptation has not been done yet.**
|
||||||
|
|
||||||
## 📥 Download
|
## 📥 Download
|
||||||
|
|
||||||
@@ -36,6 +40,8 @@ Note: The Windows platform supports generating subtitles for both audio output a
|
|||||||
|
|
||||||
## 🚀 Project Execution
|
## 🚀 Project Execution
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
### Install Dependencies
|
### Install Dependencies
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -46,6 +52,8 @@ npm install
|
|||||||
|
|
||||||
> #### Background
|
> #### Background
|
||||||
>
|
>
|
||||||
|
> If you are a developer and want to develop a custom subtitle engine, please refer to the [Subtitle Engine Documentation (Chinese)](./assets/engine-manual_zh.md).
|
||||||
|
>
|
||||||
> The so-called subtitle engine is actually a subprocess that will real-time acquire streaming data from system audio input (recording) or output (playing sound) and call an audio-to-text model to generate corresponding subtitles for the audio. The generated subtitles are output as JSON data converted to strings via IPC and returned to the main program. The main program reads the subtitle data, processes it, and displays it on the window.
|
> The so-called subtitle engine is actually a subprocess that will real-time acquire streaming data from system audio input (recording) or output (playing sound) and call an audio-to-text model to generate corresponding subtitles for the audio. The generated subtitles are output as JSON data converted to strings via IPC and returned to the main program. The main program reads the subtitle data, processes it, and displays it on the window.
|
||||||
>
|
>
|
||||||
> Currently, the project uses the [Alibaba Cloud Gummy Model](https://help.aliyun.com/zh/model-studio/gummy-speech-recognition-translation/) by default, which requires obtaining an API KEY from Alibaba Cloud's Bailian platform and configuring it in the environment variables to function properly. Related tutorials: [Get API KEY](https://help.aliyun.com/zh/model-studio/get-api-key), [Configure API Key through Environment Variables](https://help.aliyun.com/zh/model-studio/configure-api-key-through-environment-variables).
|
> Currently, the project uses the [Alibaba Cloud Gummy Model](https://help.aliyun.com/zh/model-studio/gummy-speech-recognition-translation/) by default, which requires obtaining an API KEY from Alibaba Cloud's Bailian platform and configuring it in the environment variables to function properly. Related tutorials: [Get API KEY](https://help.aliyun.com/zh/model-studio/get-api-key), [Configure API Key through Environment Variables](https://help.aliyun.com/zh/model-studio/configure-api-key-through-environment-variables).
|
||||||
|
|||||||
BIN
assets/01.png
BIN
assets/01.png
Binary file not shown.
|
Before Width: | Height: | Size: 311 KiB |
87
assets/engine-manual_zh.md
Normal file
87
assets/engine-manual_zh.md
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
# 字幕引擎说明文档
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 字幕引擎介绍
|
||||||
|
|
||||||
|
所谓的字幕引擎实际上是一个子程序,它会实时获取系统音频输入(录音)或输出(播放声音)的流式数据,并调用音频转文字的模型生成对应音频的字幕。生成的字幕通过 IPC 输出为转换为字符串的 JSON 数据,并返回给主程序。主程序读取字幕数据,处理后显示在窗口上。
|
||||||
|
|
||||||
|
## 字幕引擎需要实现的功能
|
||||||
|
|
||||||
|
### 音频获取
|
||||||
|
|
||||||
|
首先,你的字幕引擎需要获取系统音频输入(录音)或输出(播放声音)的流式数据。如果使用 Python 开发,可以使用 PyAudio 模块获取麦克风音频输入数据(全平台通用)。使用 PyAudioWPatch 模块获取音频音频输出(仅适用于 Windows 平台)。
|
||||||
|
|
||||||
|
一般获取的音频流数据实际上是一个个时间比较短的音频块,需要根据模型调整音频块的大小。比如阿里云的Gummy 模型使用 0.05 秒大小的音频块识别效果优于使用 0.2 秒大小的音频块。
|
||||||
|
|
||||||
|
### 音频处理
|
||||||
|
|
||||||
|
获取到的音频流在转文字之前可能需要进行预处理。比如阿里云的Gummy模型只能识别单通道的音频流,而收集的音频流一般是双通道的,因此要将双通道音频流转换为单通道。
|
||||||
|
|
||||||
|
### 音频转文字
|
||||||
|
|
||||||
|
在得到了合适的音频流后,就可以将音频流转换为文字了。一般使用各种模型来实现音频流转文字。可根据需求自行选择模型。
|
||||||
|
|
||||||
|
### 数据传递
|
||||||
|
|
||||||
|
在获取到当前音频流的文字后,需要将文字传递给主程序。使用进程间通信(IPC)的方式,比如通过标准输入输出流或者命名管道来实现。传递的内容必须是 JSON 字符串,其中 JSON 对象需要包含的参数如下:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export interface CaptionItem {
|
||||||
|
index: number, // 字幕序号
|
||||||
|
time_s: string, // 当前字幕开始时间
|
||||||
|
time_t: string, // 当前字幕结束时间
|
||||||
|
text: string, // 字幕内容
|
||||||
|
translation: string // 字幕翻译
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
如果使用 python 语言,可以参考以下方式将数据传递给主程序。
|
||||||
|
|
||||||
|
```python
|
||||||
|
# python-subprocess\audio2text\gummy.py
|
||||||
|
...
|
||||||
|
def send_to_node(self, data):
|
||||||
|
"""
|
||||||
|
将数据发送到 Node.js 进程
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
json_data = json.dumps(data) + '\n'
|
||||||
|
sys.stdout.write(json_data)
|
||||||
|
sys.stdout.flush()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error sending data to Node.js: {e}", file=sys.stderr)
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
数据接收端代码如下:
|
||||||
|
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// src\main\utils\engine.ts
|
||||||
|
...
|
||||||
|
this.process.stdout.on('data', (data) => {
|
||||||
|
const lines = data.toString().split('\n');
|
||||||
|
lines.forEach((line: string) => {
|
||||||
|
if (line.trim()) {
|
||||||
|
try {
|
||||||
|
const caption = JSON.parse(line);
|
||||||
|
addCaptionLog(caption);
|
||||||
|
} catch (e) {
|
||||||
|
controlWindow.sendErrorMessage('字幕引擎输出内容无法解析为 JSON 对象:' + e)
|
||||||
|
console.error('[ERROR] Error parsing JSON:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.process.stderr.on('data', (data) => {
|
||||||
|
controlWindow.sendErrorMessage('字幕引擎错误:' + data)
|
||||||
|
console.error(`[ERROR] Subprocess Error: ${data}`);
|
||||||
|
});
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
## 参考代码
|
||||||
|
|
||||||
|
本项目 `python-subprocess` 文件夹下的 `main-gummy.py` 文件为默认字幕引擎的入口代码。`src\main\utils\engine.ts` 为服务端获取字幕引擎数据和进行处理的代码。可以根据需要阅读了解字幕引擎的实现细节和完整运行原过程。
|
||||||
BIN
assets/img/01.png
Normal file
BIN
assets/img/01.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
BIN
assets/img/02.png
Normal file
BIN
assets/img/02.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 72 KiB |
BIN
assets/media/main.png
Normal file
BIN
assets/media/main.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 332 KiB |
BIN
assets/media/structure.png
Normal file
BIN
assets/media/structure.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 321 KiB |
57
assets/user-manual_zh.md
Normal file
57
assets/user-manual_zh.md
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# Auto Caption 用户手册
|
||||||
|
|
||||||
|
## 软件简介
|
||||||
|
|
||||||
|
Auto Caption 是一个跨平台的字幕显示软件,能够实时获取系统音频输入(录音)或输出(播放声音)的流式数据,并调用音频转文字的模型生成对应音频的字幕。软件提供的默认字幕引擎(使用阿里云Gummy模型)支持九种语言(中英日韩德法俄西意)的识别与翻译。
|
||||||
|
|
||||||
|
目前软件默认字幕引擎只有在 Windows 平台下才拥有完整功能。在 Linux 平台下只能生成音频输入(麦克风)的字幕,暂不支持音频输出(播放声音)的字幕生成。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 软件缺点
|
||||||
|
|
||||||
|
要使用默认字幕服务需要获取阿里云的 API KEY。
|
||||||
|
|
||||||
|
软件使用 Electron 构建,因此软件体积不可避免的较大。
|
||||||
|
|
||||||
|
## 软件使用
|
||||||
|
|
||||||
|
### 准备阿里云百炼平台 API KEY
|
||||||
|
|
||||||
|
要使用软件提供的默认字幕引擎(阿里云Gummy),需要从阿里云百炼平台获取 API KEY 并在本机环境变量中配置。
|
||||||
|
|
||||||
|
这部分阿里云提供了详细的教程,可参考:
|
||||||
|
|
||||||
|
- [获取API KEY](https://help.aliyun.com/zh/model-studio/get-api-key)
|
||||||
|
|
||||||
|
- [将API Key配置到环境变量](https://help.aliyun.com/zh/model-studio/configure-api-key-through-environment-variables)。
|
||||||
|
|
||||||
|
### 修改字幕设置
|
||||||
|
|
||||||
|
字幕设置可以分为两类:修改字幕引擎配置、修改字幕样式设置。需要注意的是在调整的设置的参数后,需要点击配置分类右上角的“更改设置”(字幕引擎设置)或“应用样式”(字幕样式设置),更改才会真正生效。如果点击“取消更改”那么当前设置将不会被保存,而是回到上次修改的状态。
|
||||||
|
|
||||||
|
### 启动和关闭字幕
|
||||||
|
|
||||||
|
在修改完全部配置后,点击界面的“启动字幕引擎”按钮,即可启动字幕。如果需要独立的字幕展示窗口,单击界面的“打开字幕窗口”按钮即可激活独立的字幕展示窗口。如果需要暂停字幕识别,单击界面的“关闭字幕引擎”按钮即可。
|
||||||
|
|
||||||
|
### 调整字幕展示窗口
|
||||||
|
|
||||||
|
如下图为字幕展示窗口,该窗口实时展示当前最新字幕。窗口右上角三个按钮的功能分别是:将窗口固定在最前面、打开字幕控制窗口、关闭字幕展示窗口。该窗口宽度可以调整,将鼠标移动至窗口的左右边缘,拖动鼠标即可调整宽度。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 字幕记录的导出
|
||||||
|
|
||||||
|
在字幕控制窗口中可以看到当前收集的所有字幕的记录,点击“导出字幕记录”按钮,即可将字幕记录导出为 JSON 文件。
|
||||||
|
|
||||||
|
## 字幕引擎
|
||||||
|
|
||||||
|
所谓的字幕引擎实际上是一个子程序,它会实时获取系统音频输入(录音)或输出(播放声音)的流式数据,并调用音频转文字的模型生成对应音频的字幕。生成的字幕通过 IPC 输出为转换为字符串的 JSON 数据,并返回给主程序。主程序读取字幕数据,处理后显示在窗口上。
|
||||||
|
|
||||||
|
软件提供的一个默认的字幕引擎,如果你需要其他的字幕引擎,可以通过打开自定义引擎选项来调用其他字幕引擎。其中引擎路径是自定义字幕引擎在你的电脑上的路径,引擎指令是自定义字幕引擎的运行参数,这部分需要按该字幕引擎的规则进行填写。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
注意使用自定义字幕引擎时,前面的字幕引擎的设置将全部不起作用,字幕引擎的配置完全通过引擎指令进行配置。
|
||||||
|
|
||||||
|
如果你是开发者,想开发自定义字幕引擎,请查看[字幕引擎说明文档](./engine-manual_zh.md)。
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "auto-caption",
|
"name": "auto-caption",
|
||||||
"version": "0.0.1",
|
"version": "0.1.0",
|
||||||
"description": "A cross-platform subtitle display software.",
|
"description": "A cross-platform subtitle display software.",
|
||||||
"main": "./out/main/index.js",
|
"main": "./out/main/index.js",
|
||||||
"author": "himeditator",
|
"author": "himeditator",
|
||||||
|
|||||||
@@ -8,10 +8,12 @@ import {
|
|||||||
captionLog,
|
captionLog,
|
||||||
controls,
|
controls,
|
||||||
setStyles,
|
setStyles,
|
||||||
|
resetStyles,
|
||||||
sendStyles,
|
sendStyles,
|
||||||
sendCaptionLog,
|
sendCaptionLog,
|
||||||
setControls,
|
setControls,
|
||||||
sendControls
|
sendControls,
|
||||||
|
readConfig
|
||||||
} from './utils/config'
|
} from './utils/config'
|
||||||
|
|
||||||
class ControlWindow {
|
class ControlWindow {
|
||||||
@@ -36,6 +38,7 @@ class ControlWindow {
|
|||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (this.window) {
|
if (this.window) {
|
||||||
|
readConfig()
|
||||||
sendStyles(this.window) // 配置初始样式
|
sendStyles(this.window) // 配置初始样式
|
||||||
sendCaptionLog(this.window, 'set') // 配置当前字幕记录
|
sendCaptionLog(this.window, 'set') // 配置当前字幕记录
|
||||||
sendControls(this.window) // 配置字幕引擎配置
|
sendControls(this.window) // 配置字幕引擎配置
|
||||||
@@ -71,6 +74,15 @@ class ControlWindow {
|
|||||||
sendStyles(captionWindow.window)
|
sendStyles(captionWindow.window)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
ipcMain.on('control.style.reset', () => {
|
||||||
|
resetStyles()
|
||||||
|
if(captionWindow.window){
|
||||||
|
sendStyles(captionWindow.window)
|
||||||
|
}
|
||||||
|
if(this.window){
|
||||||
|
sendStyles(this.window)
|
||||||
|
}
|
||||||
|
})
|
||||||
// 控制窗口请求创建字幕窗口
|
// 控制窗口请求创建字幕窗口
|
||||||
ipcMain.on('control.captionWindow.activate', () => {
|
ipcMain.on('control.captionWindow.activate', () => {
|
||||||
if(!captionWindow.window){
|
if(!captionWindow.window){
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { app, BrowserWindow } from 'electron'
|
|||||||
import { electronApp, optimizer } from '@electron-toolkit/utils'
|
import { electronApp, optimizer } from '@electron-toolkit/utils'
|
||||||
import { controlWindow } from './control'
|
import { controlWindow } from './control'
|
||||||
import { captionWindow } from './caption'
|
import { captionWindow } from './caption'
|
||||||
import { captionEngine } from './utils/config'
|
import { captionEngine, writeConfig } from './utils/config'
|
||||||
|
|
||||||
app.whenReady().then(() => {
|
app.whenReady().then(() => {
|
||||||
electronApp.setAppUserModelId('com.himeditator.autocaption')
|
electronApp.setAppUserModelId('com.himeditator.autocaption')
|
||||||
@@ -25,6 +25,7 @@ app.whenReady().then(() => {
|
|||||||
|
|
||||||
app.on('will-quit', async () => {
|
app.on('will-quit', async () => {
|
||||||
captionEngine.stop()
|
captionEngine.stop()
|
||||||
|
writeConfig()
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on('window-all-closed', () => {
|
app.on('window-all-closed', () => {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { Styles, CaptionItem, Controls } from '../types'
|
import { Styles, CaptionItem, Controls } from '../types'
|
||||||
import { BrowserWindow } from 'electron'
|
import { app, BrowserWindow } from 'electron'
|
||||||
import { CaptionEngine } from './engine'
|
import { CaptionEngine } from './engine'
|
||||||
|
import * as path from 'path'
|
||||||
|
import * as fs from 'fs'
|
||||||
|
|
||||||
export const captionEngine = new CaptionEngine()
|
export const captionEngine = new CaptionEngine()
|
||||||
|
|
||||||
@@ -30,8 +32,6 @@ export const controls: Controls = {
|
|||||||
customizedCommand: ''
|
customizedCommand: ''
|
||||||
}
|
}
|
||||||
|
|
||||||
export let engineRunning: boolean = false
|
|
||||||
|
|
||||||
export function setStyles(args: any) {
|
export function setStyles(args: any) {
|
||||||
styles.fontFamily = args.fontFamily
|
styles.fontFamily = args.fontFamily
|
||||||
styles.fontSize = args.fontSize
|
styles.fontSize = args.fontSize
|
||||||
@@ -45,6 +45,20 @@ export function setStyles(args: any) {
|
|||||||
console.log('[INFO] Set Styles:', styles)
|
console.log('[INFO] Set Styles:', styles)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function resetStyles() {
|
||||||
|
setStyles({
|
||||||
|
fontFamily: 'sans-serif',
|
||||||
|
fontSize: 24,
|
||||||
|
fontColor: '#000000',
|
||||||
|
background: '#dbe2ef',
|
||||||
|
opacity: 80,
|
||||||
|
transDisplay: true,
|
||||||
|
transFontFamily: 'sans-serif',
|
||||||
|
transFontSize: 24,
|
||||||
|
transFontColor: '#000000'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export function sendStyles(window: BrowserWindow) {
|
export function sendStyles(window: BrowserWindow) {
|
||||||
window.webContents.send('caption.style.set', styles)
|
window.webContents.send('caption.style.set', styles)
|
||||||
console.log(`[INFO] Send Styles to #${window.id}:`, styles)
|
console.log(`[INFO] Send Styles to #${window.id}:`, styles)
|
||||||
@@ -86,4 +100,24 @@ export function setControls(args: any) {
|
|||||||
export function sendControls(window: BrowserWindow) {
|
export function sendControls(window: BrowserWindow) {
|
||||||
window.webContents.send('control.control.set', controls)
|
window.webContents.send('control.control.set', controls)
|
||||||
console.log(`[INFO] Send Controls to #${window.id}:`, controls)
|
console.log(`[INFO] Send Controls to #${window.id}:`, controls)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function readConfig() {
|
||||||
|
const configPath = path.join(app.getPath('userData'), 'config.json')
|
||||||
|
if(fs.existsSync(configPath)){
|
||||||
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'))
|
||||||
|
setStyles(config.styles)
|
||||||
|
setControls(config.controls)
|
||||||
|
console.log('[INFO] Read Config from:', configPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function writeConfig() {
|
||||||
|
const config = {
|
||||||
|
controls: controls,
|
||||||
|
styles: styles
|
||||||
|
}
|
||||||
|
const configPath = path.join(app.getPath('userData'), 'config.json')
|
||||||
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2))
|
||||||
|
console.log('[INFO] Write Config to:', configPath)
|
||||||
}
|
}
|
||||||
3
src/main/utils/configSave.ts
Normal file
3
src/main/utils/configSave.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
class configSave {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -96,6 +96,8 @@ export class CaptionEngine {
|
|||||||
this.process.on('close', (code: any) => {
|
this.process.on('close', (code: any) => {
|
||||||
console.log(`[INFO] Subprocess exited with code ${code}`);
|
console.log(`[INFO] Subprocess exited with code ${code}`);
|
||||||
this.process = undefined;
|
this.process = undefined;
|
||||||
|
controls.engineEnabled = false
|
||||||
|
sendControls(window)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,9 +113,10 @@ export class CaptionEngine {
|
|||||||
} else {
|
} else {
|
||||||
this.process.kill('SIGKILL');
|
this.process.kill('SIGKILL');
|
||||||
}
|
}
|
||||||
this.process = undefined;
|
|
||||||
controls.engineEnabled = false;
|
|
||||||
console.log('[INFO] Caption engine process stopped');
|
|
||||||
}
|
}
|
||||||
|
this.process = undefined;
|
||||||
|
controls.engineEnabled = false;
|
||||||
|
console.log('[INFO] Caption engine process stopped');
|
||||||
|
if(controlWindow.window) sendControls(controlWindow.window);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,8 @@
|
|||||||
<a-card size="small" title="字幕样式设置">
|
<a-card size="small" title="字幕样式设置">
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<a @click="applyStyle">应用样式</a> |
|
<a @click="applyStyle">应用样式</a> |
|
||||||
<a @click="resetStyle">取消更改</a>
|
<a @click="backStyle">取消更改</a> |
|
||||||
|
<a @click="resetStyle">恢复默认</a>
|
||||||
</template>
|
</template>
|
||||||
<div class="style-item">
|
<div class="style-item">
|
||||||
<span class="style-label">字体族</span>
|
<span class="style-label">字体族</span>
|
||||||
@@ -168,7 +169,7 @@ function applyStyle(){
|
|||||||
captionStyle.sendStyleChange();
|
captionStyle.sendStyleChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetStyle(){
|
function backStyle(){
|
||||||
currentFontFamily.value = captionStyle.fontFamily;
|
currentFontFamily.value = captionStyle.fontFamily;
|
||||||
currentFontSize.value = captionStyle.fontSize;
|
currentFontSize.value = captionStyle.fontSize;
|
||||||
currentFontColor.value = captionStyle.fontColor;
|
currentFontColor.value = captionStyle.fontColor;
|
||||||
@@ -181,9 +182,13 @@ function resetStyle(){
|
|||||||
currentTransFontColor.value = captionStyle.transFontColor;
|
currentTransFontColor.value = captionStyle.transFontColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resetStyle() {
|
||||||
|
captionStyle.sendStyleReset();
|
||||||
|
}
|
||||||
|
|
||||||
watch(changeSignal, (val) => {
|
watch(changeSignal, (val) => {
|
||||||
if(val == true) {
|
if(val == true) {
|
||||||
resetStyle();
|
backStyle();
|
||||||
captionStyle.changeSignal = false;
|
captionStyle.changeSignal = false;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ export const useCaptionControlStore = defineStore('captionControl', () => {
|
|||||||
targetLang.value = controls.targetLang
|
targetLang.value = controls.targetLang
|
||||||
engine.value = controls.engine
|
engine.value = controls.engine
|
||||||
audio.value = controls.audio
|
audio.value = controls.audio
|
||||||
|
engineEnabled.value = controls.engineEnabled
|
||||||
translation.value = controls.translation
|
translation.value = controls.translation
|
||||||
customized.value = controls.customized
|
customized.value = controls.customized
|
||||||
customizedApp.value = controls.customizedApp
|
customizedApp.value = controls.customizedApp
|
||||||
@@ -84,7 +85,6 @@ export const useCaptionControlStore = defineStore('captionControl', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
window.electron.ipcRenderer.on('control.engine.already', () => {
|
window.electron.ipcRenderer.on('control.engine.already', () => {
|
||||||
engineEnabled.value = true
|
|
||||||
notification.open({
|
notification.open({
|
||||||
message: '字幕引擎已经启动',
|
message: '字幕引擎已经启动',
|
||||||
description: '字幕引擎已经启动,请勿重复启动'
|
description: '字幕引擎已经启动,请勿重复启动'
|
||||||
@@ -92,7 +92,6 @@ export const useCaptionControlStore = defineStore('captionControl', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
window.electron.ipcRenderer.on('control.engine.started', () => {
|
window.electron.ipcRenderer.on('control.engine.started', () => {
|
||||||
engineEnabled.value = true
|
|
||||||
const str0 =
|
const str0 =
|
||||||
`原语言:${sourceLang.value},是否翻译:${translation.value?'是':'否'},` +
|
`原语言:${sourceLang.value},是否翻译:${translation.value?'是':'否'},` +
|
||||||
`字幕引擎:${engine.value},音频类型:${audio.value ? '输入音频' : '输出音频'}` +
|
`字幕引擎:${engine.value},音频类型:${audio.value ? '输入音频' : '输出音频'}` +
|
||||||
@@ -105,7 +104,6 @@ export const useCaptionControlStore = defineStore('captionControl', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
window.electron.ipcRenderer.on('control.engine.stopped', () => {
|
window.electron.ipcRenderer.on('control.engine.stopped', () => {
|
||||||
engineEnabled.value = false
|
|
||||||
notification.open({
|
notification.open({
|
||||||
message: '字幕引擎停止',
|
message: '字幕引擎停止',
|
||||||
description: '可点击“启动字幕引擎”按钮重新启动'
|
description: '可点击“启动字幕引擎”按钮重新启动'
|
||||||
|
|||||||
@@ -40,6 +40,10 @@ export const useCaptionStyleStore = defineStore('captionStyle', () => {
|
|||||||
window.electron.ipcRenderer.send('control.style.change', styles)
|
window.electron.ipcRenderer.send('control.style.change', styles)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sendStyleReset() {
|
||||||
|
window.electron.ipcRenderer.send('control.style.reset')
|
||||||
|
}
|
||||||
|
|
||||||
window.electron.ipcRenderer.on('caption.style.set', (_, args) => {
|
window.electron.ipcRenderer.on('caption.style.set', (_, args) => {
|
||||||
fontFamily.value = args.fontFamily
|
fontFamily.value = args.fontFamily
|
||||||
fontSize.value = args.fontSize
|
fontSize.value = args.fontSize
|
||||||
@@ -65,6 +69,7 @@ export const useCaptionStyleStore = defineStore('captionStyle', () => {
|
|||||||
transFontColor, // 翻译字体颜色
|
transFontColor, // 翻译字体颜色
|
||||||
backgroundRGBA, // 带透明度的背景颜色
|
backgroundRGBA, // 带透明度的背景颜色
|
||||||
sendStyleChange, // 发送样式改变
|
sendStyleChange, // 发送样式改变
|
||||||
|
sendStyleReset, // 恢复默认样式
|
||||||
changeSignal // 样式改变信号
|
changeSignal // 样式改变信号
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
Reference in New Issue
Block a user