mirror of
https://github.com/R0m1k3/Socialflow.git
synced 2026-10-20 21:48:40 +02:00
feat(reels): import d'un fichier SRT lu par la voix à son minutage
- Nouveau Reel (bureau et mobile) : bouton « Importer un fichier SRT ». Tant qu'un SRT est chargé, le texte libre et l'assistant IA sont désactivés ; le choix de la voix (moteur, voix, ton) reste disponible. - Service Python : chaque sous-titre est lu séparément, ses silences retirés, puis posé à son instant de début. Une lecture trop longue est accélérée (atempo, sans changer la hauteur) pour tenir dans la durée du sous-titre, avec un avertissement au-delà de ×1,35. Après un repli sur Edge, tous les sous-titres gardent la même voix. - Sans voix, les mots de chaque sous-titre s'affichent sur son intervalle. - Aperçu : sous-titres calés sur le minutage du fichier. - Tests : lecture SRT (TS), fenêtres, accélération et mixage (Python). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WVqEw4Xycgf8BtwmSftE2M
This commit is contained in:
15 files changed
+635
-44
No files matched your search
@@ -7,6 +7,7 @@ import type { User } from '@shared/schema';
|
||||
import { storage } from '../storage';
|
||||
import { ffmpegService, FFmpegServiceError } from '../services/ffmpeg';
|
||||
import { ttsPreviewSchema, videoReelParamsSchema, type VideoReelParams } from '@shared/reel';
|
||||
import { srtText } from '@shared/srt';
|
||||
import { enqueueReelJob, countActiveReelJobs } from '../services/reels/queue';
|
||||
import { resolveGeminiApiKey, resolveLogoPath, resolveStoreName } from '../services/reels/assets';
|
||||
import { configuredRenderer } from '../services/reels/videoPipeline';
|
||||
@@ -334,8 +335,12 @@ reelsRouter.post('/reels', async (req: Request, res: Response) => {
|
||||
return res.status(400).json({ error: parsed.error.issues[0]?.message ?? 'Paramètres invalides' });
|
||||
}
|
||||
|
||||
const { srtCues } = parsed.data;
|
||||
const params: VideoReelParams = {
|
||||
...parsed.data,
|
||||
// Avec un SRT, le texte libre n'est plus utilisé : la voix lit les sous-titres
|
||||
overlayText: srtCues ? undefined : parsed.data.overlayText,
|
||||
description: parsed.data.description || (srtCues ? srtText(srtCues) : undefined),
|
||||
storeName: await resolveStoreName(user.id, parsed.data.pageIds[0]),
|
||||
};
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import fs from 'fs';
|
||||
import { Readable } from 'stream';
|
||||
import { pipeline } from 'stream/promises';
|
||||
import type { ReadableStream as WebReadableStream } from 'stream/web';
|
||||
import type { SrtCue } from '@shared/srt';
|
||||
import type { TtsEngine, TtsStyle } from '@shared/voices';
|
||||
|
||||
/** Un rendu long (stabilisation + encodage) peut dépasser plusieurs minutes. */
|
||||
@@ -20,6 +21,8 @@ const HEALTH_TIMEOUT_MS = 5_000;
|
||||
|
||||
export interface ReelRenderOptions {
|
||||
text?: string;
|
||||
/** Sous-titres SRT : remplacent `text`, la voix lit chacun à son instant. */
|
||||
srtCues?: SrtCue[];
|
||||
musicUrl?: string;
|
||||
ttsEnabled?: boolean;
|
||||
ttsVoice?: string;
|
||||
@@ -126,6 +129,7 @@ export class FFmpegService {
|
||||
const body = {
|
||||
video_url: videoUrl,
|
||||
text: options.text,
|
||||
srt_cues: options.srtCues,
|
||||
music_url: options.musicUrl,
|
||||
tts_enabled: options.ttsEnabled ?? false,
|
||||
tts_voice: options.ttsVoice,
|
||||
@@ -196,6 +200,7 @@ export class FFmpegService {
|
||||
body: JSON.stringify({
|
||||
video_url: videoUrl,
|
||||
text: options.text,
|
||||
srt_cues: options.srtCues,
|
||||
music_url: options.musicUrl,
|
||||
tts_enabled: options.ttsEnabled ?? false,
|
||||
tts_voice: options.ttsVoice,
|
||||
|
||||
@@ -47,7 +47,8 @@ export async function runVideoReelJob({ job, progress }: JobContext) {
|
||||
]);
|
||||
|
||||
const options: ReelRenderOptions = {
|
||||
text: params.overlayText,
|
||||
text: params.srtCues ? undefined : params.overlayText,
|
||||
srtCues: params.srtCues,
|
||||
musicUrl,
|
||||
ttsEnabled: params.ttsEnabled,
|
||||
ttsVoice: params.ttsVoice,
|
||||
|
||||
Reference in new issue
Block a user