-
Notifications
You must be signed in to change notification settings - Fork 6
Implement Remotion-powered Captioning Service for All Vinci Clips Videos #47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Co-authored-by: sdntsng <19806109+sdntsng@users.noreply.github.com>
| 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
a file system access
This route handler performs
a file system access
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 4 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.
-
Copy modified line R8 -
Copy modified lines R55-R66
| @@ -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, |
-
Copy modified lines R34-R35
| @@ -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", |
| Package | Version | Security advisories |
| express-rate-limit (npm) | 8.1.0 | None |
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.