Implement Remotion-powered Captioning Service for All Vinci Clips Videos#47
Draft
Implement Remotion-powered Captioning Service for All Vinci Clips Videos#47
Conversation
Co-authored-by: sdntsng <19806109+sdntsng@users.noreply.github.com>
Comment on lines
+54
to
+170
| router.post('/generate', async (req, res) => { | ||
| try { | ||
| const { | ||
| transcriptId, | ||
| style = 'modern', | ||
| startTime, | ||
| endTime, | ||
| captionSettings = {} | ||
| } = req.body; | ||
|
|
||
| if (!transcriptId) { | ||
| return res.status(400).json({ | ||
| success: false, | ||
| error: 'Transcript ID is required' | ||
| }); | ||
| } | ||
|
|
||
| // Get transcript data | ||
| const transcript = await Transcript.findById(transcriptId); | ||
| if (!transcript) { | ||
| return res.status(404).json({ | ||
| success: false, | ||
| error: 'Transcript not found' | ||
| }); | ||
| } | ||
|
|
||
| if (transcript.status !== 'completed') { | ||
| return res.status(400).json({ | ||
| success: false, | ||
| error: 'Transcript not ready for caption generation' | ||
| }); | ||
| } | ||
|
|
||
| // Prepare caption data from transcript | ||
| let words = []; | ||
| if (transcript.transcript && Array.isArray(transcript.transcript)) { | ||
| words = convertTranscriptToWords(transcript.transcript, startTime, endTime); | ||
| } | ||
|
|
||
| if (words.length === 0) { | ||
| return res.status(400).json({ | ||
| success: false, | ||
| error: 'No caption data found in specified range' | ||
| }); | ||
| } | ||
|
|
||
| // Create Remotion composition input data | ||
| const compositionInput = { | ||
| videoUrl: transcript.videoUrl, | ||
| words: words, | ||
| style: style, | ||
| settings: { | ||
| fontSize: captionSettings.fontSize || 48, | ||
| fontFamily: captionSettings.fontFamily || 'Arial', | ||
| color: captionSettings.color || '#FFFFFF', | ||
| backgroundColor: captionSettings.backgroundColor || 'rgba(0,0,0,0.7)', | ||
| position: captionSettings.position || 'bottom', | ||
| ...captionSettings | ||
| }, | ||
| duration: calculateDuration(words), | ||
| startTime: startTime || 0, | ||
| endTime: endTime || transcript.duration | ||
| }; | ||
|
|
||
| // Bundle and render the Remotion composition | ||
| const bundleLocation = await bundle({ | ||
| entryPoint: path.join(__dirname, '../remotion/composition.tsx'), | ||
| webpackOverride: (config) => config, | ||
| }); | ||
|
|
||
| const compositions = await getCompositions(bundleLocation, { | ||
| inputProps: compositionInput, | ||
| }); | ||
|
|
||
| const composition = compositions.find((c) => c.id === 'CaptionedVideo'); | ||
| if (!composition) { | ||
| throw new Error('Captioned video composition not found'); | ||
| } | ||
|
|
||
| // Create output directory | ||
| const outputDir = path.join(__dirname, '../../uploads/remotion-captions'); | ||
| if (!fs.existsSync(outputDir)) { | ||
| fs.mkdirSync(outputDir, { recursive: true }); | ||
| } | ||
|
|
||
| const outputFileName = `${transcriptId}_remotion_${style}_${Date.now()}.mp4`; | ||
| const outputPath = path.join(outputDir, outputFileName); | ||
|
|
||
| // Render the video | ||
| await renderMedia({ | ||
| composition, | ||
| serveUrl: bundleLocation, | ||
| codec: 'h264', | ||
| outputLocation: outputPath, | ||
| inputProps: compositionInput, | ||
| }); | ||
|
|
||
| const captionedVideoUrl = `/uploads/remotion-captions/${outputFileName}`; | ||
|
|
||
| res.json({ | ||
| success: true, | ||
| captionedVideoUrl, | ||
| style, | ||
| wordCount: words.length, | ||
| outputPath, | ||
| message: 'Remotion captioned video generated successfully' | ||
| }); | ||
|
|
||
| } catch (error) { | ||
| console.error('Remotion caption generation error:', error); | ||
| res.status(500).json({ | ||
| success: false, | ||
| error: 'Failed to generate Remotion captioned video', | ||
| details: error.message | ||
| }); | ||
| } | ||
| }); |
Check failure
Code scanning / CodeQL
Missing rate limiting High
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 6 months ago
The best way to fix the problem is to apply a rate-limiting middleware to the /generate POST route (or, optionally, to all routes in this file/router). The most standard and robust approach is to use the express-rate-limit NPM package. The fix involves:
- Importing
express-rate-limitat the top of the file. - Creating a rate limiter configuration suitable for expensive operations (e.g., allowing, say, 5 requests per minute per IP to
/generate). - Applying the middleware specifically to the
/generateroute using Express' syntax:router.post('/generate', limiter, async (req, res) => { ... });
Only the affected file,backend/src/routes/remotion-captions.js, will need to be modified:
- Add import for
express-rate-limitafter existing imports. - Add the
limiterconfiguration (for/generate; you may name it e.g.generateLimiter). - Edit the
/generateroute to include the rate limiting middleware before the handler.
Suggested changeset
2
backend/src/routes/remotion-captions.js
| @@ -5,6 +5,7 @@ | ||
| const fs = require('fs'); | ||
| const { bundle } = require('@remotion/bundler'); | ||
| const { renderMedia, getCompositions } = require('@remotion/renderer'); | ||
| const rateLimit = require('express-rate-limit'); | ||
|
|
||
| /** | ||
| * Get all available videos for captioning | ||
| @@ -51,7 +52,18 @@ | ||
| * Generate captioned video using Remotion | ||
| * POST /clips/remotion-captions/generate | ||
| */ | ||
| router.post('/generate', async (req, res) => { | ||
|
|
||
| // Rate limiter for generate endpoint: max 5 requests per minute per IP | ||
| const generateLimiter = rateLimit({ | ||
| windowMs: 60 * 1000, // 1 minute | ||
| max: 5, // limit each IP to 5 requests per windowMs | ||
| message: { | ||
| success: false, | ||
| error: 'Too many requests, please try again after a minute.' | ||
| } | ||
| }); | ||
|
|
||
| router.post('/generate', generateLimiter, async (req, res) => { | ||
| try { | ||
| const { | ||
| transcriptId, |
backend/package.json
Outside changed files
| @@ -31,7 +31,8 @@ | ||
| "typescript": "^5.9.2", | ||
| "uuid": "^9.0.1", | ||
| "winston": "^3.17.0", | ||
| "winston-daily-rotate-file": "^5.0.0" | ||
| "winston-daily-rotate-file": "^5.0.0", | ||
| "express-rate-limit": "^8.1.0" | ||
| }, | ||
| "devDependencies": { | ||
| "jest": "^29.6.2", |
This fix introduces these dependencies
| Package | Version | Security advisories |
| express-rate-limit (npm) | 8.1.0 | None |
Copilot is powered by AI and may make mistakes. Always verify output.
Copilot
AI
changed the title
[WIP] [FEATURE] Captioning Service: Build Remotion-powered Captioning for All Vinci Clips Videos
Implement Remotion-powered Captioning Service for All Vinci Clips Videos
Sep 14, 2025
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR implements a comprehensive Remotion-powered captioning service that provides state-of-the-art video captioning capabilities for all videos stored in Vinci Clips. The new service is accessible directly from the homepage and offers advanced animations and effects using Remotion technology.
Key Features
🎬 New Captioning Service
🔧 Backend Infrastructure
/clips/remotion-captions/*endpoints for video listing, style management, and generation🎨 Frontend Interface
/captionsroute with intuitive captioning workflow📱 Remotion Compositions
Technical Implementation
The service leverages Remotion's powerful React-based video generation capabilities to create professional captions with:
User Experience
Screenshots
Updated Homepage
Remotion Captions Interface
Configuration Updates
The new Remotion Captions service provides a professional-grade captioning solution that complements the existing FFmpeg system, offering users advanced animation capabilities and superior visual quality for their video content.
Fixes #46.
Warning
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
fonts.googleapis.comnext-server (v15.3.5)(dns block)https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-cpu-linux-x86_64-2.9.1.tar.gznode scripts/install.js(http block)If you need me to access, download, or install something from one of these locations, you can either:
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.