import React, { useState } from 'react'; import { Box, Paper, Typography, Button, TextField, Alert, Divider, CircularProgress, LinearProgress, } from '@mui/material'; import VideoLibraryIcon from '@mui/icons-material/VideoLibrary'; import DownloadIcon from '@mui/icons-material/Download'; import { useStoryWriterState } from '../../../hooks/useStoryWriterState'; import { storyWriterApi } from '../../../services/storyWriterApi'; interface StoryExportProps { state: ReturnType; } const StoryExport: React.FC = ({ state }) => { const [isGeneratingVideo, setIsGeneratingVideo] = useState(false); const [videoProgress, setVideoProgress] = useState(0); const [error, setError] = useState(null); const handleCopyToClipboard = () => { if (state.storyContent) { navigator.clipboard.writeText(state.storyContent); } }; const handleDownload = () => { if (state.storyContent) { const blob = new Blob([state.storyContent], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `story-${Date.now()}.txt`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } }; const handleGenerateVideo = async () => { if (!state.enableVideoNarration) { setError('Story video generation is disabled in Story Setup.'); return; } if (!state.outlineScenes || state.outlineScenes.length === 0) { setError('Please generate a structured outline first'); return; } if (!state.sceneImages || state.sceneImages.size === 0) { setError('Please generate images for scenes first'); return; } if (!state.sceneAudio || state.sceneAudio.size === 0) { setError('Please generate audio for scenes first'); return; } setIsGeneratingVideo(true); setError(null); setVideoProgress(0); try { // Prepare image and audio URLs in scene order const imageUrls: string[] = []; const audioUrls: string[] = []; const scenes = state.outlineScenes; for (const scene of scenes) { const sceneNumber = scene.scene_number || scenes.indexOf(scene) + 1; const imageUrl = state.sceneImages?.get(sceneNumber); const audioUrl = state.sceneAudio?.get(sceneNumber); if (imageUrl && audioUrl) { imageUrls.push(imageUrl); audioUrls.push(audioUrl); } else { throw new Error(`Missing image or audio for scene ${sceneNumber}`); } } if (imageUrls.length !== scenes.length || audioUrls.length !== scenes.length) { throw new Error('Number of images and audio files must match number of scenes'); } // Generate video const response = await storyWriterApi.generateStoryVideo({ scenes: scenes, image_urls: imageUrls, audio_urls: audioUrls, story_title: state.storySetting || 'Story', fps: state.videoFps, transition_duration: state.videoTransitionDuration, }); if (response.success && response.video) { state.setStoryVideo(response.video.video_url); state.setError(null); setVideoProgress(100); } else { throw new Error('Failed to generate video'); } } catch (err: any) { const errorMessage = err.response?.data?.detail || err.message || 'Failed to generate video'; setError(errorMessage); state.setError(errorMessage); } finally { setIsGeneratingVideo(false); } }; const handleDownloadVideo = () => { if (state.storyVideo) { const videoUrl = storyWriterApi.getVideoUrl(state.storyVideo); const a = document.createElement('a'); a.href = videoUrl; a.download = `story-video-${Date.now()}.mp4`; document.body.appendChild(a); a.click(); document.body.removeChild(a); } }; return ( Export Story Your story is complete! You can copy it to clipboard or download it as a text file. {error && ( setError(null)}> {error} )} {!state.storyContent ? ( No story content available. Please complete the writing phase first. ) : ( <> {/* Story Summary */} Story Summary Setting: {state.storySetting || 'N/A'} Characters: {state.characters || 'N/A'} Style: {state.writingStyle} | Tone: {state.storyTone} POV: {state.narrativePOV} | Audience: {state.audienceAgeGroup} {/* Premise */} {state.premise && ( Premise )} {/* Outline */} {state.outline && ( Outline )} {/* Story Content */} Complete Story {/* Video Generation */} {state.isOutlineStructured && state.outlineScenes && ( state.enableVideoNarration ? ( Video Generation Generate a video from your story scenes with images and audio narration. {(!state.sceneImages || state.sceneImages.size === 0) && ' Generate images first.'} {(!state.sceneAudio || state.sceneAudio.size === 0) && ' Generate audio first.'} {isGeneratingVideo && ( Generating video... {videoProgress}% )} {state.storyVideo && ( Generated story video )} {state.storyVideo && ( )} ) : ( Story video generation is disabled in Story Setup. Enable it to create narrated videos. ) )} {/* Export Actions */} )} ); }; export default StoryExport;