diff --git a/src/api/query.ts b/src/api/query.ts index b61adfa..708925c 100644 --- a/src/api/query.ts +++ b/src/api/query.ts @@ -29,7 +29,7 @@ export async function QueryPostAPIWithParams(urlString: string, params?: any, } catch (e) { console.error(`[POST] API调用失败: ${e}`) failCount++ - if (failCount > 3 && !apiFail.value) { + if (!apiFail.value) { apiFail.value = true console.log('默认API异常, 切换至故障转移节点') } @@ -54,7 +54,7 @@ export async function QueryGetAPI(urlString: string, params?: any, headers?: } catch (e) { console.error(`[GET] API调用失败: ${e}`) failCount++ - if (failCount > 3 && !apiFail.value) { + if (!apiFail.value) { apiFail.value = true console.log('默认API异常, 切换至故障转移节点') } diff --git a/src/views/open_live/ReadDanmaku.vue b/src/views/open_live/ReadDanmaku.vue index 4c2e082..b98c934 100644 --- a/src/views/open_live/ReadDanmaku.vue +++ b/src/views/open_live/ReadDanmaku.vue @@ -59,6 +59,7 @@ type SpeechSettings = { voiceType: 'local' | 'api' voiceAPISchemeType: 'http' | 'https' voiceAPI?: string + splitText: boolean combineGiftDelay?: number } @@ -86,6 +87,7 @@ const settings = useStorage('Setting.Speech', { voiceType: 'local', voiceAPISchemeType: 'https', voiceAPI: 'voice.vtsuru.live/voice/bert-vits2?text={{text}}&id=1&format=mp3&streaming=true', + splitText: false, combineGiftDelay: 2, }) @@ -188,12 +190,11 @@ async function speak() { if (data.data.type == EventDataTypes.Gift && data.updateAt > Date.now() - (settings.value.combineGiftDelay ?? 0) * 1000) { return } - const text = getTextFromDanmaku(speakQueue.value.shift()?.data) + let text = getTextFromDanmaku(speakQueue.value.shift()?.data) if (text) { isSpeaking.value = true readedDanmaku.value++ speakingText.value = text - console.log(`[TTS] 正在朗读: ${text}`) if (checkTimer) { clearInterval(checkTimer) } @@ -204,10 +205,26 @@ async function speak() { if (settings.value.voiceType == 'local') { speakDirect(text) } else { + text = settings.value.splitText ? insertSpaces(text) : 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 function speakDirect(text: string) { try { @@ -357,7 +374,7 @@ function getTextFromDanmaku(data: EventModel | undefined) { break } 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.price.regex, data.price.toString()) .replace(templateConstants.message.regex, data.msg) @@ -372,9 +389,22 @@ function getTextFromDanmaku(data: EventModel | undefined) { } else if (data.type === EventDataTypes.Guard) { 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 } +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() { canSpeech.value = true message.success('服务已启动') @@ -441,7 +471,7 @@ function test(type: EventDataTypes) { type: EventDataTypes.SC, name: accountInfo.value?.name ?? '未知用户', uid: accountInfo.value?.biliId ?? 0, - msg: '测试sc', + msg: '测试SC', price: 30, num: 1, time: Date.now(), @@ -683,7 +713,7 @@ onUnmounted(() => { 看起来你正在使用本站提供的测试API (voice.vtsuru.live), 这个接口将会返回 Xz乔希 - 训练的 Taffy 模型结果, 不支持部分英文, 仅用于测试, 不保证可用性. 侵删 + 训练的 Taffy 模型结果, 不支持部分英文, 仅用于测试, 用的人多的时候会比较慢, 不保证可用性. 侵删
@@ -781,6 +811,19 @@ onUnmounted(() => { " /> + + 启用句子拆分 + + + 仅API方式可用, 为英文用户名用引号包裹起来, 并将所有大写单词拆分成单个单词, 以防止部分单词念不出来 +
+ 例: 原文: Megghy 说: UPPERCASE单词,word. +
+ 结果: 'Megghy' 说: U P P E R C A S E 单词,word. +
+