mirror of
https://github.com/R0m1k3/Socialflow.git
synced 2026-10-11 17:26:45 +02:00
feat: Introduce Remotion video creation and publishing page with a media thumbnail component and backend routes.
This commit is contained in:
1 parent
e82d73c4e1
commit
0eca57efab
3 files changed
+196
-22
No files matched your search
@@ -2,6 +2,8 @@ import { Router } from "express";
|
||||
import multer from "multer";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import { exec } from "child_process";
|
||||
import { promisify } from "util";
|
||||
import { bundle } from "@remotion/bundler";
|
||||
import { renderMedia, selectComposition } from "@remotion/renderer";
|
||||
import { ffmpegService } from "../services/ffmpeg";
|
||||
@@ -10,6 +12,25 @@ import { minioService as cloudinaryService, buildMinioUrl } from "../services/mi
|
||||
import { facebookService } from "../services/facebook";
|
||||
import * as musicMetadata from "music-metadata";
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
/**
|
||||
* Generate a thumbnail from a video file using FFmpeg
|
||||
* @param videoPath Path to the video file
|
||||
* @param outputPath Path to save the thumbnail
|
||||
* @param seekTime Time in seconds to extract the frame (default: 1s)
|
||||
*/
|
||||
async function generateVideoThumbnail(videoPath: string, outputPath: string, seekTime: number = 1): Promise<boolean> {
|
||||
try {
|
||||
const cmd = `ffmpeg -y -ss ${seekTime} -i "${videoPath}" -vframes 1 -q:v 2 "${outputPath}"`;
|
||||
await execAsync(cmd);
|
||||
return fs.existsSync(outputPath);
|
||||
} catch (error) {
|
||||
console.warn('⚠️ Failed to generate video thumbnail:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export const remotionRouter = Router();
|
||||
|
||||
const uploadDir = path.join(process.cwd(), "uploads", "temp");
|
||||
@@ -291,8 +312,21 @@ remotionRouter.post("/render", upload.fields([{ name: "images", maxCount: 4 }, {
|
||||
});
|
||||
|
||||
console.log("✅ Render completed:", outputFilename);
|
||||
|
||||
// Generate thumbnail for the video
|
||||
const thumbnailFilename = outputFilename.replace('.mp4', '-thumb.jpg');
|
||||
const thumbnailPath = path.join(uploadDir, thumbnailFilename);
|
||||
const thumbnailGenerated = await generateVideoThumbnail(outputLocation, thumbnailPath, 2);
|
||||
|
||||
if (thumbnailGenerated) {
|
||||
console.log("🖼️ Thumbnail generated:", thumbnailFilename);
|
||||
}
|
||||
|
||||
// Return relative URL so any client (mobile, desktop, Docker) can access it
|
||||
res.json({ url: `/uploads/temp/${outputFilename}` });
|
||||
res.json({
|
||||
url: `/uploads/temp/${outputFilename}`,
|
||||
thumbnailUrl: thumbnailGenerated ? `/uploads/temp/${thumbnailFilename}` : null,
|
||||
});
|
||||
|
||||
} catch (err: any) {
|
||||
console.error("❌ Remotion render error:", err);
|
||||
|
||||
Reference in new issue
Block a user