mirror of
https://github.com/R0m1k3/Socialflow.git
synced 2026-10-23 06:58:12 +02:00
feat: Implement new Reel creation functionality with dedicated mobile and desktop UIs, server-side API routes, and a new FFmpeg processing service.
This commit is contained in:
1 parent
ae0048a6e1
commit
3a5acd425c
5 files changed
+42
-2
No files matched your search
@@ -63,6 +63,7 @@ export default function MobileNewReel() {
|
|||||||
// État audio preview
|
// État audio preview
|
||||||
const [isPlaying, setIsPlaying] = useState<string | null>(null);
|
const [isPlaying, setIsPlaying] = useState<string | null>(null);
|
||||||
const [stabilize, setStabilize] = useState(true); // Activé par défaut pour les Reels
|
const [stabilize, setStabilize] = useState(true); // Activé par défaut pour les Reels
|
||||||
|
const [enableEndingEffect, setEnableEndingEffect] = useState(true);
|
||||||
|
|
||||||
|
|
||||||
const { data: pages = [] } = useQuery<SocialPage[]>({
|
const { data: pages = [] } = useQuery<SocialPage[]>({
|
||||||
@@ -204,6 +205,7 @@ export default function MobileNewReel() {
|
|||||||
drawText: true,
|
drawText: true,
|
||||||
ttsEnabled,
|
ttsEnabled,
|
||||||
ttsVoice,
|
ttsVoice,
|
||||||
|
enableEndingEffect,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -395,6 +397,17 @@ export default function MobileNewReel() {
|
|||||||
className="text-lg"
|
className="text-lg"
|
||||||
rows={3}
|
rows={3}
|
||||||
/>
|
/>
|
||||||
|
<div className="flex items-center space-x-2 mt-4">
|
||||||
|
<Switch
|
||||||
|
id="enable-ending-effect"
|
||||||
|
checked={enableEndingEffect}
|
||||||
|
onCheckedChange={setEnableEndingEffect}
|
||||||
|
/>
|
||||||
|
<Label htmlFor="enable-ending-effect" className="font-medium cursor-pointer">
|
||||||
|
Activer l'effet de fin (logo + fondu)
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center space-x-2 mt-4">
|
<div className="flex items-center space-x-2 mt-4">
|
||||||
<Switch
|
<Switch
|
||||||
id="tts-mode"
|
id="tts-mode"
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ export default function NewReel() {
|
|||||||
const [ttsVoice, setTtsVoice] = useState("fr-FR-VivienneMultilingualNeural");
|
const [ttsVoice, setTtsVoice] = useState("fr-FR-VivienneMultilingualNeural");
|
||||||
const [drawText, setDrawText] = useState(true);
|
const [drawText, setDrawText] = useState(true);
|
||||||
const [stabilize, setStabilize] = useState(true); // default to true
|
const [stabilize, setStabilize] = useState(true); // default to true
|
||||||
|
const [enableEndingEffect, setEnableEndingEffect] = useState(true);
|
||||||
|
|
||||||
// Enable TTS by default on mobile
|
// Enable TTS by default on mobile
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -336,6 +337,7 @@ export default function NewReel() {
|
|||||||
ttsVoice,
|
ttsVoice,
|
||||||
drawText,
|
drawText,
|
||||||
stabilize: stabilize,
|
stabilize: stabilize,
|
||||||
|
enableEndingEffect,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -710,6 +712,17 @@ export default function NewReel() {
|
|||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center space-x-2 mt-4">
|
||||||
|
<Switch
|
||||||
|
id="enable-ending-effect"
|
||||||
|
checked={enableEndingEffect}
|
||||||
|
onCheckedChange={setEnableEndingEffect}
|
||||||
|
/>
|
||||||
|
<Label htmlFor="enable-ending-effect" className="font-medium cursor-pointer">
|
||||||
|
Activer l'effet de fin (logo + fondu)
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center space-x-2 mt-4">
|
<div className="flex items-center space-x-2 mt-4">
|
||||||
<Switch
|
<Switch
|
||||||
id="tts-mode"
|
id="tts-mode"
|
||||||
|
|||||||
@@ -161,6 +161,7 @@ class ReelRequest(BaseModel):
|
|||||||
tts_voice: str = "fr-FR-VivienneMultilingualNeural"
|
tts_voice: str = "fr-FR-VivienneMultilingualNeural"
|
||||||
draw_text: bool = True
|
draw_text: bool = True
|
||||||
stabilize: bool = False # Stabilisation vidéo via vidstab
|
stabilize: bool = False # Stabilisation vidéo via vidstab
|
||||||
|
enable_ending_effect: bool = True
|
||||||
|
|
||||||
|
|
||||||
def clean_text_for_display(text: str) -> str:
|
def clean_text_for_display(text: str) -> str:
|
||||||
@@ -861,7 +862,7 @@ async def process_reel(request: ReelRequest, x_api_key: str = Header(None)):
|
|||||||
v_chain += text_filter
|
v_chain += text_filter
|
||||||
|
|
||||||
if has_watermark:
|
if has_watermark:
|
||||||
if request.store_name:
|
if request.store_name and request.enable_ending_effect:
|
||||||
# Ouro Party Mode + Persistent bottom right
|
# Ouro Party Mode + Persistent bottom right
|
||||||
|
|
||||||
# We need two scaled versions of the logo
|
# We need two scaled versions of the logo
|
||||||
@@ -890,7 +891,9 @@ async def process_reel(request: ReelRequest, x_api_key: str = Header(None)):
|
|||||||
v_chain += "[v_pre_wm];[v_pre_wm][wm]overlay=W-w-20:H-h-20"
|
v_chain += "[v_pre_wm];[v_pre_wm][wm]overlay=W-w-20:H-h-20"
|
||||||
|
|
||||||
# Add Video Fade Out
|
# Add Video Fade Out
|
||||||
v_chain += f",fade=t=out:st={fade_start}:d={fade_duration}"
|
if request.enable_ending_effect:
|
||||||
|
v_chain += f",fade=t=out:st={fade_start}:d={fade_duration}"
|
||||||
|
|
||||||
|
|
||||||
# End of video chain
|
# End of video chain
|
||||||
v_chain += "[vout]"
|
v_chain += "[vout]"
|
||||||
|
|||||||
@@ -302,6 +302,7 @@ reelsRouter.post('/reels/preview', async (req: Request, res: Response) => {
|
|||||||
musicVolume = 0.25,
|
musicVolume = 0.25,
|
||||||
drawText = true,
|
drawText = true,
|
||||||
stabilize = false,
|
stabilize = false,
|
||||||
|
enableEndingEffect = true,
|
||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
// Récupérer le média vidéo
|
// Récupérer le média vidéo
|
||||||
@@ -360,6 +361,7 @@ reelsRouter.post('/reels/preview', async (req: Request, res: Response) => {
|
|||||||
drawText,
|
drawText,
|
||||||
stabilize,
|
stabilize,
|
||||||
watermarkUrl,
|
watermarkUrl,
|
||||||
|
enableEndingEffect,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
@@ -471,6 +473,7 @@ async function processReelBackground(
|
|||||||
musicVolume?: number;
|
musicVolume?: number;
|
||||||
drawText?: boolean;
|
drawText?: boolean;
|
||||||
stabilize?: boolean;
|
stabilize?: boolean;
|
||||||
|
enableEndingEffect?: boolean;
|
||||||
},
|
},
|
||||||
storeName?: string
|
storeName?: string
|
||||||
) {
|
) {
|
||||||
@@ -489,6 +492,7 @@ async function processReelBackground(
|
|||||||
musicVolume,
|
musicVolume,
|
||||||
drawText,
|
drawText,
|
||||||
stabilize,
|
stabilize,
|
||||||
|
enableEndingEffect,
|
||||||
} = data;
|
} = data;
|
||||||
|
|
||||||
console.log(`🔄 [Background] Starting processing for Post ${postId}`);
|
console.log(`🔄 [Background] Starting processing for Post ${postId}`);
|
||||||
@@ -570,6 +574,7 @@ async function processReelBackground(
|
|||||||
stabilize,
|
stabilize,
|
||||||
watermarkUrl,
|
watermarkUrl,
|
||||||
storeName,
|
storeName,
|
||||||
|
enableEndingEffect,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`⏱️ [Background] FFmpeg took ${(Date.now() - startTime) / 1000}s`);
|
console.log(`⏱️ [Background] FFmpeg took ${(Date.now() - startTime) / 1000}s`);
|
||||||
@@ -728,6 +733,7 @@ reelsRouter.post('/reels', async (req: Request, res: Response) => {
|
|||||||
musicVolume = 0.25,
|
musicVolume = 0.25,
|
||||||
drawText = true,
|
drawText = true,
|
||||||
stabilize = false,
|
stabilize = false,
|
||||||
|
enableEndingEffect = true,
|
||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
// Validation immédiate
|
// Validation immédiate
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ interface FFmpegReelRequest {
|
|||||||
stabilize?: boolean; // Stabilisation vidéo via vidstab (default: false)
|
stabilize?: boolean; // Stabilisation vidéo via vidstab (default: false)
|
||||||
watermark_url?: string; // URL du logo
|
watermark_url?: string; // URL du logo
|
||||||
store_name?: string; // Nom du magasin pour l'outro
|
store_name?: string; // Nom du magasin pour l'outro
|
||||||
|
enable_ending_effect?: boolean; // Activer l'effet de fin (logo+fondu)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FFmpegReelResponse {
|
interface FFmpegReelResponse {
|
||||||
@@ -77,6 +78,7 @@ export class FFmpegService {
|
|||||||
stabilize?: boolean;
|
stabilize?: boolean;
|
||||||
watermarkUrl?: string;
|
watermarkUrl?: string;
|
||||||
storeName?: string;
|
storeName?: string;
|
||||||
|
enableEndingEffect?: boolean;
|
||||||
} = {}
|
} = {}
|
||||||
): Promise<{ success: boolean; videoBase64?: string; duration?: number; error?: string }> {
|
): Promise<{ success: boolean; videoBase64?: string; duration?: number; error?: string }> {
|
||||||
const config = this.ensureConfigured();
|
const config = this.ensureConfigured();
|
||||||
@@ -95,6 +97,7 @@ export class FFmpegService {
|
|||||||
stabilize: options.stabilize ?? false,
|
stabilize: options.stabilize ?? false,
|
||||||
watermark_url: options.watermarkUrl,
|
watermark_url: options.watermarkUrl,
|
||||||
store_name: options.storeName,
|
store_name: options.storeName,
|
||||||
|
enable_ending_effect: options.enableEndingEffect ?? true,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Remove undefined values
|
// Remove undefined values
|
||||||
@@ -176,6 +179,7 @@ export class FFmpegService {
|
|||||||
stabilize?: boolean;
|
stabilize?: boolean;
|
||||||
watermarkUrl?: string;
|
watermarkUrl?: string;
|
||||||
storeName?: string;
|
storeName?: string;
|
||||||
|
enableEndingEffect?: boolean;
|
||||||
} = {}
|
} = {}
|
||||||
): Promise<{ success: boolean; videoBase64?: string; duration?: number; error?: string }> {
|
): Promise<{ success: boolean; videoBase64?: string; duration?: number; error?: string }> {
|
||||||
const config = this.ensureConfigured();
|
const config = this.ensureConfigured();
|
||||||
@@ -194,6 +198,7 @@ export class FFmpegService {
|
|||||||
stabilize: options.stabilize ?? false,
|
stabilize: options.stabilize ?? false,
|
||||||
watermark_url: options.watermarkUrl,
|
watermark_url: options.watermarkUrl,
|
||||||
store_name: options.storeName,
|
store_name: options.storeName,
|
||||||
|
enable_ending_effect: options.enableEndingEffect ?? true,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Remove undefined values
|
// Remove undefined values
|
||||||
|
|||||||
Reference in new issue
Block a user