improve readdanmaku

This commit is contained in:
2023-12-28 23:15:16 +08:00
parent 0baa458e89
commit 0c36a9eec1
2 changed files with 51 additions and 8 deletions

View File

@@ -29,7 +29,7 @@ export async function QueryPostAPIWithParams<T>(urlString: string, params?: any,
} catch (e) { } catch (e) {
console.error(`[POST] API调用失败: ${e}`) console.error(`[POST] API调用失败: ${e}`)
failCount++ failCount++
if (failCount > 3 && !apiFail.value) { if (!apiFail.value) {
apiFail.value = true apiFail.value = true
console.log('默认API异常, 切换至故障转移节点') console.log('默认API异常, 切换至故障转移节点')
} }
@@ -54,7 +54,7 @@ export async function QueryGetAPI<T>(urlString: string, params?: any, headers?:
} catch (e) { } catch (e) {
console.error(`[GET] API调用失败: ${e}`) console.error(`[GET] API调用失败: ${e}`)
failCount++ failCount++
if (failCount > 3 && !apiFail.value) { if (!apiFail.value) {
apiFail.value = true apiFail.value = true
console.log('默认API异常, 切换至故障转移节点') console.log('默认API异常, 切换至故障转移节点')
} }

View File

@@ -59,6 +59,7 @@ type SpeechSettings = {
voiceType: 'local' | 'api' voiceType: 'local' | 'api'
voiceAPISchemeType: 'http' | 'https' voiceAPISchemeType: 'http' | 'https'
voiceAPI?: string voiceAPI?: string
splitText: boolean
combineGiftDelay?: number combineGiftDelay?: number
} }
@@ -86,6 +87,7 @@ const settings = useStorage<SpeechSettings>('Setting.Speech', {
voiceType: 'local', voiceType: 'local',
voiceAPISchemeType: 'https', voiceAPISchemeType: 'https',
voiceAPI: 'voice.vtsuru.live/voice/bert-vits2?text={{text}}&id=1&format=mp3&streaming=true', voiceAPI: 'voice.vtsuru.live/voice/bert-vits2?text={{text}}&id=1&format=mp3&streaming=true',
splitText: false,
combineGiftDelay: 2, combineGiftDelay: 2,
}) })
@@ -188,12 +190,11 @@ async function speak() {
if (data.data.type == EventDataTypes.Gift && data.updateAt > Date.now() - (settings.value.combineGiftDelay ?? 0) * 1000) { if (data.data.type == EventDataTypes.Gift && data.updateAt > Date.now() - (settings.value.combineGiftDelay ?? 0) * 1000) {
return return
} }
const text = getTextFromDanmaku(speakQueue.value.shift()?.data) let text = getTextFromDanmaku(speakQueue.value.shift()?.data)
if (text) { if (text) {
isSpeaking.value = true isSpeaking.value = true
readedDanmaku.value++ readedDanmaku.value++
speakingText.value = text speakingText.value = text
console.log(`[TTS] 正在朗读: ${text}`)
if (checkTimer) { if (checkTimer) {
clearInterval(checkTimer) clearInterval(checkTimer)
} }
@@ -204,10 +205,26 @@ async function speak() {
if (settings.value.voiceType == 'local') { if (settings.value.voiceType == 'local') {
speakDirect(text) speakDirect(text)
} else { } else {
text = settings.value.splitText ? insertSpaces(text) : text
speakFromAPI(text) speakFromAPI(text)
} }
console.log(`[TTS] 正在朗读: ${text}`)
} }
} }
function insertSpaces(sentence: string) {
// First, insert spaces around English words and numbers
//sentence = sentence.replace(/([a-zA-Z]+)/g, "'$1'")
// Then, split all-caps words into single letters, each surrounded by spaces
sentence = sentence.replace(/\b([A-Z]{2,})\b/g, function (match) {
return match.split('').join(' ')
})
// Clean up any extra spaces that may have been added
sentence = sentence.replace(/\s+/g, ' ').trim()
return sentence
}
let checkTimer: number | undefined let checkTimer: number | undefined
function speakDirect(text: string) { function speakDirect(text: string) {
try { try {
@@ -357,7 +374,7 @@ function getTextFromDanmaku(data: EventModel | undefined) {
break break
} }
text = text text = text
.replace(templateConstants.name.regex, data.name) .replace(templateConstants.name.regex, settings.value.voiceType == 'api' && settings.value.splitText ? `\'${data.name}\'` : data.name)
.replace(templateConstants.count.regex, data.num.toString()) .replace(templateConstants.count.regex, data.num.toString())
.replace(templateConstants.price.regex, data.price.toString()) .replace(templateConstants.price.regex, data.price.toString())
.replace(templateConstants.message.regex, data.msg) .replace(templateConstants.message.regex, data.msg)
@@ -372,9 +389,22 @@ function getTextFromDanmaku(data: EventModel | undefined) {
} else if (data.type === EventDataTypes.Guard) { } else if (data.type === EventDataTypes.Guard) {
text = text.replace(templateConstants.guard_num.regex, data.num.toString()) text = text.replace(templateConstants.guard_num.regex, data.num.toString())
} }
text = text.replace(/[^0-9a-z\u4E00-\u9FFF\u3400-\u4DBF\uF900-\uFAFF ]/gi, '').normalize('NFKC') //过滤无效字符, 全角转半角 text = fullWidthToHalfWidth(text)
.replace(/[^0-9a-zA-Z\u4E00-\u9FFF\u3400-\u4DBF\uF900-\uFAFF ,.:'"\s]/gi, '')
.normalize('NFKC') //过滤无效字符, 全角转半角
return text return text
} }
function fullWidthToHalfWidth(str: string) {
// Convert full-width characters to half-width ones
var result = str.replace(/[\uff01-\uff5e]/g, function (ch) {
return String.fromCharCode(ch.charCodeAt(0) - 0xfee0)
})
// Convert full-width space (u3000) to half-width one
result = result.replace(/\u3000/g, ' ')
return result
}
function startSpeech() { function startSpeech() {
canSpeech.value = true canSpeech.value = true
message.success('服务已启动') message.success('服务已启动')
@@ -441,7 +471,7 @@ function test(type: EventDataTypes) {
type: EventDataTypes.SC, type: EventDataTypes.SC,
name: accountInfo.value?.name ?? '未知用户', name: accountInfo.value?.name ?? '未知用户',
uid: accountInfo.value?.biliId ?? 0, uid: accountInfo.value?.biliId ?? 0,
msg: '测试sc', msg: '测试SC',
price: 30, price: 30,
num: 1, num: 1,
time: Date.now(), time: Date.now(),
@@ -683,7 +713,7 @@ onUnmounted(() => {
<NAlert v-if="isVtsuruVoiceAPI" type="success" closable> <NAlert v-if="isVtsuruVoiceAPI" type="success" closable>
看起来你正在使用本站提供的测试API (voice.vtsuru.live), 这个接口将会返回 看起来你正在使用本站提供的测试API (voice.vtsuru.live), 这个接口将会返回
<NButton text type="info" tag="a" href="https://space.bilibili.com/5859321" target="_blank"> Xz乔希 </NButton> <NButton text type="info" tag="a" href="https://space.bilibili.com/5859321" target="_blank"> Xz乔希 </NButton>
训练的 Taffy 模型结果, 不支持部分英文, 仅用于测试, 不保证可用性. 侵删 训练的 Taffy 模型结果, 不支持部分英文, 仅用于测试, 用的人多的时候会比较慢, 不保证可用性. 侵删
</NAlert> </NAlert>
</NSpace> </NSpace>
<br /> <br />
@@ -781,6 +811,19 @@ onUnmounted(() => {
" "
/> />
</NInputGroup> </NInputGroup>
<NCheckbox v-model:checked="settings.splitText">
启用句子拆分
<NTooltip>
<template #trigger>
<NIcon :component="Info24Filled" />
</template>
仅API方式可用, 为英文用户名用引号包裹起来, 并将所有大写单词拆分成单个单词, 以防止部分单词念不出来
<br />
: 原文: Megghy : UPPERCASE单词,word.
<br />
结果: 'Megghy' : U P P E R C A S E 单词,word.
</NTooltip>
</NCheckbox>
</NSpace> </NSpace>
</template> </template>
</template> </template>