调整1.0鉴权音色方式
This commit is contained in:
@@ -40,7 +40,10 @@ export function speedToV3SpeechRate(speed) {
|
||||
return Math.round((normalizeSpeed(speed) - 1) * 100);
|
||||
}
|
||||
|
||||
export function inferResourceIdBySpeaker(value) {
|
||||
export function inferResourceIdBySpeaker(value, explicitResourceId = null) {
|
||||
if (explicitResourceId) {
|
||||
return explicitResourceId;
|
||||
}
|
||||
const v = (value || '').trim();
|
||||
const lower = v.toLowerCase();
|
||||
if (lower.startsWith('icl_') || lower.startsWith('s_')) {
|
||||
@@ -110,7 +113,7 @@ export async function speakSegmentAuth(messageId, segment, segmentIndex, batchId
|
||||
} = ctx;
|
||||
|
||||
const speaker = segment.resolvedSpeaker;
|
||||
const resourceId = inferResourceIdBySpeaker(speaker);
|
||||
const resourceId = segment.resolvedResourceId || inferResourceIdBySpeaker(speaker);
|
||||
const params = buildSynthesizeParams({ text: segment.text, speaker, resourceId }, config);
|
||||
const emotion = normalizeEmotion(segment.emotion);
|
||||
const contextTexts = resolveContextTexts(segment.context, resourceId);
|
||||
@@ -171,7 +174,7 @@ export async function speakSegmentAuth(messageId, segment, segmentIndex, batchId
|
||||
async function playWithStreaming(messageId, segment, segmentIndex, batchId, params, headers, ctx) {
|
||||
const { player, storeLocalCache, buildCacheKey, updateState } = ctx;
|
||||
const speaker = segment.resolvedSpeaker;
|
||||
const resourceId = inferResourceIdBySpeaker(speaker);
|
||||
const resourceId = params.resourceId;
|
||||
|
||||
const controller = new AbortController();
|
||||
const chunks = [];
|
||||
@@ -250,7 +253,7 @@ async function playWithStreaming(messageId, segment, segmentIndex, batchId, para
|
||||
async function playWithoutStreaming(messageId, segment, segmentIndex, batchId, params, headers, ctx) {
|
||||
const { player, storeLocalCache, buildCacheKey, updateState } = ctx;
|
||||
const speaker = segment.resolvedSpeaker;
|
||||
const resourceId = inferResourceIdBySpeaker(speaker);
|
||||
const resourceId = params.resourceId;
|
||||
|
||||
const result = await synthesizeV3(params, headers);
|
||||
updateState({ audioBlob: result.audioBlob, usage: result.usage, status: 'queued' });
|
||||
|
||||
@@ -1408,13 +1408,20 @@ select.input { cursor: pointer; }
|
||||
<label class="form-label">音色 ID</label>
|
||||
<input type="text" id="newVoiceId" class="input" placeholder="如 S_xxx">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">名称</label>
|
||||
<input type="text" id="newVoiceName" class="input" placeholder="显示名称">
|
||||
</div>
|
||||
<button class="btn btn-primary" id="addMySpeakerBtn" style="margin-top: 18px;"><i class="fa-solid fa-plus"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">名称</label>
|
||||
<input type="text" id="newVoiceName" class="input" placeholder="显示名称">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">复刻版本</label>
|
||||
<select id="newVoiceResourceId" class="input">
|
||||
<option value="seed-icl-2.0">复刻 2.0</option>
|
||||
<option value="seed-icl-1.0">复刻 1.0</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="btn btn-primary" id="addMySpeakerBtn" style="margin-top: 18px;"><i class="fa-solid fa-plus"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2149,14 +2156,15 @@ function renderAuthVoiceList() {
|
||||
// 数据处理
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
function normalizeMySpeakers(list) {
|
||||
if (!Array.isArray(list)) return [];
|
||||
return list.map(item => ({
|
||||
name: String(item?.name || '').trim(),
|
||||
value: String(item?.value || '').trim(),
|
||||
source: item?.source || getVoiceSource(item?.value || ''),
|
||||
})).filter(item => item.value);
|
||||
}
|
||||
function normalizeMySpeakers(list) {
|
||||
if (!Array.isArray(list)) return [];
|
||||
return list.map(item => ({
|
||||
name: String(item?.name || '').trim(),
|
||||
value: String(item?.value || '').trim(),
|
||||
source: item?.source || getVoiceSource(item?.value || ''),
|
||||
resourceId: item?.resourceId || null,
|
||||
})).filter(item => item.value);
|
||||
}
|
||||
|
||||
function applyCacheStats(stats = {}) {
|
||||
$('cacheCount').textContent = stats.count ?? 0;
|
||||
@@ -2265,13 +2273,16 @@ function doTestVoice(speaker, source, textElId, statusElId) {
|
||||
|
||||
setTestStatus(statusElId, 'playing', '正在合成...');
|
||||
|
||||
post('xb-tts:test-speak', {
|
||||
const speakerItem = mySpeakers.find(s => s.value === speaker);
|
||||
const resolvedResourceId = speakerItem?.resourceId;
|
||||
|
||||
post('xb-tts:test-speak', {
|
||||
text,
|
||||
speaker,
|
||||
source,
|
||||
resourceId: source === 'auth' ? inferResourceIdBySpeaker(speaker) : '',
|
||||
});
|
||||
}
|
||||
resourceId: source === 'auth' ? (resolvedResourceId || inferResourceIdBySpeaker(speaker)) : '',
|
||||
});
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// 消息处理
|
||||
@@ -2434,17 +2445,18 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
post('xb-tts:toast', { type: 'success', message: `已添加:${name}` });
|
||||
});
|
||||
|
||||
$('addMySpeakerBtn').addEventListener('click', () => {
|
||||
const id = $('newVoiceId').value.trim();
|
||||
const name = $('newVoiceName').value.trim();
|
||||
if (!id) { post('xb-tts:toast', { type: 'error', message: '请输入音色ID' }); return; }
|
||||
|
||||
if (!isInMyList(id)) {
|
||||
mySpeakers.push({ name: name || id, value: id, source: 'auth' });
|
||||
}
|
||||
selectedVoiceValue = id;
|
||||
$('newVoiceId').value = '';
|
||||
$('newVoiceName').value = '';
|
||||
$('addMySpeakerBtn').addEventListener('click', () => {
|
||||
const id = $('newVoiceId').value.trim();
|
||||
const name = $('newVoiceName').value.trim();
|
||||
const resourceId = $('newVoiceResourceId').value;
|
||||
if (!id) { post('xb-tts:toast', { type: 'error', message: '请输入音色ID' }); return; }
|
||||
|
||||
if (!isInMyList(id)) {
|
||||
mySpeakers.push({ name: name || id, value: id, source: 'auth', resourceId });
|
||||
}
|
||||
selectedVoiceValue = id;
|
||||
$('newVoiceId').value = '';
|
||||
$('newVoiceName').value = '';
|
||||
|
||||
renderMyVoiceList();
|
||||
updateCurrentVoiceDisplay();
|
||||
|
||||
@@ -250,47 +250,51 @@ function resolveSpeakerWithSource(speakerName, mySpeakers, defaultSpeaker) {
|
||||
});
|
||||
}
|
||||
|
||||
if (!speakerName) {
|
||||
const defaultItem = list.find(s => s.value === defaultSpeaker);
|
||||
return {
|
||||
value: defaultSpeaker,
|
||||
source: defaultItem?.source || getVoiceSource(defaultSpeaker)
|
||||
};
|
||||
}
|
||||
if (!speakerName) {
|
||||
const defaultItem = list.find(s => s.value === defaultSpeaker);
|
||||
return {
|
||||
value: defaultSpeaker,
|
||||
source: defaultItem?.source || getVoiceSource(defaultSpeaker),
|
||||
resourceId: defaultItem?.resourceId || null
|
||||
};
|
||||
}
|
||||
|
||||
const byName = list.find(s => s.name === speakerName);
|
||||
console.log('[TTS Debug] byName 查找结果:', byName); // ★ 调试
|
||||
|
||||
if (byName?.value) {
|
||||
return {
|
||||
value: byName.value,
|
||||
source: byName.source || getVoiceSource(byName.value)
|
||||
};
|
||||
}
|
||||
if (byName?.value) {
|
||||
return {
|
||||
value: byName.value,
|
||||
source: byName.source || getVoiceSource(byName.value),
|
||||
resourceId: byName.resourceId || null
|
||||
};
|
||||
}
|
||||
|
||||
const byValue = list.find(s => s.value === speakerName);
|
||||
console.log('[TTS Debug] byValue 查找结果:', byValue); // ★ 调试
|
||||
|
||||
if (byValue?.value) {
|
||||
return {
|
||||
value: byValue.value,
|
||||
source: byValue.source || getVoiceSource(byValue.value)
|
||||
};
|
||||
}
|
||||
if (byValue?.value) {
|
||||
return {
|
||||
value: byValue.value,
|
||||
source: byValue.source || getVoiceSource(byValue.value),
|
||||
resourceId: byValue.resourceId || null
|
||||
};
|
||||
}
|
||||
|
||||
if (FREE_VOICE_KEYS.has(speakerName)) {
|
||||
return { value: speakerName, source: 'free' };
|
||||
}
|
||||
if (FREE_VOICE_KEYS.has(speakerName)) {
|
||||
return { value: speakerName, source: 'free', resourceId: null };
|
||||
}
|
||||
|
||||
// ★ 回退到默认,这是问题发生的地方
|
||||
console.warn('[TTS Debug] 未找到匹配音色,回退到默认:', defaultSpeaker);
|
||||
|
||||
const defaultItem = list.find(s => s.value === defaultSpeaker);
|
||||
return {
|
||||
value: defaultSpeaker,
|
||||
source: defaultItem?.source || getVoiceSource(defaultSpeaker)
|
||||
};
|
||||
}
|
||||
const defaultItem = list.find(s => s.value === defaultSpeaker);
|
||||
return {
|
||||
value: defaultSpeaker,
|
||||
source: defaultItem?.source || getVoiceSource(defaultSpeaker),
|
||||
resourceId: defaultItem?.resourceId || null
|
||||
};
|
||||
}
|
||||
|
||||
// ============ 缓存管理 ============
|
||||
|
||||
@@ -616,16 +620,17 @@ async function speakMessage(messageId, { mode = 'manual' } = {}) {
|
||||
return;
|
||||
}
|
||||
|
||||
const resolvedSegments = segments.map(seg => {
|
||||
const resolved = seg.speaker
|
||||
? resolveSpeakerWithSource(seg.speaker, mySpeakers, defaultSpeaker)
|
||||
: defaultResolved;
|
||||
return {
|
||||
...seg,
|
||||
resolvedSpeaker: resolved.value,
|
||||
resolvedSource: resolved.source
|
||||
};
|
||||
});
|
||||
const resolvedSegments = segments.map(seg => {
|
||||
const resolved = seg.speaker
|
||||
? resolveSpeakerWithSource(seg.speaker, mySpeakers, defaultSpeaker)
|
||||
: defaultResolved;
|
||||
return {
|
||||
...seg,
|
||||
resolvedSpeaker: resolved.value,
|
||||
resolvedSource: resolved.source,
|
||||
resolvedResourceId: resolved.resourceId
|
||||
};
|
||||
});
|
||||
|
||||
const needsAuth = resolvedSegments.some(s => s.resolvedSource === 'auth');
|
||||
if (needsAuth && !isAuthConfigured()) {
|
||||
@@ -1325,7 +1330,7 @@ export async function initTts() {
|
||||
return;
|
||||
}
|
||||
|
||||
const resourceId = options.resourceId || inferResourceIdBySpeaker(resolved.value);
|
||||
const resourceId = options.resourceId || resolved.resourceId || inferResourceIdBySpeaker(resolved.value);
|
||||
const result = await synthesizeV3({
|
||||
appId: config.volc.appId,
|
||||
accessKey: config.volc.accessKey,
|
||||
|
||||
Reference in New Issue
Block a user