fix: 3 critical bugs — audio corruption, blob URLs, static duration
1. Audio/Video sync: During playback, let the browser handle media naturally and only force-seek when drift > 250ms. Prevents audio glitching caused by setting currentTime 30x/sec. When paused/scrubbing, sync precisely. 2. Blob URLs → Server uploads: All media uploads (BrandTabMedia, SceneFieldEditor, TemplateFieldInput) now POST to /api/upload and use persistent server URLs instead of blob: URLs that vanish on refresh. 3. Dynamic duration: Added useVideoDurations hook that probes actual video durations from uploaded form videos. getTemplateDuration and compileExpressToTimeline now accept videoDurations to override static durationSeconds for form-sourced scenes.
This commit is contained in:
@@ -78,9 +78,23 @@ export function getAspectDimensions(aspect: string): { w: number; h: number } {
|
||||
}
|
||||
}
|
||||
|
||||
/** Compute total duration of template in seconds */
|
||||
export function getTemplateDuration(template: ExpressTemplate): number {
|
||||
return template.scenes.reduce((sum, s) => sum + s.durationSeconds, 0);
|
||||
/**
|
||||
* Compute total duration of template in seconds.
|
||||
* @param videoDurations Optional map of scene.id → actual video duration (seconds).
|
||||
* When provided, overrides the static durationSeconds for scenes
|
||||
* that source video from form uploads.
|
||||
*/
|
||||
export function getTemplateDuration(
|
||||
template: ExpressTemplate,
|
||||
videoDurations?: Record<string, number>,
|
||||
): number {
|
||||
return template.scenes.reduce((sum, scene) => {
|
||||
// If this scene has a form-sourced video and we know its actual duration, use it
|
||||
if (videoDurations && scene.segmentSource === 'form' && videoDurations[scene.id]) {
|
||||
return sum + videoDurations[scene.id];
|
||||
}
|
||||
return sum + scene.durationSeconds;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,6 +106,7 @@ export function compileExpressToTimeline(
|
||||
fieldData: Record<string, string>,
|
||||
designMD: DesignMD,
|
||||
company?: CompanyProfile,
|
||||
videoDurations?: Record<string, number>,
|
||||
): { elements: TimelineElement[]; layers: TimelineLayer[] } {
|
||||
const fps = 30;
|
||||
const elements: TimelineElement[] = [];
|
||||
@@ -106,8 +121,11 @@ export function compileExpressToTimeline(
|
||||
|
||||
// Process each scene sequentially — the template's scenes are the sole source of truth
|
||||
for (const scene of template.scenes) {
|
||||
|
||||
const sceneDurFrames = scene.durationSeconds * fps;
|
||||
// Use actual video duration if available for form-sourced scenes
|
||||
const sceneDuration = (videoDurations && scene.segmentSource === 'form' && videoDurations[scene.id])
|
||||
? videoDurations[scene.id]
|
||||
: scene.durationSeconds;
|
||||
const sceneDurFrames = sceneDuration * fps;
|
||||
const sceneStart = frameOffset;
|
||||
const sceneEnd = frameOffset + sceneDurFrames;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user