/** * 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, // Publie aussi la vidéo en story sur les pages Facebook (sans effet sur TikTok) alsoStory: z.boolean().default(false), // 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; /** 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 ReelJobKind = "video"; export type ReelJobStatus = "pending" | "processing" | "completed" | "failed";