def run(
        self,
        collection_id: str,
        engine: str = "stabilityai",
        audio_engine: str = "videodb",
        job_type: str = "text_to_movie",
        text_to_movie: Optional[dict] = None,
        *args,
        **kwargs,
    ) -> AgentResponse:
        """
        Process the storyline to generate a movie.

        :param collection_id: The collection ID to store generated assets
        :param engine: Video generation engine to use
        :return: AgentResponse containing information about generated movie
        """
        try:
            self.videodb_tool = VideoDBTool(collection_id=collection_id)
            self.output_message.actions.append("Processing input...")
            video_content = VideoContent(
                agent_name=self.agent_name,
                status=MsgStatus.progress,
                status_message="Generating movie...",
            )
            self.output_message.content.append(video_content)
            self.output_message.push_update()

            if engine not in self.engine_configs:
                raise ValueError(f"Unsupported engine: {engine}")

            if engine == "stabilityai":
                STABILITY_API_KEY = os.getenv("STABILITYAI_API_KEY")
                if not STABILITY_API_KEY:
                    raise Exception("Stability AI API key not found")
                self.video_gen_tool = StabilityAITool(api_key=STABILITY_API_KEY)
                self.video_gen_config_key = "video_stabilityai_config"
            elif engine == "kling":
                KLING_API_ACCESS_KEY = os.getenv("KLING_AI_ACCESS_API_KEY")
                KLING_API_SECRET_KEY = os.getenv("KLING_AI_SECRET_API_KEY")
                if not KLING_API_ACCESS_KEY or not KLING_API_SECRET_KEY:
                    raise Exception("Kling AI API key not found")
                self.video_gen_tool = KlingAITool(
                    access_key=KLING_API_ACCESS_KEY, secret_key=KLING_API_SECRET_KEY
                )
                self.video_gen_config_key = "video_kling_config"

            elif engine == "videodb":
                self.video_gen_config_key = "video_kling_config"
                self.video_gen_tool = VDBVideoGenerationTool()
            else:
                raise Exception(f"{engine} not supported")

            # Initialize tools
            self.audio_gen_config_key = "audio_elevenlabs_config"

            if audio_engine == "elevenlabs":
                ELEVENLABS_API_KEY = os.getenv("ELEVENLABS_API_KEY")
                if not ELEVENLABS_API_KEY:
                    raise Exception("ElevenLabs API key not found")
                self.audio_gen_tool = ElevenLabsTool(api_key=ELEVENLABS_API_KEY)

            else:
                self.audio_gen_tool = VDBAudioGenerationTool()    

            if job_type == "text_to_movie":
                raw_storyline = text_to_movie.get("storyline", [])
                video_gen_config = text_to_movie.get(self.video_gen_config_key, {})

                if engine == "videodb":
                    audio_gen_config = {}
                else:
                    audio_gen_config = text_to_movie.get(self.audio_gen_config_key, {})

                # Generate visual style
                visual_style = self.generate_visual_style(raw_storyline)
                print("These are visual styles", visual_style)

                # Generate scenes
                scenes = self.generate_scene_sequence(
                    raw_storyline, visual_style, engine
                )
                print("These are scenes", scenes)

                self.output_message.actions.append(
                    f"Generating {len(scenes)} videos..."
                )
                self.output_message.push_update()

                engine_config = self.engine_configs[engine]
                generated_videos_results = []

                # Generate videos sequentially
                for index, scene in enumerate(scenes):
                    self.output_message.actions.append(
                        f"Generating video for scene {index + 1}..."
                    )
                    self.output_message.push_update()

                    suggested_duration = min(
                        scene.get("suggested_duration", 5), engine_config.max_duration
                    )
                    # Generate engine-specific prompt
                    prompt = self.generate_engine_prompt(scene, visual_style, engine)

                    print(f"Generating video for scene {index + 1}...")
                    print("This is the prompt", prompt)

                    video_path = f"{DOWNLOADS_PATH}/{str(uuid.uuid4())}.mp4"
                    os.makedirs(DOWNLOADS_PATH, exist_ok=True)

                    video = self.video_gen_tool.text_to_video(
                        prompt=prompt,
                        save_at=video_path,
                        duration=suggested_duration,
                        config=video_gen_config,
                    )
                    generated_videos_results.append(
                        VideoGenResult(
                            step_index=index, video_path=video_path, success=True, video=video
                        )
                    )

                self.output_message.actions.append(
                    f"Uploading {len(generated_videos_results)} videos to VideoDB..."
                )
                self.output_message.push_update()

                # Process videos and track duration
                total_duration = 0
                for result in generated_videos_results:
                    if not result.success:
                        raise Exception(
                            f"Failed to generate video {result.step_index}: {result.error}"
                        )
                    if result.video is None:
                        self.output_message.actions.append(
                            f"Uploading video {result.step_index + 1}..."
                        )
                        self.output_message.push_update()
                        media = self.videodb_tool.upload(
                            result.video_path,
                            source_type="file_path",
                            media_type="video",
                        )
                    else:
                        media = result.video

                    total_duration += float(media.get("length", 0))
                    scenes[result.step_index]["video"] = media

                    if os.path.exists(result.video_path):
                        os.remove(result.video_path)

                # Generate audio prompt
                sound_effects_description = self.generate_audio_prompt(raw_storyline)

                self.output_message.actions.append("Generating background music...")
                self.output_message.push_update()

                # Generate and add sound effects
                os.makedirs(DOWNLOADS_PATH, exist_ok=True)
                sound_effects_path = f"{DOWNLOADS_PATH}/{str(uuid.uuid4())}.mp3"

                sound_effects_media = self.audio_gen_tool.generate_sound_effect(
                    prompt=sound_effects_description,
                    save_at=sound_effects_path,
                    duration=total_duration,
                    config=audio_gen_config,
                )

                if sound_effects_media is None:
                    self.output_message.actions.append(
                        "Uploading background music to VideoDB..."
                    )
                    self.output_message.push_update()

                    sound_effects_media = self.videodb_tool.upload(
                        sound_effects_path, source_type="file_path", media_type="audio"
                    )

                if os.path.exists(sound_effects_path):
                    os.remove(sound_effects_path)

                self.output_message.actions.append(
                    "Combining assets into final video..."
                )
                self.output_message.push_update()

                # Combine everything into final video
                final_video = self.combine_assets(scenes, sound_effects_media)

                video_content.video = VideoData(stream_url=final_video)
                video_content.status = MsgStatus.success
                video_content.status_message = "Movie generation complete"
                self.output_message.publish()

                return AgentResponse(
                    status=AgentStatus.SUCCESS,
                    message="Movie generated successfully",
                    data={"video_url": final_video},
                )

            else:
                raise ValueError(f"Unsupported job type: {job_type}")

        except Exception as e:
            logger.exception(f"Error in {self.agent_name} agent: {e}")
            video_content.status = MsgStatus.error
            video_content.status_message = "Error generating movie"
            self.output_message.publish()
            return AgentResponse(
                status=AgentStatus.ERROR, message=f"Agent failed with error: {str(e)}"
            )

        Process the storyline to generate a movie.

        :param collection_id: The collection ID to store generated assets
        :param engine: Video generation engine to use
        :return: AgentResponse containing information about generated movie
        
backend/director/agents/text_to_movie.py