From e1a6d1e06c0af4a7de14e84de6ddde23fe937b80 Mon Sep 17 00:00:00 2001 From: Michael SCHAL Date: Wed, 25 Mar 2026 16:16:30 +0100 Subject: [PATCH] feat: add mobile Remotion video generator with image, text, audio, and Facebook publishing features. --- client/src/pages/mobile/remotion-video.tsx | 2 ++ client/src/pages/remotion-video.tsx | 2 ++ server/routes/remotion.ts | 9 ++++++--- server/services/facebook.ts | 10 +++++++++- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/client/src/pages/mobile/remotion-video.tsx b/client/src/pages/mobile/remotion-video.tsx index 5129b0e..290fbda 100644 --- a/client/src/pages/mobile/remotion-video.tsx +++ b/client/src/pages/mobile/remotion-video.tsx @@ -131,6 +131,8 @@ export default function MobileRemotionVideoPage() { if (!response.ok) { const e = await response.json().catch(() => ({})); throw new Error(e.error || "Erreur"); } const data = await response.json(); setVideoUrl(data.url); + // Pre-fill description with overlay text so it appears in the Facebook post + if (overlayText && !publishDescription) setPublishDescription(overlayText); toast({ title: "Vidéo générée !" }); } catch (err: any) { toast({ title: "Erreur", description: err.message, variant: "destructive" }); diff --git a/client/src/pages/remotion-video.tsx b/client/src/pages/remotion-video.tsx index a764dec..15cdd51 100644 --- a/client/src/pages/remotion-video.tsx +++ b/client/src/pages/remotion-video.tsx @@ -115,6 +115,8 @@ export default function RemotionVideoPage() { if (!response.ok) { const e = await response.json().catch(() => ({})); throw new Error(e.error || "Erreur"); } const data = await response.json(); setVideoUrl(data.url); + // Pre-fill description with overlay text so it appears in the Facebook post + if (overlayText && !publishDescription) setPublishDescription(overlayText); toast({ title: "Vidéo générée !", description: "Votre vidéo est prête à publier." }); } catch (err: any) { toast({ title: "Erreur", description: err.message || "Impossible de créer la vidéo.", variant: "destructive" }); diff --git a/server/routes/remotion.ts b/server/routes/remotion.ts index 7559645..bc93522 100644 --- a/server/routes/remotion.ts +++ b/server/routes/remotion.ts @@ -6,7 +6,7 @@ import { bundle } from "@remotion/bundler"; import { renderMedia, selectComposition } from "@remotion/renderer"; import { ffmpegService } from "../services/ffmpeg"; import { storage as dbStorage } from "../storage"; -import { minioService as cloudinaryService, buildMinioUrl, resolvePublicUrl } from "../services/minio"; +import { minioService as cloudinaryService, buildMinioUrl } from "../services/minio"; import { facebookService } from "../services/facebook"; import * as musicMetadata from "music-metadata"; @@ -371,8 +371,11 @@ remotionRouter.post("/publish", async (req, res) => { }); if (!scheduledFor) { - console.log(`🚀 Publishing to ${page.pageName}...`); - const reelId = await facebookService.publishReel(page, resolvePublicUrl(cloudinaryResult.originalUrl), description || ""); + console.log(`🚀 Publishing to ${page.pageName} (direct binary upload)...`); + // Upload video bytes directly — Facebook cannot access local URLs + const videoBuffer = await fs.promises.readFile(localPath); + const arrayBuffer = videoBuffer.buffer.slice(videoBuffer.byteOffset, videoBuffer.byteOffset + videoBuffer.byteLength) as ArrayBuffer; + const reelId = await facebookService.publishReelFromBuffer(page, arrayBuffer, description || ""); await dbStorage.updateScheduledPost(scheduledPost.id, { publishedAt: new Date(), externalPostId: reelId }); results.push({ pageId, success: true, reelId }); } else { diff --git a/server/services/facebook.ts b/server/services/facebook.ts index 7bf1f91..4b52f1f 100644 --- a/server/services/facebook.ts +++ b/server/services/facebook.ts @@ -1,3 +1,5 @@ +import fs from 'fs'; +import path from 'path'; import { storage } from '../storage'; import type { Post, SocialPage, Media } from '@shared/schema'; import { imageProcessor } from './imageProcessor'; @@ -71,7 +73,13 @@ export class FacebookService { throw new Error('Reels require a video media'); } - return await this.publishReel(page, resolvePublicUrl(videoMedia.originalUrl), post.content); + // Upload video bytes directly — Facebook cannot fetch local/Docker URLs + const localPath = videoMedia.originalUrl.startsWith('/') + ? path.join(process.cwd(), videoMedia.originalUrl) + : videoMedia.originalUrl; + const nodeBuffer = await fs.promises.readFile(localPath); + const arrayBuffer = nodeBuffer.buffer.slice(nodeBuffer.byteOffset, nodeBuffer.byteOffset + nodeBuffer.byteLength) as ArrayBuffer; + return await this.publishReelFromBuffer(page, arrayBuffer, post.content); } else { // Default to feed const imageMedia = mediaList.filter(m => m.type === 'image');