mirror of
https://github.com/R0m1k3/Socialflow.git
synced 2026-10-16 03:39:15 +02:00
feat(reels): interrupteur « Afficher le logo sur la vidéo »
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
This commit is contained in:
16 files changed
+102
-17
No files matched your search
@@ -32,6 +32,8 @@ interface CommonProps {
|
||||
musicUrl?: string;
|
||||
musicVolume: number;
|
||||
logoUrl?: string | null;
|
||||
/** Petit logo pendant la vidéo (le grand logo de fin dépend de l'effet de fin). */
|
||||
showWatermark: boolean;
|
||||
storeName?: string;
|
||||
endingEffect: boolean;
|
||||
/** Sous-titres SRT : remplacent `text`, minutés par le fichier. */
|
||||
@@ -74,7 +76,8 @@ function estimatedVoiceDuration(text: string): number {
|
||||
*/
|
||||
export function ReelPreview(props: ReelPreviewProps) {
|
||||
const videoDuration = useVideoDuration(props.kind === "video" ? props.videoUrl : undefined);
|
||||
const { text, showCaptions, captionStyle, ttsEnabled, voice, musicUrl, musicVolume, endingEffect } = props;
|
||||
const { text, showCaptions, captionStyle, ttsEnabled, voice, musicUrl, musicVolume, endingEffect, showWatermark } =
|
||||
props;
|
||||
const logoUrl = props.logoUrl ?? undefined;
|
||||
const storeName = endingEffect ? props.storeName || undefined : undefined;
|
||||
const srtCues = props.kind === "video" && props.srtCues?.length ? props.srtCues : null;
|
||||
@@ -106,6 +109,7 @@ export function ReelPreview(props: ReelPreviewProps) {
|
||||
musicUrl,
|
||||
musicVolume,
|
||||
logoUrl,
|
||||
showWatermark,
|
||||
storeName,
|
||||
endingSeconds,
|
||||
};
|
||||
@@ -136,6 +140,7 @@ export function ReelPreview(props: ReelPreviewProps) {
|
||||
words,
|
||||
captionStyle,
|
||||
logoUrl,
|
||||
showWatermark,
|
||||
storeName,
|
||||
logoStart: timing.logoStart,
|
||||
fadeStart: timing.fadeStart,
|
||||
@@ -150,7 +155,7 @@ export function ReelPreview(props: ReelPreviewProps) {
|
||||
props.kind,
|
||||
props.kind === "video" ? props.videoUrl : props.images.join("|"),
|
||||
videoDuration, text, cleanText, srtCues, showCaptions, captionStyle, ttsEnabled, voice, voiceDuration,
|
||||
musicUrl, musicVolume, logoUrl, storeName, endingEffect,
|
||||
musicUrl, musicVolume, logoUrl, showWatermark, storeName, endingEffect,
|
||||
]);
|
||||
|
||||
if (!composition) {
|
||||
|
||||
@@ -84,6 +84,7 @@ export default function MobileNewReel() {
|
||||
// Désactivée par défaut : double le temps de rendu, utile seulement pour une vidéo tremblée
|
||||
const [stabilize, setStabilize] = useState(false);
|
||||
const [enableEndingEffect, setEnableEndingEffect] = useState(true);
|
||||
const [showLogo, setShowLogo] = useState(true);
|
||||
|
||||
// TTS Sync state
|
||||
const [syncInfo, setSyncInfo] = useState<{
|
||||
@@ -280,6 +281,7 @@ export default function MobileNewReel() {
|
||||
ttsStyle,
|
||||
captionStyle,
|
||||
enableEndingEffect,
|
||||
showLogo,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -484,6 +486,16 @@ export default function MobileNewReel() {
|
||||
<Label className="text-xs font-medium">Style des sous-titres</Label>
|
||||
<CaptionStylePicker value={captionStyle} onChange={setCaptionStyle} compact />
|
||||
</div>
|
||||
<div className="flex items-center space-x-2 mt-4">
|
||||
<Switch
|
||||
id="show-logo"
|
||||
checked={showLogo}
|
||||
onCheckedChange={setShowLogo}
|
||||
/>
|
||||
<Label htmlFor="show-logo" className="font-medium cursor-pointer">
|
||||
Afficher le logo sur la vidéo
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2 mt-4">
|
||||
<Switch
|
||||
id="enable-ending-effect"
|
||||
@@ -572,6 +584,7 @@ export default function MobileNewReel() {
|
||||
musicUrl={selectedTrack?.previewUrl}
|
||||
musicVolume={musicVolume[0] / 100}
|
||||
logoUrl={reelConfig?.logoUrl}
|
||||
showWatermark={showLogo}
|
||||
storeName={pages.find((p) => p.id === selectedPages[0])?.pageName}
|
||||
endingEffect={enableEndingEffect}
|
||||
/>
|
||||
|
||||
@@ -54,6 +54,7 @@ export default function MobileRemotionVideoPage() {
|
||||
const [publishDescription, setPublishDescription] = useState("");
|
||||
const [isPublishing, setIsPublishing] = useState(false);
|
||||
const [captionStyle, setCaptionStyle] = useState<CaptionStyle>(DEFAULT_CAPTION_STYLE);
|
||||
const [showLogo, setShowLogo] = useState(true);
|
||||
const [voicePreview, setVoicePreview] = useState<VoicePreviewResult | null>(null);
|
||||
const [renderProgress, setRenderProgress] = useState(0);
|
||||
const currentVoice = isVoicePreviewCurrent(voicePreview, overlayText, voiceSettings) ? voicePreview : null;
|
||||
@@ -169,6 +170,7 @@ export default function MobileRemotionVideoPage() {
|
||||
formData.append("ttsVoice", voiceSettings.voice);
|
||||
formData.append("ttsStyle", voiceSettings.style);
|
||||
formData.append("captionStyle", captionStyle);
|
||||
formData.append("showLogo", String(showLogo));
|
||||
if (selectedPageIds[0]) formData.append("selectedPageId", selectedPageIds[0]);
|
||||
if (musicFile) { formData.append("music", musicFile); formData.append("musicVolume", String(musicVolume)); }
|
||||
else if (selectedTrack) { formData.append("musicTrackUrl", selectedTrack.url); formData.append("musicVolume", String(musicVolume)); }
|
||||
@@ -350,6 +352,10 @@ export default function MobileRemotionVideoPage() {
|
||||
<Label className="text-xs font-medium">Style des sous-titres</Label>
|
||||
<CaptionStylePicker value={captionStyle} onChange={setCaptionStyle} compact />
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<Switch id="show-logo-m" checked={showLogo} onCheckedChange={setShowLogo} />
|
||||
<Label htmlFor="show-logo-m" className="text-sm">Afficher le logo sur la vidéo</Label>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<Switch id="tts-m" checked={ttsEnabled} onCheckedChange={setTtsEnabled} />
|
||||
<Label htmlFor="tts-m" className="text-sm">Voix TTS</Label>
|
||||
@@ -474,6 +480,7 @@ export default function MobileRemotionVideoPage() {
|
||||
musicUrl={musicFileUrl ?? selectedTrack?.url}
|
||||
musicVolume={musicVolume}
|
||||
logoUrl={reelConfig?.logoUrl}
|
||||
showWatermark={showLogo}
|
||||
storeName={socialPages.find((p) => p.id === selectedPageIds[0])?.pageName ?? socialPages[0]?.pageName}
|
||||
endingEffect
|
||||
/>
|
||||
|
||||
@@ -79,6 +79,7 @@ export default function NewReel() {
|
||||
// Désactivée par défaut : double le temps de rendu, utile seulement pour une vidéo tremblée
|
||||
const [stabilize, setStabilize] = useState(false);
|
||||
const [enableEndingEffect, setEnableEndingEffect] = useState(true);
|
||||
const [showLogo, setShowLogo] = useState(true);
|
||||
|
||||
// TTS Sync state
|
||||
const [syncInfo, setSyncInfo] = useState<{
|
||||
@@ -423,6 +424,7 @@ export default function NewReel() {
|
||||
drawText,
|
||||
stabilize: stabilize,
|
||||
enableEndingEffect,
|
||||
showLogo,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -818,6 +820,17 @@ export default function NewReel() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center space-x-2 mt-4">
|
||||
<Switch
|
||||
id="show-logo"
|
||||
checked={showLogo}
|
||||
onCheckedChange={setShowLogo}
|
||||
/>
|
||||
<Label htmlFor="show-logo" className="font-medium cursor-pointer">
|
||||
Afficher le logo sur la vidéo
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-2 mt-4">
|
||||
<Switch
|
||||
id="enable-ending-effect"
|
||||
@@ -1012,6 +1025,7 @@ export default function NewReel() {
|
||||
musicUrl={selectedTrack?.previewUrl}
|
||||
musicVolume={musicVolume[0] / 100}
|
||||
logoUrl={reelConfig?.logoUrl}
|
||||
showWatermark={showLogo}
|
||||
storeName={pages.find((p) => p.id === selectedPages[0])?.pageName}
|
||||
endingEffect={enableEndingEffect}
|
||||
/>
|
||||
|
||||
@@ -55,6 +55,7 @@ export default function RemotionVideoPage() {
|
||||
const [publishDescription, setPublishDescription] = useState("");
|
||||
const [isPublishing, setIsPublishing] = useState(false);
|
||||
const [captionStyle, setCaptionStyle] = useState<CaptionStyle>(DEFAULT_CAPTION_STYLE);
|
||||
const [showLogo, setShowLogo] = useState(true);
|
||||
const [voicePreview, setVoicePreview] = useState<VoicePreviewResult | null>(null);
|
||||
const [renderProgress, setRenderProgress] = useState(0);
|
||||
const currentVoice = isVoicePreviewCurrent(voicePreview, overlayText, voiceSettings) ? voicePreview : null;
|
||||
@@ -135,6 +136,7 @@ export default function RemotionVideoPage() {
|
||||
formData.append("ttsVoice", voiceSettings.voice);
|
||||
formData.append("ttsStyle", voiceSettings.style);
|
||||
formData.append("captionStyle", captionStyle);
|
||||
formData.append("showLogo", String(showLogo));
|
||||
if (selectedPageIds[0]) formData.append("selectedPageId", selectedPageIds[0]);
|
||||
if (musicFile) { formData.append("music", musicFile); formData.append("musicVolume", String(musicVolume)); }
|
||||
else if (selectedTrack) { formData.append("musicTrackUrl", selectedTrack.url); formData.append("musicVolume", String(musicVolume)); }
|
||||
@@ -290,6 +292,10 @@ export default function RemotionVideoPage() {
|
||||
<Label className="text-sm font-medium">Style des sous-titres</Label>
|
||||
<CaptionStylePicker value={captionStyle} onChange={setCaptionStyle} />
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<Switch id="show-logo" checked={showLogo} onCheckedChange={setShowLogo} />
|
||||
<Label htmlFor="show-logo">Afficher le logo sur la vidéo</Label>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<Switch id="tts" checked={ttsEnabled} onCheckedChange={setTtsEnabled} />
|
||||
<Label htmlFor="tts">Activer la voix TTS</Label>
|
||||
@@ -420,6 +426,7 @@ export default function RemotionVideoPage() {
|
||||
musicUrl={musicFileUrl ?? selectedTrack?.url}
|
||||
musicVolume={musicVolume}
|
||||
logoUrl={reelConfig?.logoUrl}
|
||||
showWatermark={showLogo}
|
||||
storeName={socialPages.find((p) => p.id === selectedPageIds[0])?.pageName ?? socialPages[0]?.pageName}
|
||||
endingEffect
|
||||
/>
|
||||
|
||||
@@ -14,6 +14,8 @@ export type ImageCompositionProps = {
|
||||
musicUrl?: string;
|
||||
musicVolume?: number;
|
||||
logoUrl?: string;
|
||||
/** Petit logo affiché pendant la vidéo (le grand logo de fin reste sur la diapositive de fin). */
|
||||
showWatermark?: boolean;
|
||||
storeName?: string;
|
||||
/** Durée de la diapositive de fin (logo + nom du magasin), en secondes. */
|
||||
endingSeconds?: number;
|
||||
@@ -78,7 +80,7 @@ const ImageSlide: React.FC<{ src: string; effectIndex: number }> = ({ src, effec
|
||||
|
||||
/** Reel à partir d'images : diaporama animé, voix, sous-titres et diapositive de fin. */
|
||||
export const ImageComposition: React.FC<ImageCompositionProps> = ({
|
||||
images, words, captionStyle, audioUrl, musicUrl, musicVolume = 0.3, logoUrl, storeName, endingSeconds = 3,
|
||||
images, words, captionStyle, audioUrl, musicUrl, musicVolume = 0.3, logoUrl, showWatermark = true, storeName, endingSeconds = 3,
|
||||
}) => {
|
||||
const { fps, durationInFrames } = useVideoConfig();
|
||||
const hasEnding = Boolean(logoUrl || storeName) && endingSeconds > 0;
|
||||
@@ -113,7 +115,7 @@ export const ImageComposition: React.FC<ImageCompositionProps> = ({
|
||||
)}
|
||||
|
||||
<Captions words={words} style={captionStyle} hideAfter={hasEnding ? endingStart : null} />
|
||||
{logoUrl && <Watermark logoUrl={logoUrl} until={hasEnding ? endingStart : null} />}
|
||||
{logoUrl && showWatermark && <Watermark logoUrl={logoUrl} until={hasEnding ? endingStart : null} />}
|
||||
{hasEnding && <Outro start={endingStart} logoUrl={logoUrl} storeName={storeName} opaque />}
|
||||
</AbsoluteFill>
|
||||
);
|
||||
|
||||
@@ -12,6 +12,8 @@ export type ReelVideoProps = {
|
||||
words: TimedWord[];
|
||||
captionStyle: CaptionStyle;
|
||||
logoUrl?: string;
|
||||
/** Petit logo affiché pendant la vidéo (le grand logo de fin dépend de l'effet de fin). */
|
||||
showWatermark?: boolean;
|
||||
storeName?: string;
|
||||
logoStart?: number | null;
|
||||
fadeStart?: number | null;
|
||||
@@ -28,7 +30,7 @@ export type ReelVideoProps = {
|
||||
export const ReelVideo: React.FC<ReelVideoProps> = (props) => {
|
||||
const { fps } = useVideoConfig();
|
||||
const {
|
||||
videoUrl, videoDuration, totalDuration, words, captionStyle, logoUrl, storeName,
|
||||
videoUrl, videoDuration, totalDuration, words, captionStyle, logoUrl, showWatermark = true, storeName,
|
||||
logoStart = null, fadeStart = null, mixedAudioUrl, voiceUrl, voiceDelay = 2, musicUrl, musicVolume = 0.25,
|
||||
} = props;
|
||||
|
||||
@@ -67,7 +69,7 @@ export const ReelVideo: React.FC<ReelVideoProps> = (props) => {
|
||||
)}
|
||||
|
||||
<Captions words={words} style={captionStyle} hideAfter={logoStart} />
|
||||
{logoUrl && <Watermark logoUrl={logoUrl} until={logoStart} />}
|
||||
{logoUrl && showWatermark && <Watermark logoUrl={logoUrl} until={logoStart} />}
|
||||
{logoStart != null && (logoUrl || storeName) && (
|
||||
<Outro start={logoStart} logoUrl={logoUrl} storeName={storeName} />
|
||||
)}
|
||||
|
||||
Reference in new issue
Block a user