- Move voice clone cache from module-level memory to localStorage
so it survives page refresh and works across browser tabs
- VoiceAvatarPlaceholder now syncs clone result to localStorage
immediately after creation (both design and clone paths)
- usePodcastProjectState auto-merges voice clone cache into project
knobs when loading a project (fills gap for projects created
before voice clone or when voice clone was created after)
- CreateModal now detects voice clone IDs by prefix (vc_*) not
just by VOICE_CLONE_ID constant, fixing the mismatch where
VoiceSelector passes the actual clone ID but CreateModal
expected the placeholder ID
- AudioRegenerateModal is intentionally per-scene override and
does not write back to knobs (by design)
- trends.py handler added for podcast topic trend analysis
Frontend Changes:
- Add scene numbering badge (1/N) next to scene titles
- Add inline status chips (Complete, Audio, Image, Voice, Why Script)
- Professional AI-like gradient styling for all chips with shadows
- Remove Script Editor header and 'Why This Script Format?' collapsible
- Move Voice and Why Script info to per-scene chips
- Make scene section mobile-responsive (responsive layout, button sizing)
- Rename 'B-Roll Charts' to 'Podcast Charts' with accordion (collapsed by default)
- Add sceneIndex prop to SceneEditor for scene numbering
- Enhanced accessibility with keyboard navigation and focus states
Backend Changes:
- Audio handler improvements
- B-roll handler enhancements
- Script handler updates
- B-roll composer and service improvements
- Removed temporary broll_temp files
Technical:
- Full mobile responsiveness for scene cards
- Gradient chip styling: vibrant colors with white text and shadows
- Non-breaking approval/generation flow preserved
- TypeScript compatibility maintained
- Restore auth on assets_serving.py using get_current_user_with_query_token
(supports ?token= query param for <audio> elements)
- Add proper MIME type detection on asset serving (fixes NotSupportedError)
- Use storage_paths for path resolution in assets_serving.py
- VoiceSelector: append auth token to preview URLs for /api/ endpoints
- VoiceAvatarPlaceholder: add authenticatedAudioUrl state with async token
resolution so <audio> elements get ?token= query param
- TestPersonaModal: same auth token pattern for voice preview audio
- Upgrade utils/storage_paths.py with robust find_repo_root() (env var override + validation + fallback)
- Remove broken _find_root() from podcast/constants.py, import from storage_paths instead
- Fix ROOT_DIR resolving to backend/ instead of project root (caused avatar upload 500s on Render.com)
- Fix video_combination_service.py default output dir (was writing to data/media instead of workspace)
- Add deprecation comments to global data/media constants in media_utils.py
- Pass user_id through resolve_media_path for tenant-scoped podcast resolution
- Add ALWRITY_ROOT_DIR env var support for explicit production overrides
- Log warning when get_podcast_media_dir called without user_id
- Use OperationButton with cost display for scene action buttons
- Voice clone integration: When user selects voice clone in Write phase,
backend uses their uploaded voice sample + scene script text to generate
audio via qwen3/minimax/cosyvoice voice clone APIs
- Multi-tenant workspace storage: All podcast assets (audio, video, images,
charts) now use workspace-specific directories per user
- Chart preview improvements: Card-based B-Roll charts UI with thumbnails,
takeaway text, and action buttons; public endpoint for image serving
- Voice clone caching: In-memory LRU cache for voice samples (avoids
re-downloading per scene); frontend caches voice clone metadata
- Thread pool for voice clone: Audio generation uses ThreadPoolExecutor to
avoid blocking the FastAPI event loop
- Auto-detect voice clone IDs (vc_*, MY_VOICE_CLONE) to route correctly
- DB fallback for voice sample URL: Fetches from ContentAsset if not passed
- Fixed API URL resolution for chart previews
- Fixed GlassyCard DOM warnings for motion props
- Fixed ScriptGenerationProgressView syntax error
- Fixed usePodcastWorkflow scriptData reference
- Updated canSubmit to require all fields: topic, presenter avatar, voice selection, duration, speakers, and podcast mode
- Removed audio_only exception - avatar now required for all modes
- Updated tooltip message in CreateActions
- Add hamburger menu to Header with gradient styling
- Move Help, My Episodes, My Projects, New Episode into dropdown menu
- Move PodcastBiblePanel into AnalysisPanel header as icon button
- Display Bible details in a styled Popover
- Improve overall header UX and mobile responsiveness
- Add isGeneratingRef to track if generation is in progress
- Guard against double calls by checking ref before starting
- Reset ref in finally block to allow subsequent generations
- This fixes the issue where script generation was called twice in succession
- Wrap all AvatarSelector callback handlers in useCallback in CreateModal.tsx
- Add isCancelled flag pattern to RobustCamera useEffect
- Inline camera initialization to avoid stale closure issues
- Add proper cleanup on component unmount
- Ensure camera stream is properly stopped if component unmounts during initialization
- Remove unused initializeCamera function
- Add React.memo to CameraSelfie to prevent unnecessary re-renders
- Memoize callbacks in CameraSelfie
- Track previous open/facingMode state in RobustCamera to detect actual changes
- Add streamAttachedRef to prevent duplicate stream attachments
- Fix useEffect dependencies to prevent cleanup on parent re-renders
- Ensure camera only initializes on actual dialog open (not parent re-render)
- Consolidate stream attachment into single useEffect
- Remove race conditions from multiple competing effects
- Add proper cleanup with video element reset
- Simplify state management using single stream state
- Add isMountedRef to prevent state updates after unmount
- Improve error handling with specific error messages
- Add canvas flip correction for front camera mirror effect
- Use explicit MIME type for MediaRecorder (audio/webm;codecs=opus)
- Add error handling for audio playback
- Copy chunks before creating blob to prevent race conditions
- Add key prop to audio elements for proper re-rendering
- Add TextToSpeechButton to Outline, Takeaways, and Guest tabs in analysis phase
- Add help icon with tooltip to Research Queries explaining the workflow
- Change Run Research button to show 'Next: Select Query' when disabled
- Add hint text 'Select a query to continue' when no queries selected
- Make header stack responsive with column layout on mobile
- Add responsive breakpoints for tab sizes and padding
- Fix image preview max widths for mobile screens
- Add responsive font sizes for info boxes
- Adjust container padding for smaller screens
- Fix icon sizes for mobile devices
- Remove Research Queries section from AnalysisPanel.tsx
- Keep QuerySelection component as single source for research queries
- Remove unused props (onRunResearch, isResearchRunning, selectedQueries, etc.)
- Add pandas to requirements-podcast.txt for usage tracking
- Fix LLM prompt to return plain strings instead of objects for enhanced_ideas
- Add object-to-string normalization for LLM responses that return objects
- Skip bible generation in podcast-only mode (onboarding disabled)
- Skip alerts polling in AlertsBadge when in podcast-only demo mode
- AlertsBadge: Skip agent alerts fetch in podcast mode
- UserBadge: Skip system status fetch in podcast mode
- SystemStatusIndicator: Skip monitoring stats in podcast mode
This prevents 404 errors when frontend calls endpoints that don't exist in podcast-only demo mode.