mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-27 09:33:55 +08:00
补充
This commit is contained in:
+2
@@ -150,6 +150,8 @@ public class TimbreServiceImpl extends BaseServiceImpl<TimbreDao, TimbreEntity>
|
|||||||
VoiceDTO voiceDTO = new VoiceDTO();
|
VoiceDTO voiceDTO = new VoiceDTO();
|
||||||
voiceDTO.setId(entity.getId());
|
voiceDTO.setId(entity.getId());
|
||||||
voiceDTO.setName(MessageUtils.getMessage(ErrorCode.VOICE_CLONE_PREFIX) + entity.getName());
|
voiceDTO.setName(MessageUtils.getMessage(ErrorCode.VOICE_CLONE_PREFIX) + entity.getName());
|
||||||
|
// 保留从数据库查询到的voiceDemo字段
|
||||||
|
voiceDTO.setVoiceDemo(entity.getVoiceDemo());
|
||||||
redisUtils.set(RedisKeys.getTimbreNameById(voiceDTO.getId()), voiceDTO.getName(),
|
redisUtils.set(RedisKeys.getTimbreNameById(voiceDTO.getId()), voiceDTO.getName(),
|
||||||
RedisUtils.NOT_EXPIRE);
|
RedisUtils.NOT_EXPIRE);
|
||||||
voiceDTOs.add(0, voiceDTO);
|
voiceDTOs.add(0, voiceDTO);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<mapper namespace="xiaozhi.modules.voiceclone.dao.VoiceCloneDao">
|
<mapper namespace="xiaozhi.modules.voiceclone.dao.VoiceCloneDao">
|
||||||
<select id="getTrainSuccess" resultType="xiaozhi.modules.model.dto.VoiceDTO">
|
<select id="getTrainSuccess" resultType="xiaozhi.modules.model.dto.VoiceDTO">
|
||||||
select id, name
|
select id, name, voice_id as voiceDemo
|
||||||
from ai_voice_clone
|
from ai_voice_clone
|
||||||
where model_id = #{modelId} and user_id = #{userId} and train_status = 2
|
where model_id = #{modelId} and user_id = #{userId} and train_status = 2
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
@@ -221,25 +221,37 @@
|
|||||||
class="form-select"
|
class="form-select"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="(item, index) in voiceOptions"
|
v-for="(item, index) in voiceOptions"
|
||||||
:key="`voice-${index}`"
|
:key="`voice-${index}`"
|
||||||
:label="item.label"
|
:label="item.label"
|
||||||
:value="item.value"
|
:value="item.value"
|
||||||
>
|
>
|
||||||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
<div
|
||||||
<span>{{ item.label }}</span>
|
style="
|
||||||
<template v-if="hasAudioPreview(item)">
|
display: flex;
|
||||||
<el-button
|
justify-content: space-between;
|
||||||
type="text"
|
align-items: center;
|
||||||
icon="el-icon-video-play"
|
"
|
||||||
size="small"
|
>
|
||||||
@click.stop="playVoicePreview(item.value)"
|
<span>{{ item.label }}</span>
|
||||||
:loading="playingVoice && form.ttsVoiceId === item.value"
|
<template v-if="hasAudioPreview(item)">
|
||||||
class="play-button"
|
<el-button
|
||||||
/>
|
type="text"
|
||||||
</template>
|
:icon="
|
||||||
</div>
|
playingVoice &&
|
||||||
</el-option>
|
currentPlayingVoiceId === item.value &&
|
||||||
|
!isPaused
|
||||||
|
? 'el-icon-video-pause'
|
||||||
|
: 'el-icon-video-play'
|
||||||
|
"
|
||||||
|
size="small"
|
||||||
|
@click.stop="toggleAudioPlayback(item.value)"
|
||||||
|
:loading="false"
|
||||||
|
class="play-button"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
@@ -321,7 +333,9 @@ export default {
|
|||||||
allFunctions: [],
|
allFunctions: [],
|
||||||
originalFunctions: [],
|
originalFunctions: [],
|
||||||
playingVoice: false,
|
playingVoice: false,
|
||||||
|
isPaused: false,
|
||||||
currentAudio: null,
|
currentAudio: null,
|
||||||
|
currentPlayingVoiceId: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -555,9 +569,16 @@ export default {
|
|||||||
this.voiceOptions = data.data.map((voice) => ({
|
this.voiceOptions = data.data.map((voice) => ({
|
||||||
value: voice.id,
|
value: voice.id,
|
||||||
label: voice.name,
|
label: voice.name,
|
||||||
|
// 复制音频相关字段,确保hasAudioPreview能检测到
|
||||||
|
voiceDemo: voice.voiceDemo,
|
||||||
|
demoUrl: voice.demoUrl,
|
||||||
|
audioUrl: voice.audioUrl,
|
||||||
|
voice_demo: voice.voice_demo,
|
||||||
|
sample_voice: voice.sample_voice,
|
||||||
|
referenceAudio: voice.referenceAudio,
|
||||||
}));
|
}));
|
||||||
// 保存完整的音色信息,添加调试信息
|
// 保存完整的音色信息,添加调试信息
|
||||||
console.log('获取到的音色数据:', data.data);
|
console.log("获取到的音色数据:", data.data);
|
||||||
this.voiceDetails = data.data.reduce((acc, voice) => {
|
this.voiceDetails = data.data.reduce((acc, voice) => {
|
||||||
acc[voice.id] = voice;
|
acc[voice.id] = voice;
|
||||||
return acc;
|
return acc;
|
||||||
@@ -676,17 +697,54 @@ export default {
|
|||||||
},
|
},
|
||||||
// 检查是否有音频预览
|
// 检查是否有音频预览
|
||||||
hasAudioPreview(item) {
|
hasAudioPreview(item) {
|
||||||
// 检查item中是否包含音频相关字段
|
// 检查item中是否包含有效的音频URL字段,且URL必须以http开头
|
||||||
const hasAudioFields = item.voiceDemo || item.demoUrl || item.audioUrl ||
|
const audioFields = [
|
||||||
item.voice_demo || item.sample_voice || item.referenceAudio;
|
item.voiceDemo,
|
||||||
return hasAudioFields && typeof hasAudioFields === 'string' && hasAudioFields.trim() !== '';
|
item.demoUrl,
|
||||||
|
item.audioUrl,
|
||||||
|
item.voice_demo,
|
||||||
|
item.sample_voice,
|
||||||
|
item.referenceAudio
|
||||||
|
];
|
||||||
|
|
||||||
|
// 检查是否有任何音频字段是以http开头的有效URL
|
||||||
|
return audioFields.some(field =>
|
||||||
|
field !== undefined &&
|
||||||
|
field !== null &&
|
||||||
|
typeof field === "string" &&
|
||||||
|
field.trim() !== "" &&
|
||||||
|
field.toLowerCase().startsWith("http")
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 播放/暂停音频切换
|
||||||
|
toggleAudioPlayback(voiceId) {
|
||||||
|
// 如果点击的是当前正在播放的音频,则切换暂停/播放状态
|
||||||
|
if (this.playingVoice && this.currentPlayingVoiceId === voiceId) {
|
||||||
|
if (this.isPaused) {
|
||||||
|
// 从暂停状态恢复播放
|
||||||
|
this.currentAudio.play().catch((error) => {
|
||||||
|
console.error("恢复播放失败:", error);
|
||||||
|
this.$message.warning("无法恢复播放音频");
|
||||||
|
});
|
||||||
|
this.isPaused = false;
|
||||||
|
} else {
|
||||||
|
// 暂停播放
|
||||||
|
this.currentAudio.pause();
|
||||||
|
this.isPaused = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 否则开始播放新的音频
|
||||||
|
this.playVoicePreview(voiceId);
|
||||||
|
},
|
||||||
|
|
||||||
// 播放音色预览
|
// 播放音色预览
|
||||||
playVoicePreview(voiceId = null) {
|
playVoicePreview(voiceId = null) {
|
||||||
// 如果传入了voiceId,则使用传入的,否则使用当前选中的
|
// 如果传入了voiceId,则使用传入的,否则使用当前选中的
|
||||||
const targetVoiceId = voiceId || this.form.ttsVoiceId;
|
const targetVoiceId = voiceId || this.form.ttsVoiceId;
|
||||||
|
|
||||||
if (!targetVoiceId) {
|
if (!targetVoiceId) {
|
||||||
this.$message.warning("请先选择一个音色");
|
this.$message.warning("请先选择一个音色");
|
||||||
return;
|
return;
|
||||||
@@ -698,34 +756,41 @@ export default {
|
|||||||
this.currentAudio = null;
|
this.currentAudio = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 重置播放状态
|
||||||
|
this.isPaused = false;
|
||||||
|
this.currentPlayingVoiceId = targetVoiceId;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 从保存的音色详情中获取音频URL
|
// 从保存的音色详情中获取音频URL
|
||||||
const voiceDetail = this.voiceDetails[targetVoiceId];
|
const voiceDetail = this.voiceDetails[targetVoiceId];
|
||||||
|
|
||||||
// 添加调试信息
|
// 添加调试信息
|
||||||
console.log('当前选择的音色ID:', targetVoiceId);
|
console.log("当前选择的音色ID:", targetVoiceId);
|
||||||
console.log('音色详情:', voiceDetail);
|
console.log("音色详情:", voiceDetail);
|
||||||
|
|
||||||
// 尝试多种可能的音频属性名
|
// 尝试多种可能的音频属性名
|
||||||
let audioUrl = null;
|
let audioUrl = null;
|
||||||
if (voiceDetail) {
|
if (voiceDetail) {
|
||||||
// 首先尝试直接从voiceDetail中获取各种可能的音频字段
|
// 首先尝试直接从voiceDetail中获取各种可能的音频字段
|
||||||
audioUrl = voiceDetail.voiceDemo ||
|
audioUrl =
|
||||||
voiceDetail.demoUrl ||
|
voiceDetail.voiceDemo ||
|
||||||
voiceDetail.audioUrl ||
|
voiceDetail.demoUrl ||
|
||||||
voiceDetail.voice_demo ||
|
voiceDetail.audioUrl ||
|
||||||
voiceDetail.sample_voice;
|
voiceDetail.voice_demo ||
|
||||||
|
voiceDetail.sample_voice;
|
||||||
|
|
||||||
// 如果没有找到,尝试检查是否有URL格式的字段
|
// 如果没有找到,尝试检查是否有URL格式的字段
|
||||||
if (!audioUrl) {
|
if (!audioUrl) {
|
||||||
for (const key in voiceDetail) {
|
for (const key in voiceDetail) {
|
||||||
const value = voiceDetail[key];
|
const value = voiceDetail[key];
|
||||||
if (typeof value === 'string' &&
|
if (
|
||||||
(value.startsWith('http://') ||
|
typeof value === "string" &&
|
||||||
value.startsWith('https://') ||
|
(value.startsWith("http://") ||
|
||||||
value.endsWith('.mp3') ||
|
value.startsWith("https://") ||
|
||||||
value.endsWith('.wav') ||
|
value.endsWith(".mp3") ||
|
||||||
value.endsWith('.ogg'))) {
|
value.endsWith(".wav") ||
|
||||||
|
value.endsWith(".ogg"))
|
||||||
|
) {
|
||||||
audioUrl = value;
|
audioUrl = value;
|
||||||
console.log(`发现可能的音频URL在字段 '${key}':`, audioUrl);
|
console.log(`发现可能的音频URL在字段 '${key}':`, audioUrl);
|
||||||
break;
|
break;
|
||||||
@@ -733,7 +798,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!audioUrl) {
|
if (!audioUrl) {
|
||||||
// 如果没有音频URL,显示友好的提示
|
// 如果没有音频URL,显示友好的提示
|
||||||
this.$message.warning("该音色暂无可预览的音频");
|
this.$message.warning("该音色暂无可预览的音频");
|
||||||
@@ -742,7 +807,7 @@ export default {
|
|||||||
|
|
||||||
// 设置播放状态
|
// 设置播放状态
|
||||||
this.playingVoice = true;
|
this.playingVoice = true;
|
||||||
|
|
||||||
// 创建并播放音频
|
// 创建并播放音频
|
||||||
this.currentAudio = new Audio(audioUrl);
|
this.currentAudio = new Audio(audioUrl);
|
||||||
|
|
||||||
@@ -760,7 +825,7 @@ export default {
|
|||||||
// 监听播放错误
|
// 监听播放错误
|
||||||
this.currentAudio.onerror = () => {
|
this.currentAudio.onerror = () => {
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
console.error('音频播放错误');
|
console.error("音频播放错误");
|
||||||
this.$message.warning("音频播放失败");
|
this.$message.warning("音频播放失败");
|
||||||
this.playingVoice = false;
|
this.playingVoice = false;
|
||||||
};
|
};
|
||||||
@@ -778,7 +843,7 @@ export default {
|
|||||||
// 实际调用play方法开始播放
|
// 实际调用play方法开始播放
|
||||||
this.currentAudio.play().catch((error) => {
|
this.currentAudio.play().catch((error) => {
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
console.error('播放失败:', error);
|
console.error("播放失败:", error);
|
||||||
this.$message.warning("无法播放音频");
|
this.$message.warning("无法播放音频");
|
||||||
this.playingVoice = false;
|
this.playingVoice = false;
|
||||||
});
|
});
|
||||||
@@ -952,7 +1017,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.play-button {
|
.play-button {
|
||||||
color: #409EFF;
|
color: #409eff;
|
||||||
transition: color 0.3s;
|
transition: color 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user