Skip to content

fix: upload rendered video to fal.ai storage instead of base64#84

Merged
sweetmantech merged 2 commits intomainfrom
fix/content-video-oom-fal-storage
Mar 12, 2026
Merged

fix: upload rendered video to fal.ai storage instead of base64#84
sweetmantech merged 2 commits intomainfrom
fix/content-video-oom-fal-storage

Conversation

@sweetmantech
Copy link
Copy Markdown
Contributor

@sweetmantech sweetmantech commented Mar 12, 2026

Summary

  • Uploads rendered MP4 to fal.ai storage instead of converting to base64 data URL
  • Returns an HTTPS URL (videoSourceUrl) instead of a data:video/mp4;base64,... string
  • Eliminates ~2.5x memory inflation that caused TASK_PROCESS_OOM_KILLED on small-1x (512MB)

Why

The base64 encoding held the raw buffer (~20MB) + base64 string (~27MB) + serialization copy (~27MB) simultaneously, exceeding the 512MB limit. With fal.ai storage upload, peak memory is just the raw buffer (~20MB) which streams out immediately.

Test plan

  • Updated test: asserts videoSourceUrl is an HTTPS URL, not base64
  • RED: test failed against old implementation
  • GREEN: test passes with new implementation
  • All 147 tests pass
  • Deploy and verify create-content task completes without OOM

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Rendered videos are now stored and accessed via cloud storage URL instead of embedded format.
  • Tests

    • Updated test expectations to reflect the new video storage URL format.

The base64 data URL conversion was causing OOM crashes on small-1x
(512MB). Now uploads the rendered MP4 to fal.ai storage and returns
an HTTPS URL, eliminating the ~2.5x memory inflation from base64.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 12, 2026

Warning

Rate limit exceeded

@sweetmantech has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 52 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 746d789b-4b8e-45f6-bfbc-230529846820

📥 Commits

Reviewing files that changed from the base of the PR and between 086d677 and a02c122.

📒 Files selected for processing (1)
  • src/content/renderFinalVideo.ts
📝 Walkthrough

Walkthrough

The changes replace a data URL-based video output with fal.ai storage uploads. The RenderFinalVideoOutput interface is updated to return a storage URL instead of a data URL. The implementation now uploads the final MP4 buffer to fal.ai storage and returns the resulting storage URL, with all dependent code updated accordingly.

Changes

Cohort / File(s) Summary
Core Storage Upload
src/content/renderFinalVideo.ts
Added fal.ai storage upload functionality to final video rendering. Replaced dataUrl output with videoUrl returned from storage upload. Updated RenderFinalVideoOutput interface signature to reflect the new return type.
Dependent Code Updates
src/tasks/createContentTask.ts, src/tasks/__tests__/createContentTask.test.ts
Updated references from finalVideo.dataUrl to finalVideo.videoUrl and adjusted test mock/assertion to use storage URL format instead of data URL.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 A video once bound in base64 chains,
Now floats free in storage's domain,
Fal.ai uploads with elegant grace,
URLs replace data's lengthy space,
The refactor hops swift and so clean! 🎬

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely summarizes the main change: replacing base64 data URLs with fal.ai storage URLs to fix memory issues.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/content-video-oom-fal-storage
📝 Coding Plan
  • Generate coding plan for human review comments

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

Comment @coderabbitai help to get the list of available commands and usage tips.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/content/renderFinalVideo.ts`:
- Around line 191-205: The function currently returns { videoUrl, mimeType,
sizeBytes } but the type/contract and docstring (RenderFinalVideoOutput) still
reference dataUrl; update the output contract and consumers to match the new
storage URL flow: change RenderFinalVideoOutput to include videoUrl (string)
instead of dataUrl and update its docstring to describe an HTTPS URL, and then
update any callers (e.g., the consumer in src/content/testPipeline.ts that does
result.dataUrl.split(",")[1]) to read result.videoUrl (and stop treating it as a
base64 data URL). Ensure all references to dataUrl are removed or migrated to
videoUrl so types and runtime behavior align.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2fd43df8-7c6a-46a4-95cc-34e993f5dab4

📥 Commits

Reviewing files that changed from the base of the PR and between 1371b06 and 086d677.

📒 Files selected for processing (3)
  • src/content/renderFinalVideo.ts
  • src/tasks/__tests__/createContentTask.test.ts
  • src/tasks/createContentTask.ts

Comment on lines +191 to +205
// Read the final video and upload to fal.ai storage (avoids base64 OOM)
const finalBuffer = await readFile(outputPath);
const mimeType = "video/mp4";
const dataUrl = `data:${mimeType};base64,${finalBuffer.toString("base64")}`;
const sizeBytes = finalBuffer.length;

logger.log("Final video rendered", { sizeBytes: finalBuffer.length });
logger.log("Final video rendered, uploading to fal.ai storage", { sizeBytes });

const videoFile = new File([finalBuffer], "final-video.mp4", { type: "video/mp4" });
const videoUrl = await fal.storage.upload(videoFile);

logger.log("Final video uploaded to fal.ai storage", { videoUrl, sizeBytes });

return {
dataUrl,
mimeType,
sizeBytes: finalBuffer.length,
videoUrl,
mimeType: "video/mp4",
sizeBytes,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix the output contract drift (dataUrl vs videoUrl) before merge.

Lines 203-205 return videoUrl, but RenderFinalVideoOutput still defines dataUrl and the docstring still describes data-URL output. This creates a broken typed contract and leaves at least one consumer (src/content/testPipeline.ts:255-267) using result.dataUrl.split(",")[1], which will fail with an HTTPS URL.

Proposed fix
 export interface RenderFinalVideoOutput {
-  /** Data URL of the final rendered video */
-  dataUrl: string;
+  /** HTTPS URL of the final rendered video in fal.ai storage */
+  videoUrl: string;
   /** MIME type */
   mimeType: string;
   /** Size in bytes */
   sizeBytes: number;
 }

 /**
  * Renders the final social post video using ffmpeg:
  *   1. Downloads the AI-generated video
  *   2. Crops 16:9 → 9:16 (portrait for TikTok/Reels)
  *   3. Overlays audio clip from the song (unless lipsync mode)
  *   4. Overlays caption text (white with black stroke, bottom center)
- *   5. Returns the final video as a data URL
+ *   5. Uploads the final MP4 to fal.ai storage and returns an HTTPS URL
  */
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/content/renderFinalVideo.ts` around lines 191 - 205, The function
currently returns { videoUrl, mimeType, sizeBytes } but the type/contract and
docstring (RenderFinalVideoOutput) still reference dataUrl; update the output
contract and consumers to match the new storage URL flow: change
RenderFinalVideoOutput to include videoUrl (string) instead of dataUrl and
update its docstring to describe an HTTPS URL, and then update any callers
(e.g., the consumer in src/content/testPipeline.ts that does
result.dataUrl.split(",")[1]) to read result.videoUrl (and stop treating it as a
base64 data URL). Ensure all references to dataUrl are removed or migrated to
videoUrl so types and runtime behavior align.

@sweetmantech sweetmantech merged commit b40dc32 into main Mar 12, 2026
2 checks passed
@sweetmantech sweetmantech deleted the fix/content-video-oom-fal-storage branch March 12, 2026 23:00
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.

1 participant