Skip to content

Feat/genai videodb#174

Merged
0xrohitgarg merged 10 commits intovideo-db:mainfrom
omgate234:feat/genai-videodb
May 27, 2025
Merged

Feat/genai videodb#174
0xrohitgarg merged 10 commits intovideo-db:mainfrom
omgate234:feat/genai-videodb

Conversation

@omgate234
Copy link
Contributor

@omgate234 omgate234 commented May 16, 2025

  1. added the VideoDB Gen-AI methods for videodb_tool
  2. Updated agents
    • audio_generation
    • dubbing
    • image_generation
    • subtitle
    • web_search_agent
    • video_generation
    • text_to_movie

Summary by CodeRabbit

  • New Features

    • Added "videodb" as a supported and default engine across audio generation, dubbing, image generation, subtitle translation, video generation, text-to-movie, and web search agents.
    • Introduced new media generation capabilities including video, music, sound effects, voice, and image creation via the "videodb" engine.
    • Enabled direct subtitle translation and video dubbing using "videodb" for streamlined media workflows.
    • Added YouTube search and transcript translation features via "videodb".
  • Improvements

    • Added flexible engine selection options for audio and video generation tasks.
    • Streamlined subtitle translation by delegating to an external service.
    • Enhanced media upload, status messaging, and cleanup processes for generated content.
    • Improved handling of media metadata to avoid redundant uploads.
  • Chores

    • Updated the "videodb" package to version 0.2.13.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented May 16, 2025

Walkthrough

This update integrates "videodb" as a supported engine across multiple agents, including audio generation, dubbing, image generation, subtitle translation, web search, text-to-movie, and video generation. The VideoDBTool is extended with new media generation, dubbing, search, and transcript translation methods. Parameter schemas and control flows are revised to accommodate engine selection, and the videodb package version is updated.

Changes

File(s) Change Summary
backend/director/agents/audio_generation.py Added "videodb" as a supported engine; updated parameter schema, control flow, and tool instantiation for audio generation; adjusted media upload logic and added cleanup.
backend/director/agents/dubbing.py Added "videodb" as a dubbing engine; refactored control flow to use VideoDBTool for dubbing when selected; maintained existing flow for "elevenlabs".
backend/director/agents/image_generation.py Added "videodb" as an engine for text-to-image; updated schema and run method to handle engine selection and image generation accordingly; added optional parameter.
backend/director/agents/subtitle.py Removed internal translation batching and logic; delegated all transcript translation to VideoDBTool; simplified control flow and removed related imports and methods.
backend/director/agents/web_search_agent.py Added "videodb" as a search engine; introduced VideoDBSearchTool; updated agent logic and parameter schema for engine selection; changed default engine.
backend/director/agents/text_to_movie.py Added "videodb" as video and audio generation engine; updated parameters and run method to support separate audio and video engine selection; refined media upload and cleanup.
backend/director/agents/video_generation.py Added "videodb" as a supported video generation engine; updated parameter schema and run method to instantiate corresponding tool and handle media upload and cleanup.
backend/director/tools/elevenlabs.py Added VOICE_ID_MAP dictionary mapping voice IDs to names.
backend/director/tools/videodb_tool.py Added multiple new methods to VideoDBTool for translation, search, dubbing, and media generation; added VDBVideoGenerationTool and VDBAudioGenerationTool classes with local file download support and metadata return.
backend/requirements.txt Updated videodb package version from 0.2.10 to 0.2.13.
backend/director/tools/beatoven.py Renamed method generate_sound_effect to generate_music and simplified signature by removing config parameter.

Sequence Diagram(s)

Audio Generation Agent: Engine Selection and Flow

sequenceDiagram
    participant User
    participant AudioGenAgent
    participant VideoDBAudioTool
    participant ElevenLabsTool
    participant BeatovenTool

    User->>AudioGenAgent: Submit audio generation request (engine, job_type, params)
    alt engine == "videodb"
        AudioGenAgent->>VideoDBAudioTool: generate_sound_effect / text_to_speech / generate_music
        VideoDBAudioTool-->>AudioGenAgent: media URL, metadata
        AudioGenAgent->>AudioGenAgent: Download media, save locally if needed
    else engine == "elevenlabs"
        AudioGenAgent->>ElevenLabsTool: Generate audio
        ElevenLabsTool-->>AudioGenAgent: Audio file
    else engine == "beatoven"
        AudioGenAgent->>BeatovenTool: Generate music/sound
        BeatovenTool-->>AudioGenAgent: Audio file
    end
    AudioGenAgent->>VideoDBAudioTool: Upload generated audio if needed
    AudioGenAgent-->>User: Return response
Loading

Subtitle Agent: Translation Delegation

sequenceDiagram
    participant User
    participant SubtitleAgent
    participant VideoDBTool

    User->>SubtitleAgent: Request subtitle translation
    SubtitleAgent->>VideoDBTool: translate_transcript(video_id, language, notes)
    VideoDBTool-->>SubtitleAgent: Translated transcript
    SubtitleAgent-->>User: Return translated subtitles
Loading

Web Search Agent: Engine Selection

sequenceDiagram
    participant User
    participant WebSearchAgent
    participant SerpAPI
    participant VideoDBSearchTool

    User->>WebSearchAgent: Submit search request (engine, query)
    alt engine == "serp" and SERP_API_KEY present
        WebSearchAgent->>SerpAPI: search_videos(query)
        SerpAPI-->>WebSearchAgent: Search results
    else
        WebSearchAgent->>VideoDBSearchTool: search_videos(query)
        VideoDBSearchTool-->>WebSearchAgent: Search results
    end
    WebSearchAgent-->>User: Return results
Loading

Dubbing Agent: Engine-Specific Flow

sequenceDiagram
    participant User
    participant DubbingAgent
    participant ElevenLabsAPI
    participant VideoDBTool

    User->>DubbingAgent: Request dubbing (engine, video_id, language)
    alt engine == "elevenlabs"
        DubbingAgent->>ElevenLabsAPI: create_dubbing_job(video)
        ElevenLabsAPI-->>DubbingAgent: job_id
        DubbingAgent->>ElevenLabsAPI: wait_for_job(job_id)
        ElevenLabsAPI-->>DubbingAgent: dubbed_video_url
        DubbingAgent->>VideoDBTool: upload_video(dubbed_video_url)
    else engine == "videodb"
        DubbingAgent->>VideoDBTool: dub_video(video_id, language)
    end
    DubbingAgent-->>User: Return dubbed video info
Loading

Image Generation Agent: Text-to-Image Engine Selection

sequenceDiagram
    participant User
    participant ImageGenAgent
    participant FluxTool
    participant VideoDBTool

    User->>ImageGenAgent: Request image generation (engine, prompt)
    alt engine == "flux"
        ImageGenAgent->>FluxTool: flux_dev(prompt)
        FluxTool-->>ImageGenAgent: image_url or error
    else engine == "videodb"
        ImageGenAgent->>VideoDBTool: generate_image(prompt)
        VideoDBTool-->>ImageGenAgent: image_url
    end
    ImageGenAgent-->>User: Return generated image info
Loading

Text-to-Movie Agent: Audio and Video Engine Selection

sequenceDiagram
    participant User
    participant TextToMovieAgent
    participant StabilityAITool
    participant KlingTool
    participant VideoDBVideoTool
    participant ElevenLabsAudioTool
    participant VideoDBAudioTool

    User->>TextToMovieAgent: Submit text-to-movie request (video_engine, audio_engine, params)
    alt video_engine == "videodb"
        TextToMovieAgent->>VideoDBVideoTool: generate_video(...)
    else video_engine == "stabilityai"
        TextToMovieAgent->>StabilityAITool: generate_video(...)
    else video_engine == "kling"
        TextToMovieAgent->>KlingTool: generate_video(...)
    end
    alt audio_engine == "elevenlabs"
        TextToMovieAgent->>ElevenLabsAudioTool: generate_audio(...)
    else audio_engine == "videodb"
        TextToMovieAgent->>VideoDBAudioTool: generate_audio(...)
    end
    TextToMovieAgent-->>User: Return composed video with audio
Loading

Possibly related PRs

  • add support for music creation using beatoven #146: The main PR extends the audio generation agent to add "videodb" as a new engine alongside existing ones including "beatoven," while the retrieved PR originally added support for "beatoven" as a new audio generation engine; both modify the same AudioGenerationAgent class and its run method to handle multiple audio engines and job types, making their changes directly related at the code level.
  • remove kling support #118: Adds support for the "videodb" engine in video_generation.py, related by context to engine handling.
  • feat(agent): add Web Search Agent using SerpAPI #104: Adds initial WebSearchAgent with SerpAPI; this PR extends it to support "videodb" as a search engine.

