Update TTS modal to support 20 Qwen3-TTS voices

- Fix API response format parsing (data.data[].voices -> flat array)
- Update fallback voices from 6 to 20
- Bump cache version to v3 to force refresh
This commit is contained in:
russell@unturf.com 2026-01-26 17:25:20 -05:00
parent 3d1c06c1c1
commit 15e7e4c7c7

View file

@ -11,7 +11,7 @@ async function populateVoiceDropdown(voiceSelection) {
const cacheTimeKey = 'uncloseai_voices_cache_time';
const cacheVersionKey = 'uncloseai_voices_cache_version';
const cacheExpiration = 60000; // 1 minute in milliseconds
const currentVersion = '2'; // Increment this when voice models change
const currentVersion = '3'; // Increment this when voice models change (v3: 20 voices)
try {
const cachedTime = localStorage.getItem(cacheTimeKey);
@ -49,27 +49,62 @@ async function populateVoiceDropdown(voiceSelection) {
const data = await response.json();
// Transform API response format to frontend format
// API returns: {data: [{id: "tts-1-qwen", voices: ["alloy", ...]}]}
// Frontend expects: [{model: "tts-1-qwen", voice: "alloy"}, ...]
let voices = [];
if (data.data && Array.isArray(data.data)) {
data.data.forEach(modelInfo => {
if (modelInfo.voices && Array.isArray(modelInfo.voices)) {
modelInfo.voices.forEach(voice => {
voices.push({ model: modelInfo.id, voice: voice });
});
}
});
} else if (data.voices && Array.isArray(data.voices)) {
// Legacy format support
voices = data.voices;
}
console.log(`Loaded ${voices.length} voices from API`);
// Cache the response with version
try {
localStorage.setItem(cacheKey, JSON.stringify(data.voices));
localStorage.setItem(cacheKey, JSON.stringify(voices));
localStorage.setItem(cacheTimeKey, Date.now().toString());
localStorage.setItem(cacheVersionKey, currentVersion);
} catch (error) {
console.warn('Cache write failed:', error);
}
renderVoiceOptions(voiceSelection, data.voices);
return data.voices;
renderVoiceOptions(voiceSelection, voices);
return voices;
} catch (error) {
console.error('Failed to fetch voices:', error);
// Fallback to default voices
// Fallback to default voices (20 voices from Qwen3-TTS)
const defaultVoices = [
{ model: 'tts-1', voice: 'alloy' },
{ model: 'tts-1', voice: 'echo' },
{ model: 'tts-1', voice: 'fable' },
{ model: 'tts-1', voice: 'onyx' },
{ model: 'tts-1', voice: 'nova' },
{ model: 'tts-1', voice: 'shimmer' }
// Standard OpenAI-compatible voices
{ model: 'tts-1-qwen', voice: 'alloy' },
{ model: 'tts-1-qwen', voice: 'echo' },
{ model: 'tts-1-qwen', voice: 'fable' },
{ model: 'tts-1-qwen', voice: 'onyx' },
{ model: 'tts-1-qwen', voice: 'nova' },
{ model: 'tts-1-qwen', voice: 'shimmer' },
// Extended voices
{ model: 'tts-1-qwen', voice: 'amber' },
{ model: 'tts-1-qwen', voice: 'breeze' },
{ model: 'tts-1-qwen', voice: 'coral' },
{ model: 'tts-1-qwen', voice: 'dawn' },
{ model: 'tts-1-qwen', voice: 'ember' },
{ model: 'tts-1-qwen', voice: 'frost' },
{ model: 'tts-1-qwen', voice: 'glow' },
{ model: 'tts-1-qwen', voice: 'haze' },
{ model: 'tts-1-qwen', voice: 'ivy' },
{ model: 'tts-1-qwen', voice: 'jade' },
{ model: 'tts-1-qwen', voice: 'kite' },
{ model: 'tts-1-qwen', voice: 'lark' },
{ model: 'tts-1-qwen', voice: 'mist' },
{ model: 'tts-1-qwen', voice: 'nectar' }
];
renderVoiceOptions(voiceSelection, defaultVoices);
return defaultVoices;