mirror of
https://github.com/HiMeditator/auto-caption.git
synced 2026-04-09 22:17:40 +08:00
feat(log): 添加软件日志功能
- 新增 SoftwareLog 相关接口和数据结构 - 实现日志数据的收集和展示 - 添加日志相关的国际化支持 - 优化控制页面布局,支持日志切换显示
This commit is contained in:
@@ -284,6 +284,16 @@
|
||||
|
||||
**数据类型:** `Controls`
|
||||
|
||||
### `control.softwareLog.add`
|
||||
|
||||
**介绍:** 添加一条新的日志数据
|
||||
|
||||
**发起方:** 后端
|
||||
|
||||
**接收方:** 前端控制窗口
|
||||
|
||||
**数据类型:** `SoftwareLog`
|
||||
|
||||
### `both.styles.set`
|
||||
|
||||
**介绍:** 后端将最新字幕样式发送给前端,前端进行设置
|
||||
|
||||
@@ -76,7 +76,7 @@ if __name__ == "__main__":
|
||||
# vosk only
|
||||
parser.add_argument('-m', '--model_path', default='', help='The path to the vosk model.')
|
||||
|
||||
args = parser.parse_args()
|
||||
args = parser.parse_args()
|
||||
if int(args.port) == 0:
|
||||
thread_data.status = "running"
|
||||
else:
|
||||
|
||||
@@ -7,8 +7,10 @@ import icon from '../../build/icon.png?asset'
|
||||
import { captionWindow } from './CaptionWindow'
|
||||
import { allConfig } from './utils/AllConfig'
|
||||
import { captionEngine } from './utils/CaptionEngine'
|
||||
import { Log } from './utils/Log'
|
||||
|
||||
class ControlWindow {
|
||||
mounted: boolean = false;
|
||||
window: BrowserWindow | undefined;
|
||||
|
||||
public createWindow(): void {
|
||||
@@ -34,6 +36,7 @@ class ControlWindow {
|
||||
})
|
||||
|
||||
this.window.on('closed', () => {
|
||||
this.mounted = false
|
||||
this.window = undefined
|
||||
allConfig.writeConfig()
|
||||
})
|
||||
@@ -63,7 +66,8 @@ class ControlWindow {
|
||||
})
|
||||
|
||||
ipcMain.handle('both.window.mounted', () => {
|
||||
return allConfig.getFullConfig()
|
||||
this.mounted = true
|
||||
return allConfig.getFullConfig(Log.getAndClearLogQueue())
|
||||
})
|
||||
|
||||
ipcMain.handle('control.nativeTheme.get', () => {
|
||||
|
||||
@@ -45,6 +45,13 @@ export interface CaptionItem {
|
||||
translation: string
|
||||
}
|
||||
|
||||
export interface SoftwareLogItem {
|
||||
type: "INFO" | "WARN" | "ERROR",
|
||||
index: number,
|
||||
time: string,
|
||||
text: string
|
||||
}
|
||||
|
||||
export interface FullConfig {
|
||||
platform: string,
|
||||
uiLanguage: UILanguage,
|
||||
@@ -53,7 +60,8 @@ export interface FullConfig {
|
||||
leftBarWidth: number,
|
||||
styles: Styles,
|
||||
controls: Controls,
|
||||
captionLog: CaptionItem[]
|
||||
captionLog: CaptionItem[],
|
||||
softwareLog: SoftwareLogItem[]
|
||||
}
|
||||
|
||||
export interface EngineInfo {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {
|
||||
UILanguage, UITheme, Styles, Controls,
|
||||
CaptionItem, FullConfig
|
||||
CaptionItem, FullConfig, SoftwareLogItem
|
||||
} from '../types'
|
||||
import { Log } from './Log'
|
||||
import { app, BrowserWindow } from 'electron'
|
||||
@@ -88,7 +88,7 @@ class AllConfig {
|
||||
Log.info('Write Config to:', configPath)
|
||||
}
|
||||
|
||||
public getFullConfig(): FullConfig {
|
||||
public getFullConfig(softwareLog: SoftwareLogItem[]): FullConfig {
|
||||
return {
|
||||
platform: process.platform,
|
||||
uiLanguage: this.uiLanguage,
|
||||
@@ -97,7 +97,8 @@ class AllConfig {
|
||||
leftBarWidth: this.leftBarWidth,
|
||||
styles: this.styles,
|
||||
controls: this.controls,
|
||||
captionLog: this.captionLog
|
||||
captionLog: this.captionLog,
|
||||
softwareLog: softwareLog
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -85,12 +85,16 @@ export class CaptionEngine {
|
||||
}
|
||||
}
|
||||
Log.info('Engine Path:', this.appPath)
|
||||
Log.info('Engine Command:', this.command)
|
||||
if(this.command.length > 2 && this.command.at(-2) === '-k') {
|
||||
const _command = [...this.command]
|
||||
_command[_command.length -1] = _command[_command.length -1].replace(/./g, '*')
|
||||
Log.info('Engine Command:', _command)
|
||||
}
|
||||
else Log.info('Engine Command:', this.command)
|
||||
return true
|
||||
}
|
||||
|
||||
public connect() {
|
||||
Log.info('Connecting to caption engine server...')
|
||||
if(this.client) { Log.warn('Client already exists, ignoring...') }
|
||||
this.client = net.createConnection({ port: this.port }, () => {
|
||||
Log.info('Connected to caption engine server');
|
||||
@@ -177,7 +181,6 @@ export class CaptionEngine {
|
||||
this.client = undefined
|
||||
}
|
||||
this.status = 'stopping'
|
||||
Log.info('Caption engine process stopping...')
|
||||
this.timerID = setTimeout(() => {
|
||||
if(this.status !== 'stopping') return
|
||||
Log.warn('Engine process still not stopped, trying to kill...')
|
||||
@@ -226,7 +229,7 @@ function handleEngineData(data: any) {
|
||||
Log.info('Engine Info:', data.content)
|
||||
}
|
||||
else if(data.command === 'usage') {
|
||||
Log.info('Engine Usage: ', data.content)
|
||||
Log.info('Engine Token Usage: ', data.content)
|
||||
}
|
||||
else {
|
||||
Log.warn('Unknown command:', data)
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
import { controlWindow } from "../ControlWindow"
|
||||
import { type SoftwareLogItem } from "../types"
|
||||
|
||||
let logIndex = 0
|
||||
const logQueue: SoftwareLogItem[] = []
|
||||
|
||||
function getTimeString() {
|
||||
const now = new Date()
|
||||
const HH = String(now.getHours()).padStart(2, '0')
|
||||
@@ -8,15 +14,45 @@ function getTimeString() {
|
||||
}
|
||||
|
||||
export class Log {
|
||||
static getAndClearLogQueue() {
|
||||
const copiedQueue = structuredClone(logQueue)
|
||||
logQueue.length = 0
|
||||
return copiedQueue
|
||||
}
|
||||
|
||||
static handleLog(logType: "INFO" | "WARN" | "ERROR", ...msg: any[]) {
|
||||
const timeStr = getTimeString()
|
||||
const logPre = `[${logType} ${timeStr}]`
|
||||
let logStr = ""
|
||||
for(let i = 0; i < msg.length; i++) {
|
||||
logStr += i ? " " : ""
|
||||
if(typeof msg[i] === "string") logStr += msg[i]
|
||||
else logStr += JSON.stringify(msg[i], undefined, 2)
|
||||
}
|
||||
console.log(logPre, logStr)
|
||||
const logItem: SoftwareLogItem = {
|
||||
type: logType,
|
||||
index: ++logIndex,
|
||||
time: timeStr,
|
||||
text: logStr
|
||||
}
|
||||
if(controlWindow.mounted && controlWindow.window) {
|
||||
controlWindow.window.webContents.send('control.softwareLog.add', logItem)
|
||||
}
|
||||
else {
|
||||
logQueue.push(logItem)
|
||||
}
|
||||
}
|
||||
|
||||
static info(...msg: any[]){
|
||||
console.log(`[INFO ${getTimeString()}]`, ...msg)
|
||||
this.handleLog("INFO", ...msg)
|
||||
}
|
||||
|
||||
static warn(...msg: any[]){
|
||||
console.warn(`[WARN ${getTimeString()}]`, ...msg)
|
||||
this.handleLog("WARN", ...msg)
|
||||
}
|
||||
|
||||
static error(...msg: any[]){
|
||||
console.error(`[ERROR ${getTimeString()}]`, ...msg)
|
||||
this.handleLog("ERROR", ...msg)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Auto Caption</title>
|
||||
<title>Auto Caption v0.7.0</title>
|
||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { onMounted } from 'vue'
|
||||
import { FullConfig } from './types'
|
||||
import { useCaptionLogStore } from './stores/captionLog'
|
||||
import { useSoftwareLogStore } from './stores/softwareLog'
|
||||
import { useCaptionStyleStore } from './stores/captionStyle'
|
||||
import { useEngineControlStore } from './stores/engineControl'
|
||||
import { useGeneralSettingStore } from './stores/generalSetting'
|
||||
@@ -20,6 +21,7 @@ onMounted(() => {
|
||||
useEngineControlStore().platform = data.platform
|
||||
useEngineControlStore().setControls(data.controls)
|
||||
useCaptionLogStore().captionData = data.captionLog
|
||||
useSoftwareLogStore().softwareLogs = data.softwareLog
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,141 +1,139 @@
|
||||
<template>
|
||||
<div class="caption-list">
|
||||
<div>
|
||||
<a-app class="caption-title">
|
||||
<span style="margin-right: 30px;">{{ $t('log.title') }}</span>
|
||||
</a-app>
|
||||
<a-popover :title="$t('log.baseTime')">
|
||||
<template #content>
|
||||
<div class="base-time">
|
||||
<div class="base-time-container">
|
||||
<a-input
|
||||
type="number" min="0"
|
||||
v-model:value="baseHH"
|
||||
></a-input>
|
||||
<span class="base-time-label">{{ $t('log.hour') }}</span>
|
||||
</div>
|
||||
</div><span style="margin: 0 4px;">:</span>
|
||||
<div class="base-time">
|
||||
<div class="base-time-container">
|
||||
<a-input
|
||||
type="number" min="0" max="59"
|
||||
v-model:value="baseMM"
|
||||
></a-input>
|
||||
<span class="base-time-label">{{ $t('log.min') }}</span>
|
||||
</div>
|
||||
</div><span style="margin: 0 4px;">:</span>
|
||||
<div class="base-time">
|
||||
<div class="base-time-container">
|
||||
<a-input
|
||||
type="number" min="0" max="59"
|
||||
v-model:value="baseSS"
|
||||
></a-input>
|
||||
<span class="base-time-label">{{ $t('log.sec') }}</span>
|
||||
</div>
|
||||
</div><span style="margin: 0 4px;">.</span>
|
||||
<div class="base-time">
|
||||
<div class="base-time-container">
|
||||
<a-input
|
||||
type="number" min="0" max="999"
|
||||
v-model:value="baseMS"
|
||||
></a-input>
|
||||
<span class="base-time-label">{{ $t('log.ms') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<a-button
|
||||
type="primary"
|
||||
style="margin-right: 20px;"
|
||||
@click="changeBaseTime"
|
||||
:disabled="captionData.length === 0"
|
||||
>{{ $t('log.changeTime') }}</a-button>
|
||||
</a-popover>
|
||||
<a-popover :title="$t('log.exportOptions')">
|
||||
<template #content>
|
||||
<div class="input-item">
|
||||
<span class="input-label">{{ $t('log.exportFormat') }}</span>
|
||||
<a-radio-group v-model:value="exportFormat">
|
||||
<a-radio-button value="srt"><code>.srt</code></a-radio-button>
|
||||
<a-radio-button value="json"><code>.json</code></a-radio-button>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
<div class="input-item">
|
||||
<span class="input-label">{{ $t('log.exportContent') }}</span>
|
||||
<a-radio-group v-model:value="contentOption">
|
||||
<a-radio-button value="both">{{ $t('log.both') }}</a-radio-button>
|
||||
<a-radio-button value="source">{{ $t('log.source') }}</a-radio-button>
|
||||
<a-radio-button value="target">{{ $t('log.translation') }}</a-radio-button>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
</template>
|
||||
<a-button
|
||||
style="margin-right: 20px;"
|
||||
@click="exportCaptions"
|
||||
:disabled="captionData.length === 0"
|
||||
>{{ $t('log.export') }}</a-button>
|
||||
</a-popover>
|
||||
<a-popover :title="$t('log.copyOptions')">
|
||||
<template #content>
|
||||
<div class="input-item">
|
||||
<span class="input-label">{{ $t('log.addIndex') }}</span>
|
||||
<a-switch v-model:checked="showIndex" />
|
||||
<span class="input-label">{{ $t('log.copyTime') }}</span>
|
||||
<a-switch v-model:checked="copyTime" />
|
||||
</div>
|
||||
<div class="input-item">
|
||||
<span class="input-label">{{ $t('log.copyContent') }}</span>
|
||||
<a-radio-group v-model:value="contentOption">
|
||||
<a-radio-button value="both">{{ $t('log.both') }}</a-radio-button>
|
||||
<a-radio-button value="source">{{ $t('log.source') }}</a-radio-button>
|
||||
<a-radio-button value="target">{{ $t('log.translation') }}</a-radio-button>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
<div class="input-item">
|
||||
<span class="input-label">{{ $t('log.copyNum') }}</span>
|
||||
<a-radio-group v-model:value="copyNum">
|
||||
<a-radio-button :value="0"><code>[:]</code></a-radio-button>
|
||||
<a-radio-button :value="1"><code>[-1:]</code></a-radio-button>
|
||||
<a-radio-button :value="2"><code>[-2:]</code></a-radio-button>
|
||||
<a-radio-button :value="3"><code>[-3:]</code></a-radio-button>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
</template>
|
||||
<a-button
|
||||
style="margin-right: 20px;"
|
||||
@click="copyCaptions"
|
||||
>{{ $t('log.copy') }}</a-button>
|
||||
</a-popover>
|
||||
<a-button
|
||||
danger
|
||||
@click="clearCaptions"
|
||||
>{{ $t('log.clear') }}</a-button>
|
||||
<div>
|
||||
<div class="caption-title">
|
||||
<span style="margin-right: 30px;">{{ $t('log.title') }}</span>
|
||||
</div>
|
||||
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="captionData"
|
||||
v-model:pagination="pagination"
|
||||
style="margin-top: 10px;"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'index'">
|
||||
{{ record.index }}
|
||||
</template>
|
||||
<template v-if="column.key === 'time'">
|
||||
<div class="time-cell">
|
||||
<div class="time-start">{{ record.time_s }}</div>
|
||||
<div class="time-end">{{ record.time_t }}</div>
|
||||
<a-popover :title="$t('log.baseTime')">
|
||||
<template #content>
|
||||
<div class="base-time">
|
||||
<div class="base-time-container">
|
||||
<a-input
|
||||
type="number" min="0"
|
||||
v-model:value="baseHH"
|
||||
></a-input>
|
||||
<span class="base-time-label">{{ $t('log.hour') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="column.key === 'content'">
|
||||
<div class="caption-content">
|
||||
<div class="caption-text">{{ record.text }}</div>
|
||||
<div class="caption-translation">{{ record.translation }}</div>
|
||||
</div><span style="margin: 0 4px;">:</span>
|
||||
<div class="base-time">
|
||||
<div class="base-time-container">
|
||||
<a-input
|
||||
type="number" min="0" max="59"
|
||||
v-model:value="baseMM"
|
||||
></a-input>
|
||||
<span class="base-time-label">{{ $t('log.min') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</div><span style="margin: 0 4px;">:</span>
|
||||
<div class="base-time">
|
||||
<div class="base-time-container">
|
||||
<a-input
|
||||
type="number" min="0" max="59"
|
||||
v-model:value="baseSS"
|
||||
></a-input>
|
||||
<span class="base-time-label">{{ $t('log.sec') }}</span>
|
||||
</div>
|
||||
</div><span style="margin: 0 4px;">.</span>
|
||||
<div class="base-time">
|
||||
<div class="base-time-container">
|
||||
<a-input
|
||||
type="number" min="0" max="999"
|
||||
v-model:value="baseMS"
|
||||
></a-input>
|
||||
<span class="base-time-label">{{ $t('log.ms') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</a-table>
|
||||
<a-button
|
||||
type="primary"
|
||||
style="margin-right: 20px;"
|
||||
@click="changeBaseTime"
|
||||
:disabled="captionData.length === 0"
|
||||
>{{ $t('log.changeTime') }}</a-button>
|
||||
</a-popover>
|
||||
<a-popover :title="$t('log.exportOptions')">
|
||||
<template #content>
|
||||
<div class="input-item">
|
||||
<span class="input-label">{{ $t('log.exportFormat') }}</span>
|
||||
<a-radio-group v-model:value="exportFormat">
|
||||
<a-radio-button value="srt"><code>.srt</code></a-radio-button>
|
||||
<a-radio-button value="json"><code>.json</code></a-radio-button>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
<div class="input-item">
|
||||
<span class="input-label">{{ $t('log.exportContent') }}</span>
|
||||
<a-radio-group v-model:value="contentOption">
|
||||
<a-radio-button value="both">{{ $t('log.both') }}</a-radio-button>
|
||||
<a-radio-button value="source">{{ $t('log.source') }}</a-radio-button>
|
||||
<a-radio-button value="target">{{ $t('log.translation') }}</a-radio-button>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
</template>
|
||||
<a-button
|
||||
style="margin-right: 20px;"
|
||||
@click="exportCaptions"
|
||||
:disabled="captionData.length === 0"
|
||||
>{{ $t('log.export') }}</a-button>
|
||||
</a-popover>
|
||||
<a-popover :title="$t('log.copyOptions')">
|
||||
<template #content>
|
||||
<div class="input-item">
|
||||
<span class="input-label">{{ $t('log.addIndex') }}</span>
|
||||
<a-switch v-model:checked="showIndex" />
|
||||
<span class="input-label">{{ $t('log.copyTime') }}</span>
|
||||
<a-switch v-model:checked="copyTime" />
|
||||
</div>
|
||||
<div class="input-item">
|
||||
<span class="input-label">{{ $t('log.copyContent') }}</span>
|
||||
<a-radio-group v-model:value="contentOption">
|
||||
<a-radio-button value="both">{{ $t('log.both') }}</a-radio-button>
|
||||
<a-radio-button value="source">{{ $t('log.source') }}</a-radio-button>
|
||||
<a-radio-button value="target">{{ $t('log.translation') }}</a-radio-button>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
<div class="input-item">
|
||||
<span class="input-label">{{ $t('log.copyNum') }}</span>
|
||||
<a-radio-group v-model:value="copyNum">
|
||||
<a-radio-button :value="0"><code>[:]</code></a-radio-button>
|
||||
<a-radio-button :value="1"><code>[-1:]</code></a-radio-button>
|
||||
<a-radio-button :value="2"><code>[-2:]</code></a-radio-button>
|
||||
<a-radio-button :value="3"><code>[-3:]</code></a-radio-button>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
</template>
|
||||
<a-button
|
||||
style="margin-right: 20px;"
|
||||
@click="copyCaptions"
|
||||
>{{ $t('log.copy') }}</a-button>
|
||||
</a-popover>
|
||||
<a-button
|
||||
danger
|
||||
@click="clearCaptions"
|
||||
>{{ $t('log.clear') }}</a-button>
|
||||
</div>
|
||||
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="captionData"
|
||||
v-model:pagination="pagination"
|
||||
style="margin-top: 10px;"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'index'">
|
||||
{{ record.index }}
|
||||
</template>
|
||||
<template v-if="column.key === 'time'">
|
||||
<div class="time-cell">
|
||||
<div class="time-start">{{ record.time_s }}</div>
|
||||
<div class="time-end">{{ record.time_t }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="column.key === 'content'">
|
||||
<div class="caption-content">
|
||||
<div class="caption-text">{{ record.text }}</div>
|
||||
<div class="caption-translation">{{ record.translation }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -195,7 +193,7 @@ const columns = [
|
||||
title: 'time',
|
||||
dataIndex: 'time',
|
||||
key: 'time',
|
||||
width: 160,
|
||||
width: 150,
|
||||
sorter: (a: CaptionItem, b: CaptionItem) => {
|
||||
if(a.time_s <= b.time_s) return -1
|
||||
return 1
|
||||
@@ -300,7 +298,7 @@ function clearCaptions() {
|
||||
display: inline-block;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.base-time {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="caption-stat">
|
||||
<a-row>
|
||||
<a-col :span="6">
|
||||
<a-col :span="5">
|
||||
<a-statistic
|
||||
:title="$t('status.engine')"
|
||||
:value="customized?$t('status.customized'):engine"
|
||||
@@ -36,7 +36,7 @@
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
<a-col :span="6" @mouseenter="getEngineInfo" style="cursor: pointer;">
|
||||
<a-col :span="5" @mouseenter="getEngineInfo" style="cursor: pointer;">
|
||||
<a-statistic
|
||||
:title="$t('status.status')"
|
||||
:value="engineEnabled?$t('status.started'):$t('status.stopped')"
|
||||
@@ -47,10 +47,13 @@
|
||||
</a-statistic>
|
||||
</a-col>
|
||||
</a-popover>
|
||||
<a-col :span="6">
|
||||
<a-col :span="5">
|
||||
<a-statistic :title="$t('status.logNumber')" :value="captionData.length" />
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-col :span="5">
|
||||
<a-statistic :title="$t('status.logNumber2')" :value="softwareLogs.length" />
|
||||
</a-col>
|
||||
<a-col :span="4">
|
||||
<div class="about-tag">{{ $t('status.aboutProj') }}</div>
|
||||
<GithubOutlined class="proj-info" @click="showAbout = true"/>
|
||||
</a-col>
|
||||
@@ -128,14 +131,17 @@ import { EngineInfo } from '@renderer/types'
|
||||
import { ref, watch } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useCaptionLogStore } from '@renderer/stores/captionLog'
|
||||
import { useSoftwareLogStore } from '@renderer/stores/softwareLog'
|
||||
import { useEngineControlStore } from '@renderer/stores/engineControl'
|
||||
import { GithubOutlined, InfoCircleOutlined } from '@ant-design/icons-vue';
|
||||
import { GithubOutlined, InfoCircleOutlined } from '@ant-design/icons-vue'
|
||||
|
||||
const showAbout = ref(false)
|
||||
const pending = ref(false)
|
||||
|
||||
const captionLog = useCaptionLogStore()
|
||||
const { captionData } = storeToRefs(captionLog)
|
||||
const softwareLog = useSoftwareLogStore()
|
||||
const { softwareLogs } = storeToRefs(softwareLog)
|
||||
const engineControl = useEngineControlStore()
|
||||
const { engineEnabled, engine, customized, errorSignal } = storeToRefs(engineControl)
|
||||
|
||||
|
||||
@@ -33,10 +33,11 @@
|
||||
<a-radio-group v-model:value="uiColor">
|
||||
<template v-for="color in colorList" :key="color">
|
||||
<a-radio-button :value="color"
|
||||
:style="{
|
||||
backgroundColor: color
|
||||
}"
|
||||
> </a-radio-button>
|
||||
:style="{backgroundColor: color}"
|
||||
>
|
||||
<CheckOutlined style="color: white;" v-if="color === uiColor" />
|
||||
<span v-else> </span>
|
||||
</a-radio-button>
|
||||
</template>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
@@ -55,7 +56,7 @@
|
||||
<script setup lang="ts">
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useGeneralSettingStore } from '@renderer/stores/generalSetting'
|
||||
import { InfoCircleOutlined } from '@ant-design/icons-vue';
|
||||
import { InfoCircleOutlined, CheckOutlined } from '@ant-design/icons-vue'
|
||||
|
||||
const colorList = [
|
||||
'#1677ff',
|
||||
|
||||
111
src/renderer/src/components/SoftwareLog.vue
Normal file
111
src/renderer/src/components/SoftwareLog.vue
Normal file
@@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="log-title">
|
||||
<span style="margin-right: 30px;">{{ $t('log.title2') }}</span>
|
||||
</div>
|
||||
<a-button
|
||||
danger
|
||||
@click="softwareLog.clear()"
|
||||
>{{ $t('log.clear') }}</a-button>
|
||||
</div>
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="softwareLogs"
|
||||
v-model:pagination="pagination"
|
||||
style="margin-top: 10px;"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'type'">
|
||||
<span :class="record.type">{{ record.type }}</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'index'">
|
||||
{{ record.index }}
|
||||
</template>
|
||||
<template v-if="column.key === 'content'">
|
||||
<code>{{ record.text }}</code>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useSoftwareLogStore } from '@renderer/stores/softwareLog'
|
||||
import { type SoftwareLogItem } from '../types'
|
||||
|
||||
const softwareLog = useSoftwareLogStore()
|
||||
const { softwareLogs } = storeToRefs(softwareLog)
|
||||
|
||||
const pagination = ref({
|
||||
current: 1,
|
||||
pageSize: 20,
|
||||
showSizeChanger: true,
|
||||
pageSizeOptions: ['10', '20', '50', '100'],
|
||||
onChange: (page: number, pageSize: number) => {
|
||||
pagination.value.current = page
|
||||
pagination.value.pageSize = pageSize
|
||||
},
|
||||
onShowSizeChange: (current: number, size: number) => {
|
||||
pagination.value.current = current
|
||||
pagination.value.pageSize = size
|
||||
}
|
||||
})
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'index',
|
||||
dataIndex: 'index',
|
||||
key: 'index',
|
||||
width: 80,
|
||||
sorter: (a: SoftwareLogItem, b: SoftwareLogItem) => {
|
||||
if(a.index <= b.index) return -1
|
||||
return 1
|
||||
},
|
||||
sortDirections: ['descend'],
|
||||
defaultSortOrder: 'descend',
|
||||
},
|
||||
{
|
||||
title: 'type',
|
||||
dataIndex: 'type',
|
||||
key: 'type',
|
||||
width: 80,
|
||||
sorter: (a: SoftwareLogItem, b: SoftwareLogItem) => {
|
||||
if(a.type <= b.type) return -1
|
||||
return 1
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'time',
|
||||
dataIndex: 'time',
|
||||
key: 'time',
|
||||
width: 120,
|
||||
sortDirections: ['descend'],
|
||||
},
|
||||
{
|
||||
title: 'content',
|
||||
dataIndex: 'content',
|
||||
key: 'content',
|
||||
},
|
||||
]
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.log-title {
|
||||
display: inline-block;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.WARN {
|
||||
color: #ff7c05;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.ERROR {
|
||||
color: #ff0000;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
41
src/renderer/src/i18n/config/logMenu.ts
Normal file
41
src/renderer/src/i18n/config/logMenu.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { h } from 'vue';
|
||||
import { OrderedListOutlined, FileTextOutlined } from '@ant-design/icons-vue'
|
||||
|
||||
export const logMenu = {
|
||||
zh: [
|
||||
{
|
||||
key: 'captionLog',
|
||||
icon: () => h(OrderedListOutlined),
|
||||
label: '字幕记录',
|
||||
},
|
||||
{
|
||||
key: 'projLog',
|
||||
icon: () => h(FileTextOutlined),
|
||||
label: '日志记录',
|
||||
},
|
||||
],
|
||||
en: [
|
||||
{
|
||||
key: 'captionLog',
|
||||
icon: () => h(OrderedListOutlined),
|
||||
label: 'Caption Log',
|
||||
},
|
||||
{
|
||||
key: 'projLog',
|
||||
icon: () => h(FileTextOutlined),
|
||||
label: 'Software Log',
|
||||
},
|
||||
],
|
||||
ja: [
|
||||
{
|
||||
key: 'captionLog',
|
||||
icon: () => h(OrderedListOutlined),
|
||||
label: '字幕記録',
|
||||
},
|
||||
{
|
||||
key: 'projLog',
|
||||
icon: () => h(FileTextOutlined),
|
||||
label: 'ログ記録',
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -18,3 +18,4 @@ export * from './config/engine'
|
||||
export * from './config/audio'
|
||||
export * from './config/theme'
|
||||
export * from './config/linebreak'
|
||||
export * from './config/logMenu'
|
||||
@@ -106,6 +106,7 @@ export default {
|
||||
"started": "Started",
|
||||
"stopped": "Not Started",
|
||||
"logNumber": "Caption Count",
|
||||
"logNumber2": "Log Count",
|
||||
"aboutProj": "About Project",
|
||||
"openCaption": "Open Caption Window",
|
||||
"startEngine": "Start Caption Engine",
|
||||
@@ -146,6 +147,7 @@ export default {
|
||||
"copyNum": "Copy Count",
|
||||
"all": "All",
|
||||
"copySuccess": "Subtitle copied to clipboard",
|
||||
"clear": "Clear Log"
|
||||
"clear": "Clear Log",
|
||||
"title2": "Software Log"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +106,7 @@ export default {
|
||||
"started": "開始済み",
|
||||
"stopped": "未開始",
|
||||
"logNumber": "字幕数",
|
||||
"logNumber2": "ログ数",
|
||||
"aboutProj": "プロジェクト情報",
|
||||
"openCaption": "字幕ウィンドウを開く",
|
||||
"startEngine": "字幕エンジンを開始",
|
||||
@@ -124,7 +125,7 @@ export default {
|
||||
}
|
||||
},
|
||||
log: {
|
||||
"title": "字幕ログ",
|
||||
"title": "字幕記録",
|
||||
"changeTime": "時間を変更",
|
||||
"baseTime": "最初の字幕開始時間",
|
||||
"hour": "時",
|
||||
@@ -132,7 +133,7 @@ export default {
|
||||
"sec": "秒",
|
||||
"ms": "ミリ秒",
|
||||
"export": "エクスポート",
|
||||
"copy": "ログをコピー",
|
||||
"copy": "記録をコピー",
|
||||
"exportOptions": "エクスポートオプション",
|
||||
"exportFormat": "形式",
|
||||
"exportContent": "内容",
|
||||
@@ -146,6 +147,7 @@ export default {
|
||||
"copyNum": "コピー数",
|
||||
"all": "すべて",
|
||||
"copySuccess": "字幕がクリップボードにコピーされました",
|
||||
"clear": "ログをクリア"
|
||||
"clear": "記録をクリア",
|
||||
"title2": "ログ記録"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +106,7 @@ export default {
|
||||
"started": "已启动",
|
||||
"stopped": "未启动",
|
||||
"logNumber": "字幕数量",
|
||||
"logNumber2": "日志数量",
|
||||
"aboutProj": "项目关于",
|
||||
"openCaption": "打开字幕窗口",
|
||||
"startEngine": "启动字幕引擎",
|
||||
@@ -146,6 +147,7 @@ export default {
|
||||
"copyNum": "复制数量",
|
||||
"all": "全部",
|
||||
"copySuccess": "字幕已复制到剪贴板",
|
||||
"clear": "清空记录"
|
||||
"clear": "清空记录",
|
||||
"title2": "日志记录"
|
||||
}
|
||||
}
|
||||
|
||||
21
src/renderer/src/stores/softwareLog.ts
Normal file
21
src/renderer/src/stores/softwareLog.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { ref } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
import { type SoftwareLogItem } from '../types'
|
||||
|
||||
export const useSoftwareLogStore = defineStore('softwareLog', () => {
|
||||
const softwareLogs = ref<SoftwareLogItem[]>([])
|
||||
|
||||
function clear() {
|
||||
softwareLogs.value = []
|
||||
}
|
||||
|
||||
window.electron.ipcRenderer.on('control.softwareLog.add', (_, log) => {
|
||||
softwareLogs.value.push(log)
|
||||
console.log(log)
|
||||
})
|
||||
|
||||
return {
|
||||
softwareLogs,
|
||||
clear
|
||||
}
|
||||
})
|
||||
@@ -45,6 +45,13 @@ export interface CaptionItem {
|
||||
translation: string
|
||||
}
|
||||
|
||||
export interface SoftwareLogItem {
|
||||
type: "INFO" | "WARN" | "ERROR",
|
||||
index: number,
|
||||
time: string,
|
||||
text: string
|
||||
}
|
||||
|
||||
export interface FullConfig {
|
||||
platform: string,
|
||||
uiLanguage: UILanguage,
|
||||
@@ -53,7 +60,8 @@ export interface FullConfig {
|
||||
leftBarWidth: number,
|
||||
styles: Styles,
|
||||
controls: Controls,
|
||||
captionLog: CaptionItem[]
|
||||
captionLog: CaptionItem[],
|
||||
softwareLog: SoftwareLogItem[]
|
||||
}
|
||||
|
||||
export interface EngineInfo {
|
||||
|
||||
@@ -11,7 +11,13 @@
|
||||
<a-col :span="24 - leftBarWidth">
|
||||
<div class="caption-data">
|
||||
<EngineStatus />
|
||||
<CaptionLog />
|
||||
<div class="log-container">
|
||||
<a-menu v-model:selectedKeys="current" mode="horizontal" :items="items" />
|
||||
<div style="padding: 16px;">
|
||||
<CaptionLog v-if="current[0] === 'captionLog'" />
|
||||
<SoftwareLog v-else />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
@@ -24,11 +30,22 @@ import CaptionStyle from '../components/CaptionStyle.vue'
|
||||
import EngineControl from '../components/EngineControl.vue'
|
||||
import EngineStatus from '@renderer/components/EngineStatus.vue'
|
||||
import CaptionLog from '../components/CaptionLog.vue'
|
||||
import SoftwareLog from '@renderer/components/SoftwareLog.vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useGeneralSettingStore } from '@renderer/stores/generalSetting'
|
||||
import { ref, watch } from 'vue'
|
||||
import { MenuProps } from 'ant-design-vue'
|
||||
import { logMenu } from '@renderer/i18n'
|
||||
|
||||
const generalSettingStore = useGeneralSettingStore()
|
||||
const { leftBarWidth, antdTheme } = storeToRefs(generalSettingStore)
|
||||
const { leftBarWidth, antdTheme, uiLanguage } = storeToRefs(generalSettingStore)
|
||||
|
||||
const current = ref<string[]>(['captionLog'])
|
||||
const items = ref<MenuProps['items']>(logMenu[uiLanguage.value])
|
||||
|
||||
watch(uiLanguage, (val) => {
|
||||
items.value = logMenu[val]
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -53,4 +70,10 @@ const { leftBarWidth, antdTheme } = storeToRefs(generalSettingStore)
|
||||
.caption-data::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.log-container {
|
||||
padding: 20px 10px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user