fix: dynamic video duration across ProductionForm + LivePreviewCanvas
- Expanded useVideoDurations to detect both form-sourced segments AND editable-slot video fields inside content scenes - Wired videoDurations into ProductionForm (getTemplateDuration, compileExpressToTimeline, LivePreviewCanvas, ExportModal) - LivePreviewCanvas now accepts videoDurations prop - Simplified getTemplateDuration/compiler: any scene with a known video duration uses it, regardless of segmentSource type - A 33s uploaded video now creates a 33s timeline, not 5s
This commit is contained in:
@@ -14,6 +14,7 @@ import { TemplateFieldInput } from '../shared/TemplateFieldInput';
|
||||
import { LivePreviewCanvas } from '../shared/LivePreviewCanvas';
|
||||
import { migrateExpressFields } from '../../context/TemplateBuilderContext';
|
||||
import { useBatchProduction } from '../../hooks/useBatchProduction';
|
||||
import { useVideoDurations } from '../../hooks/useVideoDurations';
|
||||
import { BatchDataPanel } from './BatchDataPanel';
|
||||
import { exportBatchAsZip, BatchExportProgress } from '../../utils/batchExporter';
|
||||
|
||||
@@ -97,16 +98,19 @@ export const ProductionForm: React.FC<ProductionFormProps> = ({
|
||||
|
||||
const playerRef = useRef<BradlyPlayerRef>(null);
|
||||
|
||||
// Probe actual video durations for dynamic timeline
|
||||
const videoDurations = useVideoDurations(template, fieldData);
|
||||
|
||||
const designMD = brand.design;
|
||||
const fps = 30;
|
||||
const totalDuration = getTemplateDuration(template);
|
||||
const totalDuration = getTemplateDuration(template, videoDurations);
|
||||
const totalFrames = Math.max(30, totalDuration * fps);
|
||||
|
||||
// ─── Compile for ExportModal (only when modal is open — LivePreviewCanvas handles its own compile) ───
|
||||
const compiled = useMemo(
|
||||
() => {
|
||||
if (!showExportModal) return { elements: [], layers: [] };
|
||||
const result = compileExpressToTimeline(template, fieldData, designMD, brand);
|
||||
const result = compileExpressToTimeline(template, fieldData, designMD, brand, videoDurations);
|
||||
result.elements = result.elements.map(el => {
|
||||
const fieldId = el.sourceFieldId;
|
||||
const fitOverride = fieldId ? mediaFits[fieldId] : undefined;
|
||||
@@ -121,7 +125,7 @@ export const ProductionForm: React.FC<ProductionFormProps> = ({
|
||||
});
|
||||
return result;
|
||||
},
|
||||
[showExportModal, template, fieldData, designMD, brand, mediaFits, containBgColors]
|
||||
[showExportModal, template, fieldData, designMD, brand, mediaFits, containBgColors, videoDurations]
|
||||
);
|
||||
|
||||
// ─── Collect all TemplateFields across all scenes ───
|
||||
@@ -600,6 +604,7 @@ export const ProductionForm: React.FC<ProductionFormProps> = ({
|
||||
activeSceneId={activeSceneId}
|
||||
onSceneChange={setActiveSceneId}
|
||||
playerRef={playerRef}
|
||||
videoDurations={videoDurations}
|
||||
statusLabel={
|
||||
batch.isBatchMode
|
||||
? (batch.pieceCount > 0 ? `Pieza ${activeBatchPieceIndex + 1} de ${batch.pieceCount}` : 'Sin piezas')
|
||||
|
||||
@@ -36,6 +36,8 @@ export interface LivePreviewCanvasProps {
|
||||
statusLabel?: string;
|
||||
/** Whether all required fields are complete */
|
||||
isComplete?: boolean;
|
||||
/** Video duration overrides per scene (from useVideoDurations) */
|
||||
videoDurations?: Record<string, number>;
|
||||
}
|
||||
|
||||
/** Format frame number to mm:ss */
|
||||
@@ -66,6 +68,7 @@ export const LivePreviewCanvas: React.FC<LivePreviewCanvasProps> = ({
|
||||
playerRef: externalRef,
|
||||
statusLabel,
|
||||
isComplete = false,
|
||||
videoDurations,
|
||||
}) => {
|
||||
const internalRef = useRef<BradlyPlayerRef>(null);
|
||||
const playerRef = externalRef || internalRef;
|
||||
@@ -76,13 +79,13 @@ export const LivePreviewCanvas: React.FC<LivePreviewCanvasProps> = ({
|
||||
const isScrubbing = useRef(false);
|
||||
|
||||
const fps = 30;
|
||||
const totalDuration = getTemplateDuration(template);
|
||||
const totalDuration = getTemplateDuration(template, videoDurations);
|
||||
const totalFrames = Math.max(30, totalDuration * fps);
|
||||
const dimensions = getAspectDimensions(template.aspectRatio);
|
||||
|
||||
// Compile template to timeline (reactive to fieldData + mediaFits)
|
||||
const compiled = useMemo(() => {
|
||||
const result = compileExpressToTimeline(template, fieldData, designMD, brand);
|
||||
const result = compileExpressToTimeline(template, fieldData, designMD, brand, videoDurations);
|
||||
// Strip transitions and apply mediaFit overrides
|
||||
result.elements = result.elements.map(el => {
|
||||
const fieldId = el.sourceFieldId;
|
||||
@@ -97,7 +100,7 @@ export const LivePreviewCanvas: React.FC<LivePreviewCanvasProps> = ({
|
||||
};
|
||||
});
|
||||
return result;
|
||||
}, [template, fieldData, designMD, brand, mediaFits, containBgColors]);
|
||||
}, [template, fieldData, designMD, brand, mediaFits, containBgColors, videoDurations]);
|
||||
|
||||
const playerInputProps = useMemo(() => ({
|
||||
designMD,
|
||||
|
||||
Reference in New Issue
Block a user