From 5e205d52cdb9b4034fbc09e1702b9f01fc9414ba Mon Sep 17 00:00:00 2001 From: ajaysi Date: Wed, 22 Apr 2026 09:58:20 +0530 Subject: [PATCH] fix: add comprehensive logging for voice clone debugging - Backend: change logger.info to logger.warning for production logs - Frontend: add console logs in brandAssets.ts createVoiceClone - Frontend: add token auth and audio error logs in VoiceAvatarPlaceholder - Log token fetching, authenticated URL, audio duration --- .../onboarding_utils/step4_asset_routes.py | 28 +++++++++---------- frontend/src/api/brandAssets.ts | 6 +++- .../components/VoiceAvatarPlaceholder.tsx | 23 ++++++++++++--- 3 files changed, 38 insertions(+), 19 deletions(-) diff --git a/backend/api/onboarding_utils/step4_asset_routes.py b/backend/api/onboarding_utils/step4_asset_routes.py index 6e83f820..a99d4f5c 100644 --- a/backend/api/onboarding_utils/step4_asset_routes.py +++ b/backend/api/onboarding_utils/step4_asset_routes.py @@ -87,7 +87,7 @@ async def get_latest_avatar( try: user_id = _extract_user_id(current_user) - logger.info(f"[latest-avatar] Looking for avatar for user_id: {user_id}") + logger.warning(f"[latest-avatar] Looking for avatar for user_id: {user_id}") # Search for assets that are either: # 1. Saved with source_module=BRAND_AVATAR_GENERATOR (new) @@ -103,7 +103,7 @@ async def get_latest_avatar( ]) ).order_by(desc(ContentAsset.created_at)).limit(50).all() - logger.info(f"[latest-avatar] Found {len(candidates)} candidate(s)") + logger.warning(f"[latest-avatar] Found {len(candidates)} candidate(s)") asset = None for candidate in candidates: @@ -185,7 +185,7 @@ async def generate_avatar( try: user_id = _extract_user_id(current_user) - logger.info(f"Generating avatar for user {user_id} with prompt: {request.prompt}") + logger.warning(f"Generating avatar for user {user_id} with prompt: {request.prompt}") # 1. Generate Image result = await generate_image_with_provider( @@ -288,7 +288,7 @@ async def enhance_prompt_route( """Enhance a simple prompt into a detailed midjourney-style prompt.""" try: user_id = _extract_user_id(current_user) - logger.info(f"Enhancing prompt for user {user_id}: {request.prompt}") + logger.warning(f"Enhancing prompt for user {user_id}: {request.prompt}") enhanced_prompt = await enhance_image_prompt(request.prompt, user_id=user_id) @@ -312,7 +312,7 @@ async def create_variation_route( """Generate a variation of an existing avatar.""" try: user_id = _extract_user_id(current_user) - logger.info(f"Creating variation for user {user_id} with prompt: {prompt}") + logger.warning(f"Creating variation for user {user_id} with prompt: {prompt}") # Read file file_content = await file.read() @@ -387,7 +387,7 @@ async def enhance_avatar_route( """Enhance/Upscale an existing avatar.""" try: user_id = _extract_user_id(current_user) - logger.info(f"Enhancing avatar for user {user_id}") + logger.warning(f"Enhancing avatar for user {user_id}") # Read file file_content = await file.read() @@ -492,7 +492,7 @@ async def create_voice_clone( random_suffix = ''.join(random.choices(string.ascii_letters + string.digits, k=8)) custom_voice_id = f"vc_{random_suffix}" - logger.info(f"Cloning voice with Minimax, ID: {custom_voice_id}") + logger.warning(f"Cloning voice with Minimax, ID: {custom_voice_id}") # Run blocking call in executor result = await loop.run_in_executor( @@ -507,7 +507,7 @@ async def create_voice_clone( preview_audio_bytes = result.preview_audio_bytes elif engine.lower() == "cosyvoice": - logger.info("Cloning voice with CosyVoice") + logger.warning("Cloning voice with CosyVoice") result = await loop.run_in_executor( None, lambda: cosyvoice_voice_clone( @@ -522,7 +522,7 @@ async def create_voice_clone( custom_voice_id = f"vc_cosy_{asset_uuid}" else: # qwen3 (default) - logger.info("Cloning voice with Qwen3") + logger.warning("Cloning voice with Qwen3") result = await loop.run_in_executor( None, lambda: qwen3_voice_clone( @@ -558,9 +558,9 @@ async def create_voice_clone( logger.warning(f"[VoiceClone] Preview filename (corrected ext): {preview_filename}") user_voice_dir = get_user_workspace(user_id) / "assets" / "voice_samples" - logger.info(f"[VoiceClone] user_id: {user_id}") - logger.info(f"[VoiceClone] user_voice_dir: {user_voice_dir}") - logger.info(f"[VoiceClone] directory exists: {user_voice_dir.exists()}") + logger.warning(f"[VoiceClone] user_id: {user_id}") + logger.warning(f"[VoiceClone] user_voice_dir: {user_voice_dir}") + logger.warning(f"[VoiceClone] directory exists: {user_voice_dir.exists()}") saved_preview_path, error = save_file_safely(preview_audio_bytes, user_voice_dir, preview_filename) if not error and saved_preview_path: @@ -623,7 +623,7 @@ async def create_voice_design( """Create a voice from text description (Voice Design).""" try: user_id = _extract_user_id(current_user) - logger.info(f"Designing voice for user {user_id}") + logger.warning(f"Designing voice for user {user_id}") loop = asyncio.get_event_loop() @@ -640,7 +640,7 @@ async def create_voice_design( # Save the result to a file with correct extension based on content from utils.media_utils import detect_audio_format, ensure_audio_extension detected_fmt, mime_type = detect_audio_format(result.preview_audio_bytes) - logger.info(f"[VoiceDesign] Detected audio format: {detected_fmt} ({mime_type})") + logger.warning(f"[VoiceDesign] Detected audio format: {detected_fmt} ({mime_type})") filename = generate_unique_filename("voice_design_preview", detected_fmt) filename = ensure_audio_extension(filename, result.preview_audio_bytes) diff --git a/frontend/src/api/brandAssets.ts b/frontend/src/api/brandAssets.ts index 190e40fb..b7c1745a 100644 --- a/frontend/src/api/brandAssets.ts +++ b/frontend/src/api/brandAssets.ts @@ -195,6 +195,8 @@ export const createVoiceClone = async ( params: VoiceCloneParams ): Promise => { try { + console.log('[VoiceClone] Creating voice clone with engine:', params.engine); + const formData = new FormData(); formData.append('file', params.audioFile); formData.append('engine', params.engine); @@ -214,14 +216,16 @@ export const createVoiceClone = async ( // We might want to remove this if backend doesn't need it formData.append('voice_name', 'My Voice Clone'); + console.log('[VoiceClone] Sending request to /onboarding/assets/create-voice-clone'); const response = await apiClient.post('/onboarding/assets/create-voice-clone', formData, { headers: { 'Content-Type': 'multipart/form-data', }, }); + console.log('[VoiceClone] Response received:', response.data); return response.data; } catch (error: any) { - console.error('Voice cloning error:', error); + console.error('[VoiceClone] Error creating voice clone:', error.response?.data || error.message); return { success: false, error: error.response?.data?.detail || 'Failed to create voice clone' diff --git a/frontend/src/components/OnboardingWizard/PersonalizationStep/components/VoiceAvatarPlaceholder.tsx b/frontend/src/components/OnboardingWizard/PersonalizationStep/components/VoiceAvatarPlaceholder.tsx index 2342acd0..662d253f 100644 --- a/frontend/src/components/OnboardingWizard/PersonalizationStep/components/VoiceAvatarPlaceholder.tsx +++ b/frontend/src/components/OnboardingWizard/PersonalizationStep/components/VoiceAvatarPlaceholder.tsx @@ -100,7 +100,9 @@ export const VoiceAvatarPlaceholder: React.FC<{ domainName?: string; onVoiceSet? const [authenticatedAudioUrl, setAuthenticatedAudioUrl] = useState(null); useEffect(() => { + console.log('[VoiceClone] resultAudioUrl changed:', resultAudioUrl); if (!resultAudioUrl || !resultAudioUrl.includes('/api/')) { + console.log('[VoiceClone] Using resultAudioUrl directly (no API auth needed)'); setAuthenticatedAudioUrl(resultAudioUrl); return; } @@ -110,14 +112,22 @@ export const VoiceAvatarPlaceholder: React.FC<{ domainName?: string; onVoiceSet? const tokenGetter = getAuthTokenGetter(); if (tokenGetter) { const token = await tokenGetter(); + console.log('[VoiceClone] Got token:', token ? 'yes' : 'no'); if (token && !cancelled) { const sep = resultAudioUrl.includes('?') ? '&' : '?'; - setAuthenticatedAudioUrl(`${resultAudioUrl}${sep}token=${encodeURIComponent(token)}`); + const authUrl = `${resultAudioUrl}${sep}token=${encodeURIComponent(token)}`; + console.log('[VoiceClone] Setting authenticatedAudioUrl:', authUrl); + setAuthenticatedAudioUrl(authUrl); return; } } - } catch { /* fallback to unauthenticated */ } - if (!cancelled) setAuthenticatedAudioUrl(resultAudioUrl); + } catch (e) { + console.warn('[VoiceClone] Token fetch error:', e); + } + if (!cancelled) { + console.log('[VoiceClone] Falling back to unauthenticated URL'); + setAuthenticatedAudioUrl(resultAudioUrl); + } })(); return () => { cancelled = true; }; }, [resultAudioUrl]); @@ -1123,8 +1133,13 @@ export const VoiceAvatarPlaceholder: React.FC<{ domainName?: string; onVoiceSet? src={authenticatedAudioUrl || undefined} preload="auto" style={{ width: '100%', height: '28px' }} + onLoadedMetadata={(e) => { + console.log('[VoiceClone] Audio duration loaded:', (e.target as HTMLAudioElement).duration); + }} onError={(e) => { - console.error('[VoiceClone] Generated audio playback error:', e); + const audioEl = e.target as HTMLAudioElement; + const errorMsg = audioEl?.error ? `code=${audioEl.error.code}, message=${audioEl.error.message}` : 'unknown'; + console.error('[VoiceClone] Generated audio playback error:', errorMsg, 'URL:', authenticatedAudioUrl); }} />