mirror of
https://github.com/HiMeditator/auto-caption.git
synced 2026-02-22 09:24:41 +08:00
feat(renderer):实现多行字幕显示功能
- 在 CaptionStyle 组件中添加字幕行数设置选项 - 修改组件以支持多行字幕显示 - 优化字幕数据处理逻辑,支持按时间顺序显示多条字幕
This commit is contained in:
@@ -63,7 +63,7 @@ class SosvRecognizer:
|
|||||||
vad_config.silero_vad.threshold = 0.5
|
vad_config.silero_vad.threshold = 0.5
|
||||||
vad_config.silero_vad.min_silence_duration = 0.1
|
vad_config.silero_vad.min_silence_duration = 0.1
|
||||||
vad_config.silero_vad.min_speech_duration = 0.25
|
vad_config.silero_vad.min_speech_duration = 0.25
|
||||||
vad_config.silero_vad.max_speech_duration = 8
|
vad_config.silero_vad.max_speech_duration = 5
|
||||||
vad_config.sample_rate = 16000
|
vad_config.sample_rate = 16000
|
||||||
self.window_size = vad_config.silero_vad.window_size
|
self.window_size = vad_config.silero_vad.window_size
|
||||||
self.vad = sherpa_onnx.VoiceActivityDetector(vad_config, buffer_size_in_seconds=100)
|
self.vad = sherpa_onnx.VoiceActivityDetector(vad_config, buffer_size_in_seconds=100)
|
||||||
|
|||||||
@@ -17,8 +17,11 @@ def audio_recording(stream: AudioStream, resample: bool, record = False, path =
|
|||||||
wf = None
|
wf = None
|
||||||
full_name = ''
|
full_name = ''
|
||||||
if record:
|
if record:
|
||||||
if path != '' and path[-1] != '/':
|
if path != '':
|
||||||
path += '/'
|
if path.startswith('"') and path.endswith('"'):
|
||||||
|
path = path[1:-1]
|
||||||
|
if path[-1] != '/':
|
||||||
|
path += '/'
|
||||||
cur_dt = datetime.datetime.now()
|
cur_dt = datetime.datetime.now()
|
||||||
name = cur_dt.strftime("audio-%Y-%m-%dT%H-%M-%S")
|
name = cur_dt.strftime("audio-%Y-%m-%dT%H-%M-%S")
|
||||||
full_name = f'{path}{name}.wav'
|
full_name = f'{path}{name}.wav'
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export interface Controls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Styles {
|
export interface Styles {
|
||||||
|
lineNumber: number,
|
||||||
lineBreak: number,
|
lineBreak: number,
|
||||||
fontFamily: string,
|
fontFamily: string,
|
||||||
fontSize: number,
|
fontSize: number,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ function getDesktopPath() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const defaultStyles: Styles = {
|
const defaultStyles: Styles = {
|
||||||
|
lineNumber: 1,
|
||||||
lineBreak: 1,
|
lineBreak: 1,
|
||||||
fontFamily: 'sans-serif',
|
fontFamily: 'sans-serif',
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ export class CaptionEngine {
|
|||||||
this.command.push('-a', allConfig.controls.audio ? '1' : '0')
|
this.command.push('-a', allConfig.controls.audio ? '1' : '0')
|
||||||
if(allConfig.controls.recording) {
|
if(allConfig.controls.recording) {
|
||||||
this.command.push('-r', '1')
|
this.command.push('-r', '1')
|
||||||
this.command.push('-rp', allConfig.controls.recordingPath)
|
this.command.push('-rp', `"${allConfig.controls.recordingPath}"`)
|
||||||
}
|
}
|
||||||
this.port = Math.floor(Math.random() * (65535 - 1024 + 1)) + 1024
|
this.port = Math.floor(Math.random() * (65535 - 1024 + 1)) + 1024
|
||||||
this.command.push('-p', this.port.toString())
|
this.command.push('-p', this.port.toString())
|
||||||
|
|||||||
@@ -6,6 +6,16 @@
|
|||||||
<a @click="resetStyle">{{ $t('style.resetStyle') }}</a>
|
<a @click="resetStyle">{{ $t('style.resetStyle') }}</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<div class="input-item">
|
||||||
|
<span class="input-label">{{ '字幕行数' }}</span>
|
||||||
|
<a-radio-group v-model:value="currentLineNumber">
|
||||||
|
<a-radio-button :value="1">1</a-radio-button>
|
||||||
|
<a-radio-button :value="2">2</a-radio-button>
|
||||||
|
<a-radio-button :value="3">3</a-radio-button>
|
||||||
|
<a-radio-button :value="4">4</a-radio-button>
|
||||||
|
</a-radio-group>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="input-item">
|
<div class="input-item">
|
||||||
<span class="input-label">{{ $t('style.longCaption') }}</span>
|
<span class="input-label">{{ $t('style.longCaption') }}</span>
|
||||||
<a-select
|
<a-select
|
||||||
@@ -178,28 +188,57 @@
|
|||||||
textShadow: currentTextShadow ? `${currentOffsetX}px ${currentOffsetY}px ${currentBlur}px ${currentTextShadowColor}` : 'none'
|
textShadow: currentTextShadow ? `${currentOffsetX}px ${currentOffsetY}px ${currentBlur}px ${currentTextShadowColor}` : 'none'
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<p :class="[currentLineBreak?'':'left-ellipsis']"
|
<template v-if="captionData.length">
|
||||||
:style="{
|
<template
|
||||||
fontFamily: currentFontFamily,
|
v-for="val in revArr[Math.min(currentLineNumber, captionData.length)]"
|
||||||
fontSize: currentFontSize + 'px',
|
:key="captionData[captionData.length - val].time_s"
|
||||||
color: currentFontColor,
|
>
|
||||||
fontWeight: currentFontWeight * 100
|
<p :class="[currentLineBreak?'':'left-ellipsis']"
|
||||||
}">
|
:style="{
|
||||||
<span v-if="captionData.length">{{ captionData[captionData.length-1].text }}</span>
|
fontFamily: currentFontFamily,
|
||||||
<span v-else>{{ $t('example.original') }}</span>
|
fontSize: currentFontSize + 'px',
|
||||||
</p>
|
color: currentFontColor,
|
||||||
<p :class="[currentLineBreak?'':'left-ellipsis']"
|
fontWeight: currentFontWeight * 100
|
||||||
v-if="currentTransDisplay"
|
}">
|
||||||
:style="{
|
<span>{{ captionData[captionData.length - val].text }}</span>
|
||||||
fontFamily: currentTransFontFamily,
|
</p>
|
||||||
fontSize: currentTransFontSize + 'px',
|
<p :class="[currentLineBreak?'':'left-ellipsis']"
|
||||||
color: currentTransFontColor,
|
v-if="currentTransDisplay && captionData[captionData.length - val].translation"
|
||||||
fontWeight: currentTransFontWeight * 100
|
:style="{
|
||||||
}"
|
fontFamily: currentTransFontFamily,
|
||||||
>
|
fontSize: currentTransFontSize + 'px',
|
||||||
<span v-if="captionData.length">{{ captionData[captionData.length-1].translation }}</span>
|
color: currentTransFontColor,
|
||||||
<span v-else>{{ $t('example.translation') }}</span>
|
fontWeight: currentTransFontWeight * 100
|
||||||
</p>
|
}"
|
||||||
|
>
|
||||||
|
<span>{{ captionData[captionData.length - val].translation }}</span>
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<template v-for="val in currentLineNumber" :key="val">
|
||||||
|
<p :class="[currentLineBreak?'':'left-ellipsis']"
|
||||||
|
:style="{
|
||||||
|
fontFamily: currentFontFamily,
|
||||||
|
fontSize: currentFontSize + 'px',
|
||||||
|
color: currentFontColor,
|
||||||
|
fontWeight: currentFontWeight * 100
|
||||||
|
}">
|
||||||
|
<span>{{ $t('example.original') }}</span>
|
||||||
|
</p>
|
||||||
|
<p :class="[currentLineBreak?'':'left-ellipsis']"
|
||||||
|
v-if="currentTransDisplay"
|
||||||
|
:style="{
|
||||||
|
fontFamily: currentTransFontFamily,
|
||||||
|
fontSize: currentTransFontSize + 'px',
|
||||||
|
color: currentTransFontColor,
|
||||||
|
fontWeight: currentTransFontWeight * 100
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<span>{{ $t('example.translation') }}</span>
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
|
|
||||||
@@ -212,6 +251,14 @@ import { storeToRefs } from 'pinia'
|
|||||||
import { notification } from 'ant-design-vue'
|
import { notification } from 'ant-design-vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useCaptionLogStore } from '@renderer/stores/captionLog';
|
import { useCaptionLogStore } from '@renderer/stores/captionLog';
|
||||||
|
|
||||||
|
const revArr = {
|
||||||
|
1: [1],
|
||||||
|
2: [2, 1],
|
||||||
|
3: [3, 2, 1],
|
||||||
|
4: [4, 3, 2, 1],
|
||||||
|
}
|
||||||
|
|
||||||
const captionLog = useCaptionLogStore();
|
const captionLog = useCaptionLogStore();
|
||||||
const { captionData } = storeToRefs(captionLog);
|
const { captionData } = storeToRefs(captionLog);
|
||||||
|
|
||||||
@@ -220,6 +267,7 @@ const { t } = useI18n()
|
|||||||
const captionStyle = useCaptionStyleStore()
|
const captionStyle = useCaptionStyleStore()
|
||||||
const { changeSignal } = storeToRefs(captionStyle)
|
const { changeSignal } = storeToRefs(captionStyle)
|
||||||
|
|
||||||
|
const currentLineNumber = ref<number>(1)
|
||||||
const currentLineBreak = ref<number>(0)
|
const currentLineBreak = ref<number>(0)
|
||||||
const currentFontFamily = ref<string>('sans-serif')
|
const currentFontFamily = ref<string>('sans-serif')
|
||||||
const currentFontSize = ref<number>(24)
|
const currentFontSize = ref<number>(24)
|
||||||
@@ -253,6 +301,7 @@ function useSameStyle(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
function applyStyle(){
|
function applyStyle(){
|
||||||
|
captionStyle.lineNumber = currentLineNumber.value;
|
||||||
captionStyle.lineBreak = currentLineBreak.value;
|
captionStyle.lineBreak = currentLineBreak.value;
|
||||||
captionStyle.fontFamily = currentFontFamily.value;
|
captionStyle.fontFamily = currentFontFamily.value;
|
||||||
captionStyle.fontSize = currentFontSize.value;
|
captionStyle.fontSize = currentFontSize.value;
|
||||||
@@ -282,6 +331,7 @@ function applyStyle(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
function backStyle(){
|
function backStyle(){
|
||||||
|
currentLineNumber.value = captionStyle.lineNumber;
|
||||||
currentLineBreak.value = captionStyle.lineBreak;
|
currentLineBreak.value = captionStyle.lineBreak;
|
||||||
currentFontFamily.value = captionStyle.fontFamily;
|
currentFontFamily.value = captionStyle.fontFamily;
|
||||||
currentFontSize.value = captionStyle.fontSize;
|
currentFontSize.value = captionStyle.fontSize;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { Styles } from '@renderer/types'
|
|||||||
import { breakOptions } from '@renderer/i18n'
|
import { breakOptions } from '@renderer/i18n'
|
||||||
|
|
||||||
export const useCaptionStyleStore = defineStore('captionStyle', () => {
|
export const useCaptionStyleStore = defineStore('captionStyle', () => {
|
||||||
|
const lineNumber = ref<number>(1)
|
||||||
const lineBreak = ref<number>(1)
|
const lineBreak = ref<number>(1)
|
||||||
const fontFamily = ref<string>('sans-serif')
|
const fontFamily = ref<string>('sans-serif')
|
||||||
const fontSize = ref<number>(24)
|
const fontSize = ref<number>(24)
|
||||||
@@ -38,6 +39,7 @@ export const useCaptionStyleStore = defineStore('captionStyle', () => {
|
|||||||
|
|
||||||
function sendStylesChange() {
|
function sendStylesChange() {
|
||||||
const styles: Styles = {
|
const styles: Styles = {
|
||||||
|
lineNumber: lineNumber.value,
|
||||||
lineBreak: lineBreak.value,
|
lineBreak: lineBreak.value,
|
||||||
fontFamily: fontFamily.value,
|
fontFamily: fontFamily.value,
|
||||||
fontSize: fontSize.value,
|
fontSize: fontSize.value,
|
||||||
@@ -65,6 +67,7 @@ export const useCaptionStyleStore = defineStore('captionStyle', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setStyles(args: Styles){
|
function setStyles(args: Styles){
|
||||||
|
lineNumber.value = args.lineNumber
|
||||||
lineBreak.value = args.lineBreak
|
lineBreak.value = args.lineBreak
|
||||||
fontFamily.value = args.fontFamily
|
fontFamily.value = args.fontFamily
|
||||||
fontSize.value = args.fontSize
|
fontSize.value = args.fontSize
|
||||||
@@ -91,6 +94,7 @@ export const useCaptionStyleStore = defineStore('captionStyle', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
lineNumber, // 显示字幕行数
|
||||||
lineBreak, // 换行方式
|
lineBreak, // 换行方式
|
||||||
fontFamily, // 字体族
|
fontFamily, // 字体族
|
||||||
fontSize, // 字体大小
|
fontSize, // 字体大小
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export interface Controls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Styles {
|
export interface Styles {
|
||||||
|
lineNumber: number,
|
||||||
lineBreak: number,
|
lineBreak: number,
|
||||||
fontFamily: string,
|
fontFamily: string,
|
||||||
fontSize: number,
|
fontSize: number,
|
||||||
|
|||||||
@@ -12,26 +12,53 @@
|
|||||||
textShadow: captionStyle.textShadow ? `${captionStyle.offsetX}px ${captionStyle.offsetY}px ${captionStyle.blur}px ${captionStyle.textShadowColor}` : 'none'
|
textShadow: captionStyle.textShadow ? `${captionStyle.offsetX}px ${captionStyle.offsetY}px ${captionStyle.blur}px ${captionStyle.textShadowColor}` : 'none'
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<p :class="[captionStyle.lineBreak?'':'left-ellipsis']" :style="{
|
<template v-if="captionData.length">
|
||||||
fontFamily: captionStyle.fontFamily,
|
<template
|
||||||
fontSize: captionStyle.fontSize + 'px',
|
v-for="val in revArr[Math.min(captionStyle.lineNumber, captionData.length)]"
|
||||||
color: captionStyle.fontColor,
|
:key="captionData[captionData.length - val].time_s"
|
||||||
fontWeight: captionStyle.fontWeight * 100
|
>
|
||||||
}">
|
<p :class="[captionStyle.lineBreak?'':'left-ellipsis']" :style="{
|
||||||
<span v-if="captionData.length">{{ captionData[captionData.length-1].text }}</span>
|
fontFamily: captionStyle.fontFamily,
|
||||||
<span v-else>{{ $t('example.original') }}</span>
|
fontSize: captionStyle.fontSize + 'px',
|
||||||
</p>
|
color: captionStyle.fontColor,
|
||||||
<p :class="[captionStyle.lineBreak?'':'left-ellipsis']"
|
fontWeight: captionStyle.fontWeight * 100
|
||||||
v-if="captionStyle.transDisplay"
|
}">
|
||||||
:style="{
|
<span>{{ captionData[captionData.length - val].text }}</span>
|
||||||
fontFamily: captionStyle.transFontFamily,
|
</p>
|
||||||
fontSize: captionStyle.transFontSize + 'px',
|
<p :class="[captionStyle.lineBreak?'':'left-ellipsis']"
|
||||||
color: captionStyle.transFontColor,
|
v-if="captionStyle.transDisplay && captionData[captionData.length - val].translation"
|
||||||
fontWeight: captionStyle.transFontWeight * 100
|
:style="{
|
||||||
}">
|
fontFamily: captionStyle.transFontFamily,
|
||||||
<span v-if="captionData.length">{{ captionData[captionData.length-1].translation }}</span>
|
fontSize: captionStyle.transFontSize + 'px',
|
||||||
<span v-else>{{ $t('example.translation') }}</span>
|
color: captionStyle.transFontColor,
|
||||||
</p>
|
fontWeight: captionStyle.transFontWeight * 100
|
||||||
|
}">
|
||||||
|
<span>{{ captionData[captionData.length - val].translation }}</span>
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<template v-for="val in captionStyle.lineNumber" :key="val">
|
||||||
|
<p :class="[captionStyle.lineBreak?'':'left-ellipsis']" :style="{
|
||||||
|
fontFamily: captionStyle.fontFamily,
|
||||||
|
fontSize: captionStyle.fontSize + 'px',
|
||||||
|
color: captionStyle.fontColor,
|
||||||
|
fontWeight: captionStyle.fontWeight * 100
|
||||||
|
}">
|
||||||
|
<span>{{ $t('example.original') }}</span>
|
||||||
|
</p>
|
||||||
|
<p :class="[captionStyle.lineBreak?'':'left-ellipsis']"
|
||||||
|
v-if="captionStyle.transDisplay"
|
||||||
|
:style="{
|
||||||
|
fontFamily: captionStyle.transFontFamily,
|
||||||
|
fontSize: captionStyle.transFontSize + 'px',
|
||||||
|
color: captionStyle.transFontColor,
|
||||||
|
fontWeight: captionStyle.transFontWeight * 100
|
||||||
|
}">
|
||||||
|
<span>{{ $t('example.translation') }}</span>
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="title-bar" :style="{color: captionStyle.fontColor}">
|
<div class="title-bar" :style="{color: captionStyle.fontColor}">
|
||||||
@@ -56,6 +83,14 @@ import { ref, onMounted } from 'vue';
|
|||||||
import { useCaptionStyleStore } from '@renderer/stores/captionStyle';
|
import { useCaptionStyleStore } from '@renderer/stores/captionStyle';
|
||||||
import { useCaptionLogStore } from '@renderer/stores/captionLog';
|
import { useCaptionLogStore } from '@renderer/stores/captionLog';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
|
|
||||||
|
const revArr = {
|
||||||
|
1: [1],
|
||||||
|
2: [2, 1],
|
||||||
|
3: [3, 2, 1],
|
||||||
|
4: [4, 3, 2, 1],
|
||||||
|
}
|
||||||
|
|
||||||
const captionStyle = useCaptionStyleStore();
|
const captionStyle = useCaptionStyleStore();
|
||||||
const captionLog = useCaptionLogStore();
|
const captionLog = useCaptionLogStore();
|
||||||
const { captionData } = storeToRefs(captionLog);
|
const { captionData } = storeToRefs(captionLog);
|
||||||
|
|||||||
Reference in New Issue
Block a user