mirror of
https://github.com/R0m1k3/Socialflow.git
synced 2026-10-11 17:26:45 +02:00
Le petit logo affiché pendant la vidéo devient optionnel (activé par défaut), sur les 4 pages Reel (vidéo et images, bureau et mobile) ainsi que dans l'aperçu. Le grand logo de l'effet de fin reste piloté par son propre interrupteur. Pris en charge par le rendu Remotion et par le rendu FFmpeg de secours (show_watermark). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018Ze4bs7tpF1KGWUk6ZZSZ4
88 lines
3.4 KiB
TypeScript
88 lines
3.4 KiB
TypeScript
/**
|
|
* Paramètres des rendus de Reels, validés à l'entrée des routes et stockés tels
|
|
* quels dans `reel_jobs.params` : un job repris après redémarrage retrouve ainsi
|
|
* exactement ce que l'utilisateur avait demandé.
|
|
*/
|
|
|
|
import { z } from "zod";
|
|
import { CAPTION_STYLES, DEFAULT_CAPTION_STYLE } from "./captions";
|
|
import { srtCuesSchema } from "./srt";
|
|
import { TTS_ENGINES, TTS_STYLES } from "./voices";
|
|
|
|
const optionalText = z
|
|
.string()
|
|
.trim()
|
|
.optional()
|
|
.transform((value) => (value ? value : undefined));
|
|
|
|
/** Reel construit à partir d'une vidéo de la médiathèque. */
|
|
export const videoReelParamsSchema = z.object({
|
|
videoMediaId: z.string({ required_error: "Vidéo requise" }).min(1, "Vidéo requise"),
|
|
pageIds: z
|
|
.array(z.string().min(1), { required_error: "Au moins une page requise" })
|
|
.min(1, "Au moins une page requise"),
|
|
musicTrackId: optionalText,
|
|
musicUrl: optionalText,
|
|
overlayText: optionalText,
|
|
// Sous-titres SRT : remplacent le texte libre, la voix lit chacun à son instant
|
|
srtCues: srtCuesSchema.optional(),
|
|
description: optionalText,
|
|
ttsEnabled: z.boolean().default(false),
|
|
ttsVoice: optionalText,
|
|
ttsEngine: z.enum(TTS_ENGINES).optional(),
|
|
ttsStyle: z.enum(TTS_STYLES).optional(),
|
|
scheduledFor: optionalText,
|
|
// Ancien réglage, ignoré : le minutage vient désormais de la voix elle-même
|
|
wordDuration: z.number().optional(),
|
|
fontSize: z.number().int().min(16).max(200).default(64),
|
|
musicVolume: z.number().min(0).max(2).default(0.25),
|
|
drawText: z.boolean().default(true),
|
|
captionStyle: z.enum(CAPTION_STYLES).default(DEFAULT_CAPTION_STYLE),
|
|
stabilize: z.boolean().default(false),
|
|
// Petit logo pendant la vidéo ; le grand logo de fin dépend de enableEndingEffect
|
|
showLogo: z.boolean().default(true),
|
|
enableEndingEffect: z.boolean().default(true),
|
|
// Déterminé par le serveur à partir de la première page, jamais par le client
|
|
storeName: z.string().optional(),
|
|
});
|
|
|
|
export type VideoReelParams = z.infer<typeof videoReelParamsSchema>;
|
|
|
|
/** Reel construit à partir d'images (rendu Remotion). */
|
|
export const imagesReelParamsSchema = z.object({
|
|
// Chemins relatifs /uploads/... ou URL absolues
|
|
imageUrls: z.array(z.string().min(1)).min(1, "Aucune image fournie").max(4),
|
|
overlayText: optionalText,
|
|
musicUrl: optionalText,
|
|
musicVolume: z.number().min(0).max(2).default(0.3),
|
|
ttsEnabled: z.boolean().default(true),
|
|
ttsEngine: z.enum(TTS_ENGINES).optional(),
|
|
ttsVoice: optionalText,
|
|
ttsStyle: z.enum(TTS_STYLES).optional(),
|
|
captionStyle: z.enum(CAPTION_STYLES).default(DEFAULT_CAPTION_STYLE),
|
|
showLogo: z.boolean().default(true),
|
|
storeName: z.string().optional(),
|
|
// Fichiers temporaires à supprimer une fois le rendu terminé
|
|
tempFiles: z.array(z.string()).default([]),
|
|
});
|
|
|
|
/** Aperçu de la voix. */
|
|
export const ttsPreviewSchema = z.object({
|
|
text: z.string({ required_error: "Texte requis" }).trim().min(1, "Texte requis").max(2000),
|
|
ttsVoice: optionalText,
|
|
ttsEngine: z.enum(TTS_ENGINES).optional(),
|
|
ttsStyle: z.enum(TTS_STYLES).optional(),
|
|
});
|
|
|
|
export type ImagesReelParams = z.infer<typeof imagesReelParamsSchema>;
|
|
|
|
export type ReelJobKind = "video" | "images";
|
|
|
|
export type ReelJobStatus = "pending" | "processing" | "completed" | "failed";
|
|
|
|
/** Résultat d'un rendu d'images, consulté par le client pour l'aperçu. */
|
|
export interface ImagesReelResult {
|
|
url: string;
|
|
thumbnailUrl: string | null;
|
|
}
|