diff --git a/backend/api/onboarding_utils/step4_asset_routes.py b/backend/api/onboarding_utils/step4_asset_routes.py index 08d584e0..86845961 100644 --- a/backend/api/onboarding_utils/step4_asset_routes.py +++ b/backend/api/onboarding_utils/step4_asset_routes.py @@ -9,8 +9,21 @@ from fastapi.responses import FileResponse from sqlalchemy.orm import Session from pydantic import BaseModel from loguru import logger -from .step4_persona_routes import _extract_user_id from middleware.auth_middleware import get_current_user + + +def _extract_user_id(user: Dict[str, Any]) -> str: + """Extract a stable user ID from Clerk-authenticated user payloads. + Prefers 'clerk_user_id' or 'id', falls back to 'user_id', else 'unknown'. + """ + if not isinstance(user, dict): + return 'unknown' + return ( + user.get('clerk_user_id') + or user.get('id') + or user.get('user_id') + or 'unknown' + ) import base64 import os from pathlib import Path diff --git a/backend/app.py b/backend/app.py index 61f597ab..fad59c5a 100644 --- a/backend/app.py +++ b/backend/app.py @@ -426,13 +426,15 @@ if PODCAST_ONLY_DEMO_MODE: podcast_routers = [r for r in CORE_ROUTER_REGISTRY if "podcast" in r.get("features", set())] logger.info(f"[PODCAST-ONLY] Found {len(podcast_routers)} podcast routers: {[r['name'] for r in podcast_routers]}") - # Force include step4_assets for voice cloning + # Try to include step4_assets for voice cloning (may fail if nltk not installed) step4_entry = next((r for r in CORE_ROUTER_REGISTRY if r.get("name") == "step4_assets"), None) if step4_entry: try: - logger.info(f"[PODCAST-ONLY] Forcing load of step4_assets for voice cloning") + logger.info(f"[PODCAST-ONLY] Attempting to load step4_assets for voice cloning") router = router_manager._load_router_from_registry(step4_entry) router_manager.include_router_safely(router, step4_entry["name"], step4_entry.get("include_kwargs")) + except ImportError as e: + logger.warning(f"[PODCAST-ONLY] Skipping step4_assets (missing optional dependency): {e}") except Exception as e: logger.error(f"[PODCAST-ONLY] Failed to mount step4_assets: {e}") diff --git a/backend/utils/media_utils.py b/backend/utils/media_utils.py index d97cf90b..34d77c3d 100644 --- a/backend/utils/media_utils.py +++ b/backend/utils/media_utils.py @@ -97,13 +97,22 @@ def resolve_media_path(media_url_or_path: str) -> Optional[Path]: # Prioritize YouTube paths pass # Already first in list elif "/api/podcast/" in media_url_or_path: - # Prioritize Podcast paths - search_paths = [ - PODCAST_AVATARS_DIR / filename, - PODCAST_IMAGES_DIR / filename, - YOUTUBE_AVATARS_DIR / filename, - YOUTUBE_IMAGES_DIR / filename - ] + # Prioritize Podcast paths: use centralized podcast media resolution + try: + # Import the centralized function that checks tenant workspace first + from api.podcast.constants import get_podcast_media_read_dirs + podcast_dirs = get_podcast_media_read_dirs("image") + search_paths = [] + for pod_dir in podcast_dirs: + # Add both avatar and image subdirectories + search_paths.append(pod_dir / "avatars" / filename) + search_paths.append(pod_dir / filename) + except ImportError: + # Fallback if podcast constants not available + search_paths = [ + PODCAST_AVATARS_DIR / filename, + PODCAST_IMAGES_DIR / filename, + ] # Iterate and find first existing file for path in search_paths: