lang
Some checks are pending
Backend CI / test (push) Waiting to run
Backend CI / build (push) Blocked by required conditions
Deploy Admin Panel / Deploy Admin Panel (push) Waiting to run
Deploy Admin Panel / Admin Panel Verification (push) Blocked by required conditions
Deploy Mnemo Cards / Deploy Backend (push) Waiting to run
Deploy Mnemo Cards / Deploy Web App (push) Blocked by required conditions
Deploy Mnemo Cards / Final Verification (push) Blocked by required conditions

This commit is contained in:
Dmitry 2026-01-24 19:59:04 +03:00
parent ac92935b78
commit 03e1e0b478
3 changed files with 14 additions and 9 deletions

View file

@ -11,17 +11,20 @@ export const voiceServiceApi = {
* Backend calls voice-service, which handles caching, and saves to MinIO
* @param text Text to convert to speech
* @param voice Voice identifier (optional, defaults to 'alloy')
* @param language Language code (optional)
* @returns Object ID and presigned URL
*/
async generateTTS(
text: string,
voice?: string
voice?: string,
language?: string
): Promise<GenerateTTSResponse> {
const response = await adminApiClient.post<GenerateTTSResponse>(
'/api/v2/media/generate-tts',
{
text,
voice: voice || 'alloy',
...(language && { language }),
}
)

View file

@ -24,7 +24,7 @@ export function CardVoicesManager({
const queryClient = useQueryClient()
const [showAddForm, setShowAddForm] = useState(false)
const [newAudio, setNewAudio] = useState<string | undefined>(undefined)
const [newLanguage, setNewLanguage] = useState('en')
const [newLanguage, setNewLanguage] = useState('es')
const [isGeneratingTTS, setIsGeneratingTTS] = useState(false)
// Load voices for the card
@ -65,7 +65,7 @@ export function CardVoicesManager({
// Auto-close form and reset after successful addition
setShowAddForm(false)
setNewAudio(undefined)
setNewLanguage('en')
setNewLanguage('es')
},
onError: (error: unknown) => {
console.error('❌ CardVoicesManager: addVoiceMutation.onError', error)
@ -111,14 +111,14 @@ export function CardVoicesManager({
try {
const text = cardOriginal.trim()
// Use default voice 'alloy' for TTS generation
// Language (newLanguage) is used only when saving the voice to the card
// Language (newLanguage) is used for both TTS generation and saving the voice to the card
const voice = 'alloy'
console.log('🔄 CardVoicesManager: Requesting TTS generation', { text, voice })
console.log('🔄 CardVoicesManager: Requesting TTS generation', { text, voice, language: newLanguage })
// Generate TTS via backend API
// Backend calls voice-service (which handles caching) and saves to MinIO
const ttsResponse = await voiceServiceApi.generateTTS(text, voice)
const ttsResponse = await voiceServiceApi.generateTTS(text, voice, newLanguage)
console.log('✅ CardVoicesManager: TTS generated and saved to MinIO', {
objectId: ttsResponse.objectId
})
@ -126,7 +126,7 @@ export function CardVoicesManager({
// Add voice to card using the objectId from backend
await addVoiceMutation.mutateAsync({
voiceUrl: ttsResponse.objectId,
language: newLanguage || 'en',
language: newLanguage || 'es',
})
} catch (error) {
console.error('❌ CardVoicesManager: TTS generation failed', error)
@ -224,7 +224,7 @@ export function CardVoicesManager({
onClick={() => {
setShowAddForm(false)
setNewAudio(undefined)
setNewLanguage('en')
setNewLanguage('es')
}}
disabled={disabled || addVoiceMutation.isPending}
>

View file

@ -508,13 +508,14 @@ class MediaApiV2 {
final json = jsonDecode(body) as Map<String, dynamic>;
final text = json['text'] as String?;
final voice = json['voice'] as String?;
final language = json['language'] as String?;
if (text == null || text.trim().isEmpty) {
return _badRequest('Text is required');
}
print(
'🔄 MediaApiV2: Requesting TTS from voice-service: text="${text.substring(0, text.length > 50 ? 50 : text.length)}", voice="${voice ?? 'default'}"',
'🔄 MediaApiV2: Requesting TTS from voice-service: text="${text.substring(0, text.length > 50 ? 50 : text.length)}", voice="${voice ?? 'default'}", language="${language ?? 'not provided'}"',
);
// Call voice-service
@ -524,6 +525,7 @@ class MediaApiV2 {
'text': text,
if (voice != null) 'voice': voice,
'format': 'mp3',
if (language != null) 'language': language,
},
options: dio.Options(
responseType: dio.ResponseType.bytes,