Suggested reviewers

  • ankit-v2-3
  • 0xrohitgarg

Poem

🐇
In the garden of code, new engines bloom,
"videodb" joins the warren, dispelling the gloom.
Audio, image, subtitles—now all can play,
With seamless new methods, in a bright, hoppy way.
The search grows more clever, the dubbing more fun,
As rabbits rejoice—our work is well done!
🌱🎶🖼️

Note

⚡️ AI Code Reviews for VS Code, Cursor, Windsurf

CodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback.
Learn more here.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cb63768 and 51c4207.

📒 Files selected for processing (1)
  • backend/director/agents/video_generation.py (9 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/director/agents/video_generation.py
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🔭 Outside diff range comments (6)
backend/director/agents/web_search_agent.py (1)

99-103: 🛠️ Refactor suggestion

Incorrect type-hint for search_videos masks real-world bugs

search_videos is expected to be a mapping (you call .get() on it later) but the
signature marks it as Optional[str]. Type-checkers (mypy/pyright) will
complain, and IDEs will suggest the wrong auto-completions.
Changing it to Optional[dict] (or a TypedDict for extra safety) prevents those issues.

-        search_videos: Optional[str] = None,
+        search_videos: Optional[dict] = None,
backend/director/agents/subtitle.py (1)

181-185: 🛠️ Refactor suggestion

Potential undefined stream_url on unexpected paths

stream_url is defined only if the subtitle overlay succeeds.
If an unforeseen exception slips past the earlier guards, the final SUCCESS
response will raise UnboundLocalError. Initialising it to None and adding
a final sanity check prevents that:

stream_url: str | None = None
...
if stream_url is None:
    raise RuntimeError("Subtitle overlay failed unexpectedly")
backend/director/agents/audio_generation.py (4)

24-26: ⚠️ Potential issue

videodb is the new default engine but is not in SUPPORTED_ENGINES – every call will raise

run() rejects any engine not listed in SUPPORTED_ENGINES.
Because "videodb" is missing, the very first validation (engine not in SUPPORTED_ENGINES) raises, making the rest of the Videodb integration unreachable.

-SUPPORTED_ENGINES = ["elevenlabs", "beatoven"]
+SUPPORTED_ENGINES = ["elevenlabs", "beatoven", "videodb"]

You may also want to invert the guard to use the freshly-updated list.

Also applies to: 137-139


157-165: ⚠️ Potential issue

Unreachable Videodb branch for sound_effect

The early guard rejects anything that isn’t "elevenlabs", so the later if engine == "videodb": block can never execute.

-    if engine != "elevenlabs":
-        raise Exception("Sound effects only supported with elevenlabs")
+    if engine not in ("elevenlabs", "videodb"):
+        raise Exception(f"Sound effects not supported with {engine}")

Consider refactoring the whole sound_effect section to:

if job_type == "sound_effect":
    prompt = sound_effect["prompt"]
    duration = sound_effect.get("duration", 5)
    config = sound_effect.get("elevenlabs_config", {})

    if engine == "elevenlabs":
        audio_gen_tool.generate_sound_effect(prompt, output_path, duration, config)
    elif engine == "videodb":
        media = self.videodb_tool.generate_sound_effect(prompt, duration, config)
        _download_to_path(media["url"], output_path)
    else:
        raise Exception(f"{engine} does not support sound effects")

188-196: ⚠️ Potential issue

Same logic gap for create_music

Early guard forces "beatoven" only, but a "videodb" branch is added later ⇒ dead code.
Tighten the guard or restructure like the sound-effect suggestion.


216-224: ⚠️ Potential issue

text_to_speech branch also unreachable for Videodb

if engine != "elevenlabs": raise … blocks Videodb TTS completely. Remove the guard or widen it to the supported pair.

🧹 Nitpick comments (7)
backend/director/agents/web_search_agent.py (1)

81-88: Minor: unnecessary wrapper & default duration inconsistency

VideoDBSearchTool is a one-line proxy around VideoDBTool.youtube_search.
Instead of instantiating an extra class every run, you could reuse the
existing VideoDBTool directly, reducing object churn and keeping a single
source of truth.

If you keep the wrapper:

  • the method’s default duration="medium" differs from the old SerpAPI
    behaviour (which defaulted to no duration filter). Align defaults or make
    them explicit in the schema to avoid confusing results.
backend/director/agents/subtitle.py (1)

122-130: get_transcript() return value is ignored

The transcript fetched in the first try block is discarded.
If the intention is only to verify availability, please add a clarifying
comment or reuse the text later; otherwise the network round-trip is wasted.

# We only call this to check availability – result intentionally ignored.

[nitpick]

backend/director/tools/videodb_tool.py (2)

360-372: Typo & naming consistency in dub_video

Variable dubed_video is misspelled. More importantly, returning a dict
manually duplicates field names used throughout the class; consider delegating
to self.get_video() for consistency and to avoid future drift.

-        dubed_video = self.collection.dub_video(...)
+        dubbed_video = self.collection.dub_video(...)
...
-            "id": dubed_video.id,
+            "id": dubbed_video.id,

417-425: Inconsistent URL/property naming across generators

generate_video() returns stream_url, while generate_image() / generate_*
return url. Standardising the key (url or stream_url) makes it easier for
callers to handle generically-generated assets.

Consider aligning to a single attribute or adding both for backward
compatibility.

backend/director/agents/audio_generation.py (3)

3-3: Remove unused import I from re

I isn’t referenced anywhere in this module and triggers Ruff F401.

-from re import I
🧰 Tools
🪛 Ruff (0.8.2)

3-3: re.I imported but unused

Remove unused import: re.I

(F401)


205-207: Typo: “Uploadeed”

Small UI / log polish.

-                        f"Uploadeed generated music to VideoDB with Audio ID {media.get('id')}"
+                        f"Uploaded generated music to VideoDB with Audio ID {media.get('id')}"

174-176: HTTP download lacks error handling & streaming

requests.get(url) without stream=True loads the entire file into memory and no status-code check is done.
Prefer:

resp = requests.get(url, stream=True, timeout=30)
resp.raise_for_status()
with open(output_path, "wb") as f:
    for chunk in resp.iter_content(chunk_size=8192):
        f.write(chunk)

Also applies to: 202-205, 233-236

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 0446582 and a952bf7.

📒 Files selected for processing (8)
  • backend/director/agents/audio_generation.py (6 hunks)
  • backend/director/agents/dubbing.py (4 hunks)
  • backend/director/agents/image_generation.py (5 hunks)
  • backend/director/agents/subtitle.py (2 hunks)
  • backend/director/agents/web_search_agent.py (4 hunks)
  • backend/director/tools/elevenlabs.py (1 hunks)
  • backend/director/tools/videodb_tool.py (1 hunks)
  • backend/requirements.txt (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
backend/director/agents/dubbing.py (3)
backend/director/core/session.py (1)
  • push_update (227-232)
backend/director/tools/videodb_tool.py (3)
  • download (289-291)
  • upload (195-239)
  • dub_video (359-371)
backend/director/handler.py (1)
  • upload (148-149)
🪛 Ruff (0.8.2)
backend/director/agents/audio_generation.py

3-3: re.I imported but unused

Remove unused import: re.I

(F401)

🔇 Additional comments (7)
backend/director/tools/elevenlabs.py (1)

31-52: Good addition of voice mapping dictionary.

The new VOICE_ID_MAP dictionary provides a clean way to map voice IDs to their human-readable names, which will be useful for the videodb integration.

backend/requirements.txt (1)

18-18: Appropriate version update for videodb package.

The update to videodb 0.2.13 is needed to support the new Gen-AI capabilities being integrated in this PR.

backend/director/agents/dubbing.py (2)

13-13: Good addition of videodb to supported engines.

Adding "videodb" to the SUPPORTED_ENGINES list correctly expands the available dubbing options.


146-155: Appropriate implementation of videodb dubbing flow.

The implementation correctly uses the videodb_tool.dub_video method when the "videodb" engine is selected, which aligns with the PR's objective of integrating VideoDB Gen-AI methods.

backend/director/agents/image_generation.py (2)

53-63: Good addition of text_to_image configuration.

The addition of the text_to_image object with engine selection properly supports the new videodb integration alongside the existing flux option.


66-87: Well structured conditional requirements.

The use of allOf with conditional requirements ensures the appropriate parameters are provided based on the job_type, improving validation.

backend/director/tools/videodb_tool.py (1)

352-357:

Details

❓ Verification inconclusive

Check youtube_search() parameter name – possible API mismatch

result_threshold= is passed straight through, but the upstream SDK
(commonly) expects max_results or limit. If mismatched, users will always
get the default number of items.

Please verify against the current videodb SDK and adjust:

-            result_threshold=count,
+            max_results=count,

Confirm youtube_search() parameter in the videodb client

I wasn’t able to find the youtube_search definition in this repo (it’s in the external videodb SDK), so please double-check its signature. If it actually expects max_results (or limit) rather than result_threshold, update the call accordingly:

File: backend/director/tools/videodb_tool.py

-        return self.conn.youtube_search(
-            query=query,
-            result_threshold=count,
-            duration=duration,
-        )
+        return self.conn.youtube_search(
+            query=query,
+            max_results=count,    # adjust to the SDK’s actual parameter name
+            duration=duration,
+        )

Comment on lines 129 to 149
engine = text_to_image.get("engine", "videodb")
if engine == "flux":
flux_output = flux_dev(prompt)
if not flux_output:
image_content.status = MsgStatus.error
image_content.status_message = "Error in generating image."
self.output_message.publish()
error_message = "Agent failed with error in replicate."
return AgentResponse(
status=AgentStatus.ERROR, message=error_message
)
output_image_url = flux_output[0].url
image_content.image = ImageData(url=output_image_url)
image_content.status = MsgStatus.success
image_content.status_message = "Here is your generated image"
else:
generated_image = self.videodb_tool.generate_image(prompt)
image_content.image = ImageData(**generated_image)
image_content.status = MsgStatus.success
image_content.status_message = "Here is your generated image"
elif job_type == "image_to_image":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling for videodb engine.

There's good error handling for the flux engine, but equivalent error handling is missing for the videodb engine. Also, consider adding a validation for unsupported engines.

 engine = text_to_image.get("engine", "videodb")
+if engine not in ["videodb", "flux"]:
+    image_content.status = MsgStatus.error
+    image_content.status_message = f"Unsupported engine: {engine}"
+    self.output_message.publish()
+    return AgentResponse(
+        status=AgentStatus.ERROR, message=f"Unsupported engine: {engine}"
+    )
 if engine == "flux":
     flux_output = flux_dev(prompt)
     if not flux_output:
         image_content.status = MsgStatus.error
         image_content.status_message = "Error in generating image."
         self.output_message.publish()
         error_message = "Agent failed with error in replicate."
         return AgentResponse(
             status=AgentStatus.ERROR, message=error_message
         )
     output_image_url = flux_output[0].url
     image_content.image = ImageData(url=output_image_url)
     image_content.status = MsgStatus.success
     image_content.status_message = "Here is your generated image"
 else:
     generated_image = self.videodb_tool.generate_image(prompt)
+    if not generated_image:
+        image_content.status = MsgStatus.error
+        image_content.status_message = "Error in generating image with videodb."
+        self.output_message.publish()
+        return AgentResponse(
+            status=AgentStatus.ERROR, message="Error in generating image with videodb."
+        )
     image_content.image = ImageData(**generated_image)
     image_content.status = MsgStatus.success
     image_content.status_message = "Here is your generated image"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
engine = text_to_image.get("engine", "videodb")
if engine == "flux":
flux_output = flux_dev(prompt)
if not flux_output:
image_content.status = MsgStatus.error
image_content.status_message = "Error in generating image."
self.output_message.publish()
error_message = "Agent failed with error in replicate."
return AgentResponse(
status=AgentStatus.ERROR, message=error_message
)
output_image_url = flux_output[0].url
image_content.image = ImageData(url=output_image_url)
image_content.status = MsgStatus.success
image_content.status_message = "Here is your generated image"
else:
generated_image = self.videodb_tool.generate_image(prompt)
image_content.image = ImageData(**generated_image)
image_content.status = MsgStatus.success
image_content.status_message = "Here is your generated image"
elif job_type == "image_to_image":
engine = text_to_image.get("engine", "videodb")
+ if engine not in ["videodb", "flux"]:
+ image_content.status = MsgStatus.error
+ image_content.status_message = f"Unsupported engine: {engine}"
+ self.output_message.publish()
+ return AgentResponse(
+ status=AgentStatus.ERROR, message=f"Unsupported engine: {engine}"
+ )
if engine == "flux":
flux_output = flux_dev(prompt)
if not flux_output:
image_content.status = MsgStatus.error
image_content.status_message = "Error in generating image."
self.output_message.publish()
error_message = "Agent failed with error in replicate."
return AgentResponse(
status=AgentStatus.ERROR, message=error_message
)
output_image_url = flux_output[0].url
image_content.image = ImageData(url=output_image_url)
image_content.status = MsgStatus.success
image_content.status_message = "Here is your generated image"
else:
generated_image = self.videodb_tool.generate_image(prompt)
+ if not generated_image:
+ image_content.status = MsgStatus.error
+ image_content.status_message = "Error in generating image with videodb."
+ self.output_message.publish()
+ return AgentResponse(
+ status=AgentStatus.ERROR, message="Error in generating image with videodb."
+ )
image_content.image = ImageData(**generated_image)
image_content.status = MsgStatus.success
image_content.status_message = "Here is your generated image"
elif job_type == "image_to_image":
🤖 Prompt for AI Agents
In backend/director/agents/image_generation.py around lines 129 to 149, add
error handling for the videodb engine similar to the flux engine by checking if
image generation was successful and setting error status and messages
accordingly. Also, include validation for unsupported engine values by returning
an error response if the engine is neither "flux" nor "videodb".

Comment on lines 119 to 129
self.api_key = os.getenv("SERP_API_KEY")
if not self.api_key:
return AgentResponse(
status=AgentStatus.ERROR,
message="SERP_API_KEY environment variable is not set.",
if self.api_key and engine == "serp":
serp_config = search_videos.get("serp_config", {})
search_engine_tool = SerpAPI(
api_key=self.api_key,
base_url=serp_config.get("base_url"),
timeout=serp_config.get("timeout", 10),
)

serp_config = search_videos.get("serp_config", {})
search_engine_tool = SerpAPI(
api_key=self.api_key,
base_url=serp_config.get("base_url"),
timeout=serp_config.get("timeout", 10),
)
else:
search_engine_tool = VideoDBSearchTool()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

❓ Verification inconclusive

Silent fallback from “serp” to “videodb” may surprise callers

If the caller explicitly passes engine="serp" but no SERP_API_KEY is set, the
code quietly switches to VideoDBSearchTool.
This breaks the principle of least astonishment: the user asked for SerpAPI, but
gets completely different results without being told.

Consider one of the following:

  1. Raise an error when engine == "serp" and the key is missing.
  2. Leave the silent fallback but emit a clear warning in the response or logs.

Either way, document the behaviour in the JSON schema.


Clarify fallback behavior for missing SERP_API_KEY

When engine="serp" but no SERP_API_KEY is set, the code silently falls back to VideoDBSearchTool, which can be surprising. Please update as follows:

  • In backend/director/agents/web_search_agent.py (lines 119–129):
    • If engine == "serp" and SERP_API_KEY is unset, raise a clear error (e.g., ValueError("SERP_API_KEY is required for ‘serp’ engine")).
    • Alternatively, keep the fallback but emit a warning (via logging or in the response) indicating that VideoDBSearchTool is used due to the missing key.
  • Update the JSON schema documentation to explicitly describe this behavior and its requirements.
🤖 Prompt for AI Agents
In backend/director/agents/web_search_agent.py around lines 119 to 129, the code
silently falls back to VideoDBSearchTool when engine is "serp" but SERP_API_KEY
is missing, which can confuse users. Fix this by checking if engine is "serp"
and the API key is missing, then raise a ValueError with a clear message stating
that SERP_API_KEY is required for the "serp" engine. Additionally, update the
JSON schema documentation to explicitly state this requirement and the behavior
when the key is missing.

Comment on lines 139 to 146
try:
subtitles = self.translate_transcript_in_parallel(
compact_transcript=compact_transcript,
target_language=target_language,
notes=notes,
subtitles = self.videodb_tool.translate_transcript(
video_id=video_id,
language=target_language,
additional_notes=notes,
)

except Exception as e:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Validate subtitles structure before overlaying

translate_transcript() may return None or an unexpected schema depending on
server errors. add_subtitles_using_timeline assumes a list of dicts with
start, end, text. A quick guard avoids cryptic attribute errors later:

-            subtitles = self.videodb_tool.translate_transcript(...)
+            subtitles = self.videodb_tool.translate_transcript(...)
+            if not isinstance(subtitles, list):
+                raise ValueError("Unexpected subtitle format returned from VideoDB")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try:
subtitles = self.translate_transcript_in_parallel(
compact_transcript=compact_transcript,
target_language=target_language,
notes=notes,
subtitles = self.videodb_tool.translate_transcript(
video_id=video_id,
language=target_language,
additional_notes=notes,
)
except Exception as e:
try:
subtitles = self.videodb_tool.translate_transcript(
video_id=video_id,
language=target_language,
additional_notes=notes,
)
if not isinstance(subtitles, list):
raise ValueError("Unexpected subtitle format returned from VideoDB")
except Exception as e:
🤖 Prompt for AI Agents
In backend/director/agents/subtitle.py around lines 139 to 146, after calling
translate_transcript(), add a validation step to check if subtitles is a list of
dictionaries containing the keys 'start', 'end', and 'text'. If the validation
fails or subtitles is None, handle the case gracefully (e.g., log an error or
skip overlay) before calling add_subtitles_using_timeline to prevent attribute
errors.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🔭 Outside diff range comments (2)
backend/director/agents/audio_generation.py (2)

24-24: ⚠️ Potential issue

SUPPORTED_ENGINES list needs to be updated.

You've added "videodb" to the engine enum in AUDIO_GENERATION_AGENT_PARAMETERS but not to the SUPPORTED_ENGINES list. This will cause the validation check at line 137 to fail when "videodb" is selected.

-SUPPORTED_ENGINES = ["elevenlabs", "beatoven"]
+SUPPORTED_ENGINES = ["elevenlabs", "beatoven", "videodb"]

158-160: ⚠️ Potential issue

Job type restrictions don't allow VideoDB.

The current implementation restricts job types to specific engines:

  • "sound_effect" only with "elevenlabs"
  • "create_music" only with "beatoven"
  • "text_to_speech" only with "elevenlabs"

Yet the engine description at line 35-37 states that "videodb" supports all three job types. You need to update these conditions to allow "videodb" for these job types.

-if engine != "elevenlabs":
+if engine not in ["elevenlabs", "videodb"]:
     raise Exception("Sound effects only supported with elevenlabs")

Similar changes are needed for the other job type validations.

Also applies to: 178-179, 197-198

♻️ Duplicate comments (1)
backend/director/agents/audio_generation.py (1)

190-195: ⚠️ Potential issue

Wrong method call for music generation.

In the "create_music" branch, you're calling generate_sound_effect() instead of generate_music(). This will produce incorrect results or errors when generating music.

-audio_gen_tool.generate_sound_effect(
+audio_gen_tool.generate_music(
     prompt=prompt,
     save_at=output_path,
     duration=duration,
     config={},
 )
🧹 Nitpick comments (6)
backend/director/agents/audio_generation.py (3)

3-3: Remove unused import.

The import of I from re is not used anywhere in the code and should be removed.

-from re import I
🧰 Tools
🪛 Ruff (0.11.9)

3-3: re.I imported but unused

Remove unused import: re.I

(F401)


8-8: Remove unused import.

The requests module is imported but not used in this file.

-import requests
🧰 Tools
🪛 Ruff (0.11.9)

8-8: requests imported but unused

Remove unused import: requests

(F401)


16-16: Remove unused import.

The VOICE_ID_MAP is imported but not used in this file. It's used in videodb_tool.py but not here.

-    VOICE_ID_MAP
🧰 Tools
🪛 Ruff (0.11.9)

16-16: director.tools.elevenlabs.VOICE_ID_MAP imported but unused

Remove unused import: director.tools.elevenlabs.VOICE_ID_MAP

(F401)

backend/director/tools/videodb_tool.py (3)

2-3: Remove unused imports.

The uuid and elevenlabs.save imports are not used in the code and should be removed.

-import uuid
-from elevenlabs import save
🧰 Tools
🪛 Ruff (0.11.9)

2-2: uuid imported but unused

Remove unused import: uuid

(F401)


3-3: elevenlabs.save imported but unused

Remove unused import: elevenlabs.save

(F401)


483-516: Well-designed video generation tool.

The VDBVideoGenerationTool class provides a clean interface for video generation with good error handling:

  • Proper initialization with collection ID
  • Robust file download method with content-type validation
  • Clear error messaging for download failures

However, there's an unnecessary blank line at line 512 that should be removed for consistency.

        download_response = self.videodb_tool.download(video.stream_url)
        download_url = download_response.get("download_url")

-
        self._download_video_file(download_url, save_at)

517-554: Well-implemented audio generation tool.

The VDBAudioGenerationTool class provides a clean interface for audio generation with good error handling:

  • Proper initialization with collection ID
  • Robust file download method
  • Both generate_sound_effect and text_to_speech methods include existence checks after download
  • Good use of VOICE_ID_MAP for voice name mapping in text_to_speech method

One improvement could be to add the same content-type validation for audio downloads that you have for video downloads.

    def _download_audio_file(self, audio_url: str, save_at: str) -> bool:
        os.makedirs(DOWNLOADS_PATH, exist_ok=True)

        try:
            response = requests.get(audio_url, stream=True)
            response.raise_for_status()

+           if not response.headers.get('Content-Type', '').startswith('audio'):
+               raise ValueError(f"The URL does not point to an audio file: {audio_url}")
+
            with open(save_at, 'wb') as file:
                file.write(response.content)

            return True
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between a952bf7 and 236d143.

📒 Files selected for processing (4)
  • backend/director/agents/audio_generation.py (5 hunks)
  • backend/director/agents/text_to_movie.py (4 hunks)
  • backend/director/agents/video_generation.py (5 hunks)
  • backend/director/tools/videodb_tool.py (3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
backend/director/agents/video_generation.py (1)
backend/director/tools/videodb_tool.py (2)
  • VDBVideoGenerationTool (483-515)
  • VideoDBTool (14-481)
🪛 Ruff (0.11.9)
backend/director/tools/videodb_tool.py

2-2: uuid imported but unused

Remove unused import: uuid

(F401)


3-3: elevenlabs.save imported but unused

Remove unused import: elevenlabs.save

(F401)

backend/director/agents/audio_generation.py

3-3: re.I imported but unused

Remove unused import: re.I

(F401)


8-8: requests imported but unused

Remove unused import: requests

(F401)


16-16: director.tools.elevenlabs.VOICE_ID_MAP imported but unused

Remove unused import: director.tools.elevenlabs.VOICE_ID_MAP

(F401)

🔇 Additional comments (17)
backend/director/agents/video_generation.py (5)

9-9: Good addition of the VideoDB tool imports.

The import of VDBVideoGenerationTool and VideoDBTool from director.tools.videodb_tool is required to support the new "videodb" engine option.


22-22: Correctly updated the supported engines.

Good job adding "videodb" to the SUPPORTED_ENGINES list. This enables the engine validation check later in the code.


33-35: Appropriate engine description and default updates.

The description now provides clear guidance on when to use which engine, and setting "videodb" as the default is consistent with the broader VideoDB integration across agents.


44-44: Clear documentation of feature limitations.

Good clarification that image-to-video is only supported by "fal" engine, not by "stabilityai" or "videodb". This prevents potential misuse.


165-168: VideoDB engine implementation is complete.

The conditional branch for the "videodb" engine properly instantiates VDBVideoGenerationTool and sets the appropriate config key.

backend/director/agents/audio_generation.py (3)

12-12: Good addition of VideoDB audio tool imports.

The import of VDBAudioGenerationTool will support the implementation of the "videodb" engine.


35-40: Good engine description and default updates.

The description clearly explains which engines support which job types, and setting "videodb" as the default is consistent with other agents.


151-152: Proper initialization of VDBAudioGenerationTool.

The "videodb" engine case correctly initializes the appropriate tool without requiring an API key.

backend/director/agents/text_to_movie.py (5)

28-28: Good import of VideoDB tools.

The import of VDBAudioGenerationTool, VDBVideoGenerationTool, and VideoDBTool provides the necessary components for implementing "videodb" support.


34-35: Well-structured engine support definition.

Adding "videodb" to SUPPORTED_ENGINES and creating a separate SUPPORTED_AUDIO_ENGINES list provides clear separation of concerns for video and audio generation.


47-54: Good parameter configuration for VideoDB.

Setting "videodb" as the default for both video and audio engines is consistent with the broader VideoDB integration across the system.


199-200: Simple and clean VideoDB video tool implementation.

The conditional branch for "videodb" engine properly instantiates VDBVideoGenerationTool without requiring API keys.


207-214: Well-structured audio engine selection logic.

The audio engine selection logic properly handles both "elevenlabs" (with API key) and "videodb" (default case).

backend/director/tools/videodb_tool.py (4)

348-354: Good implementation of transcript translation.

The translate_transcript method properly delegates to the VideoDB backend for transcript translation, passing through language and additional notes parameters.


355-361: Well-structured YouTube search implementation.

The youtube_search method properly delegates to the VideoDB backend for YouTube searches, passing through appropriate parameters.


362-374: Good implementation of video dubbing.

The dub_video method correctly interfaces with the VideoDB backend for dubbing and returns a properly structured result object with all necessary video metadata.


376-429: Comprehensive media generation methods.

The added generation methods for different media types (image, music, sound effect, voice, video) follow a consistent pattern and return properly structured results with necessary metadata.

raise Exception(f"Failed to save audio at {save_at}")

def text_to_speech(self, text: str, save_at: str, config: dict):
audio = self.collection.generate_voice(text=text, voice_name=VOICE_ID_MAP.get(config.get("voice_id")), config=config)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify voice name mapping functionality.

The text_to_speech method uses VOICE_ID_MAP.get(config.get("voice_id")) to map voice IDs to voice names. This assumes that:

  1. The config dictionary contains a "voice_id" key
  2. The VOICE_ID_MAP contains the corresponding voice name

Let's verify this by checking what happens if the voice ID is not found in the map.


🏁 Script executed:

#!/bin/bash
# Check how VOICE_ID_MAP is defined and how it handles missing keys

# Find the definition of VOICE_ID_MAP
rg -A 5 "VOICE_ID_MAP" --glob "*.py"

# Check how VDBAudioGenerationTool.text_to_speech handles voice_id
cat backend/director/tools/videodb_tool.py | grep -A 10 "text_to_speech"

Length of output: 2332


Ensure voice_id is provided and valid before calling generate_voice.

Currently, VOICE_ID_MAP.get(config.get("voice_id")) will return None if the key is missing or invalid, which may lead to unexpected errors deep in the TTS client. Explicitly validate and fail fast:

• File: backend/director/tools/videodb_tool.py
Line: ~549

Suggested diff:

-       audio = self.collection.generate_voice(
-           text=text,
-           voice_name=VOICE_ID_MAP.get(config.get("voice_id")),
-           config=config
-       )
+       voice_id = config.get("voice_id")
+       if voice_id not in VOICE_ID_MAP:
+           raise ValueError(
+               f"Invalid or missing voice_id '{voice_id}'. " 
+               f"Valid voice IDs are: {list(VOICE_ID_MAP.keys())}"
+           )
+       audio = self.collection.generate_voice(
+           text=text,
+           voice_name=VOICE_ID_MAP[voice_id],
+           config=config
+       )

This ensures we never pass None as the voice_name and provides a clear error message when configuration is incorrect.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
audio = self.collection.generate_voice(text=text, voice_name=VOICE_ID_MAP.get(config.get("voice_id")), config=config)
# Validate that a voice_id is provided and mapped before calling TTS
voice_id = config.get("voice_id")
if voice_id not in VOICE_ID_MAP:
raise ValueError(
f"Invalid or missing voice_id '{voice_id}'. "
f"Valid voice IDs are: {list(VOICE_ID_MAP.keys())}"
)
audio = self.collection.generate_voice(
text=text,
voice_name=VOICE_ID_MAP[voice_id],
config=config
)
🤖 Prompt for AI Agents
In backend/director/tools/videodb_tool.py around line 549, the code uses
VOICE_ID_MAP.get(config.get("voice_id")) which can return None if the voice_id
is missing or invalid, potentially causing errors later. To fix this, add
explicit validation before calling generate_voice: check if "voice_id" exists in
config and if VOICE_ID_MAP contains that voice_id; if not, raise a clear error
or handle the case gracefully. This prevents passing None as voice_name and
ensures fail-fast behavior with informative error messages.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (1)
backend/director/tools/videodb_tool.py (1)

545-550: Ensure voice_id is provided and valid before calling generate_voice.

Currently, VOICE_ID_MAP.get(config.get("voice_id")) will return None if the key is missing or invalid, which may lead to unexpected errors deep in the TTS client. Explicitly validate and fail fast:

-       audio = self.collection.generate_voice(
-           text=text,
-           voice_name=VOICE_ID_MAP.get(config.get("voice_id")),
-           config=config
-       )
+       voice_id = config.get("voice_id")
+       if voice_id not in VOICE_ID_MAP:
+           raise ValueError(
+               f"Invalid or missing voice_id '{voice_id}'. " 
+               f"Valid voice IDs are: {list(VOICE_ID_MAP.keys())}"
+           )
+       audio = self.collection.generate_voice(
+           text=text,
+           voice_name=VOICE_ID_MAP[voice_id],
+           config=config
+       )

Also, similar to generate_sound_effect, this method should return metadata about the generated audio:

def text_to_speech(self, text: str, save_at: str, config: dict):
    voice_id = config.get("voice_id")
    if voice_id not in VOICE_ID_MAP:
        raise ValueError(
            f"Invalid or missing voice_id '{voice_id}'. " 
            f"Valid voice IDs are: {list(VOICE_ID_MAP.keys())}"
        )
    audio = self.collection.generate_voice(
        text=text,
        voice_name=VOICE_ID_MAP[voice_id],
        config=config
    )
    download_url = audio.generate_url()
    self._download_audio_file(download_url, save_at)
    if not os.path.exists(save_at):
        raise Exception(f"Failed to save audio at {save_at}")
+   
+   return {
+       "id": audio.id,
+       "name": audio.name,
+       "collection_id": audio.collection_id,
+       "url": download_url,
+       "local_path": save_at
+   }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 236d143 and b6d2220.

📒 Files selected for processing (2)
  • backend/director/agents/audio_generation.py (5 hunks)
  • backend/director/tools/videodb_tool.py (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/director/agents/audio_generation.py
🧰 Additional context used
🧬 Code Graph Analysis (1)
backend/director/tools/videodb_tool.py (4)
backend/director/tools/elevenlabs.py (2)
  • generate_sound_effect (152-167)
  • text_to_speech (169-188)
backend/director/agents/clone_voice.py (2)
  • _download_video_file (104-125)
  • _download_audio_file (81-102)
backend/director/agents/voice_replacement.py (2)
  • _download_video_file (78-100)
  • _download_audio_file (102-123)
backend/director/tools/fal_video.py (1)
  • text_to_video (74-93)
🔇 Additional comments (4)
backend/director/tools/videodb_tool.py (4)

346-351: LGTM: Good implementation of transcript translation

This implementation correctly delegates transcript translation to the VideoDB API, providing a clean interface to translate transcripts with additional notes.


353-358: LGTM: YouTube search functionality properly implemented

The method correctly passes parameters to the underlying API and maintains a consistent interface.


360-372: LGTM: Video dubbing implementation looks good

The method properly wraps the VideoDB API for dubbing videos and returns a consistent response structure with all necessary video metadata.


374-381: LGTM: Media generation methods are well implemented

All media generation methods (image, music, sound effect, voice, video) follow a consistent pattern of calling the corresponding API method and returning a structured response with relevant metadata.

Also applies to: 383-426

Comment on lines +486 to +504
def _download_video_file(self, video_url: str, save_at: str) -> bool:
os.makedirs(DOWNLOADS_PATH, exist_ok=True)

try:
response = requests.get(video_url, stream=True)
response.raise_for_status()

if not response.headers.get('Content-Type', '').startswith('video'):
raise ValueError(f"The URL does not point to a video file: {video_url}")

with open(save_at, 'wb') as file:
file.write(response.content)

return True

except Exception as e:
print(f"Failed to download {video_url}: {e}")
return False

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve content type validation and error handling in video download

The _download_video_file method has a few issues:

  1. The content type check is too strict - not all video content types start with 'video/'. Some CDNs or servers might return 'application/octet-stream'.
  2. The method returns False on error, but the calling method doesn't check the return value, only if the file exists.
def _download_video_file(self, video_url: str, save_at: str) -> bool:
    os.makedirs(DOWNLOADS_PATH, exist_ok=True)

    try:
        response = requests.get(video_url, stream=True)
        response.raise_for_status()

-       if not response.headers.get('Content-Type', '').startswith('video'):
+       content_type = response.headers.get('Content-Type', '')
+       if not (content_type.startswith('video') or content_type == 'application/octet-stream'):
            raise ValueError(f"The URL does not point to a video file: {video_url}")

        with open(save_at, 'wb') as file:
            file.write(response.content)

        return True

    except Exception as e:
-       print(f"Failed to download {video_url}: {e}")
+       error_msg = f"Failed to download {video_url}: {e}"
+       print(error_msg)
+       raise Exception(error_msg)
-       return False
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def _download_video_file(self, video_url: str, save_at: str) -> bool:
os.makedirs(DOWNLOADS_PATH, exist_ok=True)
try:
response = requests.get(video_url, stream=True)
response.raise_for_status()
if not response.headers.get('Content-Type', '').startswith('video'):
raise ValueError(f"The URL does not point to a video file: {video_url}")
with open(save_at, 'wb') as file:
file.write(response.content)
return True
except Exception as e:
print(f"Failed to download {video_url}: {e}")
return False
def _download_video_file(self, video_url: str, save_at: str) -> bool:
os.makedirs(DOWNLOADS_PATH, exist_ok=True)
try:
response = requests.get(video_url, stream=True)
response.raise_for_status()
content_type = response.headers.get('Content-Type', '')
if not (content_type.startswith('video') or content_type == 'application/octet-stream'):
raise ValueError(f"The URL does not point to a video file: {video_url}")
with open(save_at, 'wb') as file:
file.write(response.content)
return True
except Exception as e:
error_msg = f"Failed to download {video_url}: {e}"
print(error_msg)
raise Exception(error_msg)
🤖 Prompt for AI Agents
In backend/director/tools/videodb_tool.py around lines 486 to 504, improve the
content type validation by allowing common fallback types like
'application/octet-stream' in addition to those starting with 'video/'. Also,
enhance error handling by raising exceptions instead of returning False, so
calling methods can properly handle failures rather than relying solely on file
existence checks.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (5)
backend/director/tools/videodb_tool.py (5)

486-497: 🛠️ Refactor suggestion

Improve error handling and content type validation in download method.

The _download_video_file method lacks proper error handling and has a content type check that may be too strict.

Apply this diff to improve both aspects:

def _download_video_file(self, video_url: str, save_at: str) -> bool:
    os.makedirs(DOWNLOADS_PATH, exist_ok=True)

+   try:
        response = requests.get(video_url, stream=True)
        response.raise_for_status()

-       if not response.headers.get('Content-Type', '').startswith('video'):
+       content_type = response.headers.get('Content-Type', '')
+       if not (content_type.startswith('video') or content_type == 'application/octet-stream'):
            raise ValueError(f"The URL does not point to a video file: {video_url}")

        with open(save_at, 'wb') as file:
            file.write(response.content)

+       return True
+   except Exception as e:
+       error_msg = f"Failed to download {video_url}: {e}"
+       print(error_msg)
+       raise Exception(error_msg)

499-507: 🛠️ Refactor suggestion

Add return value to text_to_video method.

The text_to_video method doesn't return any information about the generated video, unlike other methods in the file.

Apply this diff to return consistent metadata:

def text_to_video(self, prompt: str, save_at: str, duration: float, config: dict):
    video = self.collection.generate_video(prompt=prompt, duration=duration)
    download_response = self.videodb_tool.download(video.stream_url)
    download_url = download_response.get("download_url")

    self._download_video_file(download_url, save_at)
    if not os.path.exists(save_at):
        raise Exception(f"Failed to save video at {save_at}")
+   
+   return {
+       "id": video.id,
+       "name": video.name,
+       "collection_id": video.collection_id,
+       "stream_url": video.stream_url,
+       "length": video.length,
+       "local_path": save_at
+   }

514-521: 🛠️ Refactor suggestion

Improve error handling in audio download method.

The _download_audio_file method lacks proper error handling.

Apply this diff to add proper error handling:

def _download_audio_file(self, audio_url: str, save_at: str) -> bool:
    os.makedirs(DOWNLOADS_PATH, exist_ok=True)
+   try:
        response = requests.get(audio_url, stream=True)
        response.raise_for_status()

        with open(save_at, 'wb') as file:
            file.write(response.content)
+       
+       return True
+   except Exception as e:
+       error_msg = f"Failed to download {audio_url}: {e}"
+       print(error_msg)
+       raise Exception(error_msg)

523-531: 🛠️ Refactor suggestion

Add return value to generate_sound_effect method.

The generate_sound_effect method doesn't return any metadata about the generated audio.

Apply this diff to return consistent metadata:

def generate_sound_effect(
    self, prompt: str, save_at: str, duration: float, config: dict
):        
    audio = self.collection.generate_sound_effect(prompt=prompt, duration=duration, config=config)
    download_url = audio.generate_url()
    self._download_audio_file(download_url, save_at)
    if not os.path.exists(save_at):
        raise Exception(f"Failed to save audio at {save_at}")
+   
+   return {
+       "id": audio.id,
+       "name": audio.name,
+       "collection_id": audio.collection_id,
+       "url": download_url,
+       "local_path": save_at
+   }

532-537: 🛠️ Refactor suggestion

Ensure voice_id is provided and valid before calling generate_voice.

Currently, VOICE_ID_MAP.get(config.get("voice_id")) will return None if the key is missing or invalid, which may lead to unexpected errors.

Apply this diff to add validation:

def text_to_speech(self, text: str, save_at: str, config: dict):
-   audio = self.collection.generate_voice(text=text, voice_name=VOICE_ID_MAP.get(config.get("voice_id")), config=config)
+   voice_id = config.get("voice_id")
+   if voice_id not in VOICE_ID_MAP:
+       raise ValueError(
+           f"Invalid or missing voice_id '{voice_id}'. " 
+           f"Valid voice IDs are: {list(VOICE_ID_MAP.keys())}"
+       )
+   audio = self.collection.generate_voice(
+       text=text,
+       voice_name=VOICE_ID_MAP[voice_id],
+       config=config
+   )
    download_url = audio.generate_url()
    self._download_audio_file(download_url, save_at)
    if not os.path.exists(save_at):
        raise Exception(f"Failed to save audio at {save_at}")
+   
+   return {
+       "id": audio.id,
+       "name": audio.name,
+       "collection_id": audio.collection_id,
+       "url": download_url,
+       "local_path": save_at
+   }
🧹 Nitpick comments (1)
backend/director/tools/videodb_tool.py (1)

481-538: Add docstrings to new classes and methods.

Unlike the methods in VideoDBTool, the new classes and their methods don't have docstrings, which hinders code readability and maintainability.

Consider adding docstrings that explain:

  • The purpose of each class and method
  • Parameter descriptions
  • Return value descriptions
  • Any exceptions that might be raised

Example for the text_to_speech method:

def text_to_speech(self, text: str, save_at: str, config: dict):
    """
    Converts text to speech using the VideoDB voice generation API.
    
    Args:
        text (str): The text to convert to speech.
        save_at (str): Local file path where the audio will be saved.
        config (dict): Configuration options including:
            - voice_id (str): ID of the voice to use.
            
    Returns:
        dict: Metadata about the generated audio including ID, name,
              collection ID, URL, and local path.
              
    Raises:
        ValueError: If the voice_id is invalid or missing.
        Exception: If the audio file cannot be downloaded or saved.
    """
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between b6d2220 and 6b8123e.

📒 Files selected for processing (4)
  • backend/director/agents/audio_generation.py (3 hunks)
  • backend/director/agents/dubbing.py (4 hunks)
  • backend/director/tools/elevenlabs.py (1 hunks)
  • backend/director/tools/videodb_tool.py (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • backend/director/tools/elevenlabs.py
  • backend/director/agents/dubbing.py
  • backend/director/agents/audio_generation.py
🧰 Additional context used
🧬 Code Graph Analysis (1)
backend/director/tools/videodb_tool.py (2)
backend/director/tools/elevenlabs.py (2)
  • generate_sound_effect (152-167)
  • text_to_speech (169-188)
backend/director/agents/voice_replacement.py (2)
  • _download_video_file (78-100)
  • _download_audio_file (102-123)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
backend/director/tools/videodb_tool.py (1)

515-522: ⚠️ Potential issue

Ensure voice_id validation in text_to_speech method.

Similar to a previously reported issue, you need to validate the voice_id before passing it to the API.

def text_to_speech(self, text: str, save_at: str, config: dict):
-    audio = self.collection.generate_voice(text=text, voice_name=VOICE_ID_MAP.get(config.get("voice_id")), config=config)
+    voice_id = config.get("voice_id")
+    if voice_id not in VOICE_ID_MAP:
+        raise ValueError(
+            f"Invalid or missing voice_id '{voice_id}'. " 
+            f"Valid voice IDs are: {list(VOICE_ID_MAP.keys())}"
+        )
+    audio = self.collection.generate_voice(
+        text=text,
+        voice_name=VOICE_ID_MAP[voice_id],
+        config=config
+    )
     return {
         "id": audio.id,
         "collection_id": audio.collection_id,
         "name": audio.name,
         "url": audio.generate_url()
     }
🧹 Nitpick comments (3)
backend/director/tools/videodb_tool.py (3)

10-10: Remove unused import.

The DOWNLOADS_PATH import is never used in this file.

-from director.constants import DOWNLOADS_PATH
🧰 Tools
🪛 Ruff (0.11.9)

10-10: director.constants.DOWNLOADS_PATH imported but unused

Remove unused import: director.constants.DOWNLOADS_PATH

(F401)


353-359: Consider adding additional parameters for YouTube search.

The YouTube search implementation is good, but consider adding parameters for filtering by upload date or video type for more advanced search capabilities.


392-404: Add method documentation.

This method should include docstring documentation explaining parameters and return values.

 def generate_sound_effect(self, prompt, duration, config):
+    """
+    Generate a sound effect based on prompt.
+    
+    Args:
+        prompt: Text description of the sound effect
+        duration: Length of the sound effect in seconds
+        config: Additional configuration parameters
+        
+    Returns:
+        Dictionary containing the generated sound effect metadata
+    """
     sound_effect = self.collection.generate_sound_effect(
         prompt=prompt,
         duration=duration,
         config=config,
     )
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 64a3a37 and 1d260b5.

📒 Files selected for processing (4)
  • backend/director/agents/audio_generation.py (6 hunks)
  • backend/director/agents/text_to_movie.py (7 hunks)
  • backend/director/agents/video_generation.py (8 hunks)
  • backend/director/tools/videodb_tool.py (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • backend/director/agents/video_generation.py
  • backend/director/agents/audio_generation.py
  • backend/director/agents/text_to_movie.py
🧰 Additional context used
🪛 Ruff (0.11.9)
backend/director/tools/videodb_tool.py

10-10: director.constants.DOWNLOADS_PATH imported but unused

Remove unused import: director.constants.DOWNLOADS_PATH

(F401)

🔇 Additional comments (3)
backend/director/tools/videodb_tool.py (3)

346-352: LGTM: Translation functionality properly implemented.

The translate_transcript method provides a clean wrapper around the VideoDb API method, passing through the necessary parameters.


360-373: LGTM: Clean implementation of video dubbing.

The method returns a comprehensive dictionary with all relevant video metadata.


374-382: LGTM: Image generation implementation looks good.

The method correctly returns the necessary metadata for the generated image.

Comment on lines +405 to +417
def generate_voice(self, text, voice_name, config):
voice = self.collection.generate_voice(
text=text,
voice_name=voice_name,
config=config,
)
return {
"id": voice.id,
"name": voice.name,
"collection_id": voice.collection_id,
"url": voice.generate_url(),
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add parameter validation for voice name.

The method should validate that the voice_name parameter is not None before calling the API.

 def generate_voice(self, text, voice_name, config):
+    if voice_name is None:
+        raise ValueError("voice_name cannot be None or empty")
     voice = self.collection.generate_voice(
         text=text,
         voice_name=voice_name,
         config=config,
     )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def generate_voice(self, text, voice_name, config):
voice = self.collection.generate_voice(
text=text,
voice_name=voice_name,
config=config,
)
return {
"id": voice.id,
"name": voice.name,
"collection_id": voice.collection_id,
"url": voice.generate_url(),
}
def generate_voice(self, text, voice_name, config):
if voice_name is None:
raise ValueError("voice_name cannot be None or empty")
voice = self.collection.generate_voice(
text=text,
voice_name=voice_name,
config=config,
)
return {
"id": voice.id,
"name": voice.name,
"collection_id": voice.collection_id,
"url": voice.generate_url(),
}
🤖 Prompt for AI Agents
In backend/director/tools/videodb_tool.py around lines 405 to 417, the
generate_voice method lacks validation for the voice_name parameter. Add a check
at the start of the method to ensure voice_name is not None, and raise an
appropriate exception or handle the error if it is None before proceeding to
call the API.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🔭 Outside diff range comments (1)
backend/director/agents/audio_generation.py (1)

193-207: 🛠️ Refactor suggestion

Missing text_to_speech input validation

text_to_speech may be None (e.g. if the caller forgets the section).
Accessing text_to_speech.get(...) will then raise AttributeError.
Add an explicit guard similar to the sound_effect/create_music branches.

-elif job_type == "text_to_speech":
+elif job_type == "text_to_speech":
+    if text_to_speech is None:
+        raise ValueError("`text_to_speech` section is required for this job type")
♻️ Duplicate comments (2)
backend/director/agents/audio_generation.py (1)

174-191: Beatoven path still calls generate_sound_effect instead of generate_music

The earlier review already highlighted that Beatoven provides a dedicated generate_music method, yet the code path for create_music (line 186) still invokes audio_gen_tool.generate_sound_effect.
This will either raise on the Beatoven side or return a very short SFX instead of a music track.

backend/director/tools/videodb_tool.py (1)

552-556: Validate voice_id before mapping

Previous review already requested a guard to ensure voice_id exists in
VOICE_ID_MAP. The risk of passing None as voice_name is still present.

🧹 Nitpick comments (3)
backend/director/tools/videodb_tool.py (3)

486-497: _download_video_file declares a return value but returns None

The signature says -> bool, yet there is no return statement.
Either return True/False or change the annotation.

Minor, but mismatching type hints hinder static analysis.


525-532: Large files are read into memory at once

Both _download_audio_file and _download_video_file use response.content,
loading the whole payload in RAM. For long videos or music this can blow up
memory usage. Stream to disk in chunks (see iter_content pattern used
elsewhere in the codebase).


424-426: generate_video creates a fresh stream every call

Returning video.generate_stream() may trigger an expensive transcoding every
time the metadata is requested. Consider returning the already-available
video.stream_url and let callers generate a stream only when needed.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 1d260b5 and 639dde2.

📒 Files selected for processing (5)
  • backend/director/agents/audio_generation.py (7 hunks)
  • backend/director/agents/dubbing.py (4 hunks)
  • backend/director/agents/text_to_movie.py (8 hunks)
  • backend/director/agents/video_generation.py (9 hunks)
  • backend/director/tools/videodb_tool.py (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • backend/director/agents/dubbing.py
  • backend/director/agents/video_generation.py
  • backend/director/agents/text_to_movie.py
🧰 Additional context used
🧬 Code Graph Analysis (2)
backend/director/agents/audio_generation.py (4)
backend/director/tools/videodb_tool.py (6)
  • VDBAudioGenerationTool (520-566)
  • VideoDBTool (12-479)
  • generate_sound_effect (392-403)
  • generate_sound_effect (534-550)
  • text_to_speech (552-566)
  • upload (196-240)
backend/director/tools/elevenlabs.py (3)
  • ElevenLabsTool (139-274)
  • generate_sound_effect (152-167)
  • text_to_speech (169-188)
backend/director/tools/beatoven.py (2)
  • BeatovenTool (13-70)
  • generate_sound_effect (24-70)
backend/director/handler.py (1)
  • upload (148-149)
backend/director/tools/videodb_tool.py (5)
backend/director/tools/elevenlabs.py (2)
  • generate_sound_effect (152-167)
  • text_to_speech (169-188)
backend/director/agents/voice_replacement.py (2)
  • _download_video_file (78-100)
  • _download_audio_file (102-123)
backend/director/tools/fal_video.py (1)
  • text_to_video (74-93)
backend/director/tools/kling.py (1)
  • text_to_video (129-187)
backend/director/tools/stabilityai.py (1)
  • text_to_video (50-135)
🪛 Ruff (0.11.9)
backend/director/tools/videodb_tool.py

498-498: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)

Comment on lines +498 to +503
def text_to_video(self, prompt: str, save_at: str, duration: float, config: dict = {}):
media = self.collection.generate_video(prompt=prompt, duration=duration)

download_response = self.videodb_tool.download(media.stream_url)
download_url = download_response.get("download_url")

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid mutable default argument for config

config={} is instantiated only once at function-definition time, causing
state-sharing between calls. Use None and replace inside.

-def text_to_video(self, prompt: str, save_at: str, duration: float, config: dict = {}):
+def text_to_video(
+    self,
+    prompt: str,
+    save_at: str,
+    duration: float,
+    config: dict | None = None,
+):
+    config = config or {}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def text_to_video(self, prompt: str, save_at: str, duration: float, config: dict = {}):
media = self.collection.generate_video(prompt=prompt, duration=duration)
download_response = self.videodb_tool.download(media.stream_url)
download_url = download_response.get("download_url")
def text_to_video(
self,
prompt: str,
save_at: str,
duration: float,
config: dict | None = None,
):
config = config or {}
media = self.collection.generate_video(prompt=prompt, duration=duration)
download_response = self.videodb_tool.download(media.stream_url)
download_url = download_response.get("download_url")
🧰 Tools
🪛 Ruff (0.11.9)

498-498: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)

🤖 Prompt for AI Agents
In backend/director/tools/videodb_tool.py around lines 498 to 503, the function
text_to_video uses a mutable default argument config={} which can cause state
sharing between calls. Change the default value of config to None and inside the
function, check if config is None and if so, assign it to an empty dictionary to
avoid unintended shared state.

@video-db video-db deleted a comment from coderabbitai bot May 21, 2025
"type": "string",
"description": """The engine to use for audio generation:
"description": """The engine to use for audio generation. Default is 'videodb'.`:
- videodb: supports text_to_speech, sound_effect
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add create_music here

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also the (elevenlab only) can be updated to respective supported engines at line #44 in this file.

The reason why we added because we were running into some edge-case and llm was selecting unsupported engines/job_types

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the create_music feature too by adding generate_music in videodb. I have renamed the Beatoven's method to generate_music for consistency with job type.

Also have updated the supported engines

)
msg = "Uploaded generated audio to VideoDB"
if media is None:
# Upload to VideoDB
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line no #205-#207 can be moved inside this if block.

self.output_message.actions.append(
f"Generated audio saved at {output_path}"
)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


download_url = audio.generate_url()
self._download_audio_file(download_url, save_at)
if not os.path.exists(save_at):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the function never uses the save_at path, throwing an exception when the path is missing only disrupts the user’s workflow. It’s safe to drop that conditional block entirely.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

elif job_type == "text_to_speech":
if engine != "elevenlabs":
raise Exception("Text to speech only supported with elevenlabs")
if engine != "elevenlabs" and engine != "videodb":
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor readability improv.

if engine not in ["elevenlabs", "videodb"]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -251,16 +259,18 @@ def run(
)
self.output_message.push_update()

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if any pointer from audio_generation.py is applicable here, we can add those too

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (4)
backend/director/tools/videodb_tool.py (4)

405-416: Voice name validation still missing.

This is the same issue flagged in previous reviews. The method should validate that voice_name is not None before calling the API.


486-496: Video download method needs error handling improvements.

The method lacks proper exception handling and the content type validation is too restrictive. This should be addressed as mentioned in previous reviews.


525-531: Audio download method needs exception handling.

Similar to the video download method, this should raise exceptions instead of returning boolean values for better error handling.


550-562: Voice ID validation still needed.

This method uses VOICE_ID_MAP.get(config.get("voice_id")) which can return None, leading to the same issue flagged in previous reviews for the main generate_voice method.

🧹 Nitpick comments (1)
backend/director/tools/beatoven.py (1)

24-29: Update docstring to match new method name.

The method was renamed to generate_music but the docstring still says "Generate a sound effect using Beatoven API".

Apply this diff to update the docstring:

    def generate_music(
        self, prompt: str, save_at: str, duration: float
    ):
        """
-        Generate a sound effect using Beatoven API
+        Generate music using Beatoven API
        """
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 639dde2 and cb63768.

📒 Files selected for processing (3)
  • backend/director/agents/audio_generation.py (8 hunks)
  • backend/director/tools/beatoven.py (1 hunks)
  • backend/director/tools/videodb_tool.py (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/director/agents/audio_generation.py
🧰 Additional context used
🪛 Ruff (0.11.9)
backend/director/tools/videodb_tool.py

498-498: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)

🔇 Additional comments (10)
backend/director/tools/videodb_tool.py (9)

9-10: Import statements look good.

The new imports for VOICE_ID_MAP and DOWNLOADS_PATH are properly structured and support the new functionality being added.


346-351: Clean wrapper method for transcript translation.

The method provides a clear interface for translating video transcripts with optional additional notes.


353-358: YouTube search functionality implemented correctly.

Good parameter mapping and clean interface for YouTube video searches.


360-372: Dubbing method returns comprehensive metadata.

The method properly structures the response with all relevant video information after dubbing.


374-426: Media generation methods provide consistent interfaces.

All the new generation methods (generate_image, generate_music, generate_sound_effect, generate_voice, generate_video) follow a consistent pattern and return structured metadata.


481-484: VDBVideoGenerationTool initialization is well-structured.

Clean initialization pattern that properly sets up both the tool and collection references.


520-523: VDBAudioGenerationTool initialization follows good patterns.

Consistent with the video generation tool initialization approach.


534-548: Sound effect generation method is well-implemented.

Good structure with proper metadata return and file validation.


564-576: Music generation method implemented correctly.

Clean implementation with proper file download and metadata return.

backend/director/tools/beatoven.py (1)

24-26: Method rename aligns with standardized naming.

The rename from generate_sound_effect to generate_music and removal of the unused config parameter improves consistency across the codebase.

Comment on lines +498 to +518
def text_to_video(self, prompt: str, save_at: str, duration: float, config: dict = {}):
media = self.collection.generate_video(prompt=prompt, duration=duration)

download_response = self.videodb_tool.download(media.stream_url)
download_url = download_response.get("download_url")

self._download_video_file(download_url, save_at)
if not os.path.exists(save_at):
raise Exception(f"Failed to save video at {save_at}")

video_dict = {
"id": media.id,
"collection_id": media.collection_id,
"stream_url": media.stream_url,
"player_url": media.player_url,
"name": media.name,
"description": media.description,
"thumbnail_url": media.thumbnail_url,
"length": media.length,
}
return video_dict
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix mutable default argument and improve error handling.

The method has a mutable default argument issue flagged by static analysis, and based on past reviews, error handling should be improved.

Apply this diff to fix the mutable default argument:

-def text_to_video(self, prompt: str, save_at: str, duration: float, config: dict = {}):
+def text_to_video(self, prompt: str, save_at: str, duration: float, config: dict | None = None):
+    config = config or {}

Additionally, the method should handle download failures more gracefully as mentioned in previous reviews.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def text_to_video(self, prompt: str, save_at: str, duration: float, config: dict = {}):
media = self.collection.generate_video(prompt=prompt, duration=duration)
download_response = self.videodb_tool.download(media.stream_url)
download_url = download_response.get("download_url")
self._download_video_file(download_url, save_at)
if not os.path.exists(save_at):
raise Exception(f"Failed to save video at {save_at}")
video_dict = {
"id": media.id,
"collection_id": media.collection_id,
"stream_url": media.stream_url,
"player_url": media.player_url,
"name": media.name,
"description": media.description,
"thumbnail_url": media.thumbnail_url,
"length": media.length,
}
return video_dict
def text_to_video(self, prompt: str, save_at: str, duration: float, config: dict | None = None):
config = config or {}
media = self.collection.generate_video(prompt=prompt, duration=duration)
download_response = self.videodb_tool.download(media.stream_url)
download_url = download_response.get("download_url")
self._download_video_file(download_url, save_at)
if not os.path.exists(save_at):
raise Exception(f"Failed to save video at {save_at}")
video_dict = {
"id": media.id,
"collection_id": media.collection_id,
"stream_url": media.stream_url,
"player_url": media.player_url,
"name": media.name,
"description": media.description,
"thumbnail_url": media.thumbnail_url,
"length": media.length,
}
return video_dict
🧰 Tools
🪛 Ruff (0.11.9)

498-498: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)

🤖 Prompt for AI Agents
In backend/director/tools/videodb_tool.py around lines 498 to 518, fix the
mutable default argument by changing the default value of the config parameter
from an empty dictionary to None and then initializing it inside the method.
Also, improve error handling by checking if the download_response or
download_url is valid before proceeding, and raise a clear exception or handle
the failure gracefully if the download fails or the URL is missing.

@0xrohitgarg 0xrohitgarg merged commit 024f316 into video-db:main May 27, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants