mirror of
https://github.com/R0m1k3/Socialflow.git
synced 2026-10-23 23:18:12 +02:00
feat: add mobile Remotion video generator with image, text, audio, and Facebook publishing features.
This commit is contained in:
1 parent
3d51fdc90b
commit
e1a6d1e06c
4 files changed
+19
-4
No files matched your search
@@ -131,6 +131,8 @@ export default function MobileRemotionVideoPage() {
|
|||||||
if (!response.ok) { const e = await response.json().catch(() => ({})); throw new Error(e.error || "Erreur"); }
|
if (!response.ok) { const e = await response.json().catch(() => ({})); throw new Error(e.error || "Erreur"); }
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
setVideoUrl(data.url);
|
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 !" });
|
toast({ title: "Vidéo générée !" });
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
toast({ title: "Erreur", description: err.message, variant: "destructive" });
|
toast({ title: "Erreur", description: err.message, variant: "destructive" });
|
||||||
|
|||||||
@@ -115,6 +115,8 @@ export default function RemotionVideoPage() {
|
|||||||
if (!response.ok) { const e = await response.json().catch(() => ({})); throw new Error(e.error || "Erreur"); }
|
if (!response.ok) { const e = await response.json().catch(() => ({})); throw new Error(e.error || "Erreur"); }
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
setVideoUrl(data.url);
|
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." });
|
toast({ title: "Vidéo générée !", description: "Votre vidéo est prête à publier." });
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
toast({ title: "Erreur", description: err.message || "Impossible de créer la vidéo.", variant: "destructive" });
|
toast({ title: "Erreur", description: err.message || "Impossible de créer la vidéo.", variant: "destructive" });
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { bundle } from "@remotion/bundler";
|
|||||||
import { renderMedia, selectComposition } from "@remotion/renderer";
|
import { renderMedia, selectComposition } from "@remotion/renderer";
|
||||||
import { ffmpegService } from "../services/ffmpeg";
|
import { ffmpegService } from "../services/ffmpeg";
|
||||||
import { storage as dbStorage } from "../storage";
|
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 { facebookService } from "../services/facebook";
|
||||||
import * as musicMetadata from "music-metadata";
|
import * as musicMetadata from "music-metadata";
|
||||||
|
|
||||||
@@ -371,8 +371,11 @@ remotionRouter.post("/publish", async (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!scheduledFor) {
|
if (!scheduledFor) {
|
||||||
console.log(`🚀 Publishing to ${page.pageName}...`);
|
console.log(`🚀 Publishing to ${page.pageName} (direct binary upload)...`);
|
||||||
const reelId = await facebookService.publishReel(page, resolvePublicUrl(cloudinaryResult.originalUrl), description || "");
|
// 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 });
|
await dbStorage.updateScheduledPost(scheduledPost.id, { publishedAt: new Date(), externalPostId: reelId });
|
||||||
results.push({ pageId, success: true, reelId });
|
results.push({ pageId, success: true, reelId });
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
import { storage } from '../storage';
|
import { storage } from '../storage';
|
||||||
import type { Post, SocialPage, Media } from '@shared/schema';
|
import type { Post, SocialPage, Media } from '@shared/schema';
|
||||||
import { imageProcessor } from './imageProcessor';
|
import { imageProcessor } from './imageProcessor';
|
||||||
@@ -71,7 +73,13 @@ export class FacebookService {
|
|||||||
throw new Error('Reels require a video media');
|
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 {
|
} else {
|
||||||
// Default to feed
|
// Default to feed
|
||||||
const imageMedia = mediaList.filter(m => m.type === 'image');
|
const imageMedia = mediaList.filter(m => m.type === 'image');
|
||||||
|
|||||||
Reference in new issue
Block a user