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

View file

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

View file

@ -508,13 +508,14 @@ class MediaApiV2 {
final json = jsonDecode(body) as Map<String, dynamic>; final json = jsonDecode(body) as Map<String, dynamic>;
final text = json['text'] as String?; final text = json['text'] as String?;
final voice = json['voice'] as String?; final voice = json['voice'] as String?;
final language = json['language'] as String?;
if (text == null || text.trim().isEmpty) { if (text == null || text.trim().isEmpty) {
return _badRequest('Text is required'); return _badRequest('Text is required');
} }
print( 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 // Call voice-service
@ -524,6 +525,7 @@ class MediaApiV2 {
'text': text, 'text': text,
if (voice != null) 'voice': voice, if (voice != null) 'voice': voice,
'format': 'mp3', 'format': 'mp3',
if (language != null) 'language': language,
}, },
options: dio.Options( options: dio.Options(
responseType: dio.ResponseType.bytes, responseType: dio.ResponseType.bytes,