feat: voice clone audio generation + podcast workspace architecture

- 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
This commit is contained in:
ajaysi
2026-04-21 19:38:50 +05:30
parent 7637babd7d
commit 91b2f996fd
33 changed files with 1642 additions and 457 deletions

View File

@@ -5,12 +5,40 @@ interface GlassyCardProps {
children?: React.ReactNode;
sx?: SxProps<Theme>;
onClick?: () => void;
[key: string]: any; // Allow other props for framer-motion
// Allow motion props (framer-motion) - they'll be filtered out to avoid DOM warnings
whileHover?: any;
whileTap?: any;
initial?: any;
animate?: any;
exit?: any;
transition?: any;
variants?: any;
layout?: any;
layoutId?: any;
className?: string;
'aria-label'?: string;
}
export const GlassyCard: React.FC<GlassyCardProps> = ({ children, sx, ...props }) => {
// Filter out motion props to avoid DOM warnings - these won't work with MUI Paper anyway
const {
whileHover,
whileTap,
initial,
animate,
exit,
transition,
variants,
layout,
layoutId,
className,
'aria-label': ariaLabel,
...filteredProps
} = props;
return (
<Paper
className={className}
aria-label={ariaLabel}
sx={{
borderRadius: 3,
border: "1px solid rgba(15, 23, 42, 0.06)",
@@ -25,7 +53,7 @@ export const GlassyCard: React.FC<GlassyCardProps> = ({ children, sx, ...props }
},
...sx
}}
{...props}
{...filteredProps}
>
{children}
</Paper>