Commit ·
37dbf21
1
Parent(s): 4697873
Fix video playback: strict 2s duration, chain method (no overlap), audio duration cap, and ffmpeg browser compatibility (yuv420p)
Browse files
modules/story_reels/services/story_creator.py
CHANGED
|
@@ -438,12 +438,14 @@ class StoryCreator:
|
|
| 438 |
if result.get("path"):
|
| 439 |
temp_files.append(Path(result["path"]))
|
| 440 |
|
| 441 |
-
#
|
| 442 |
scene_duration = 2.0
|
| 443 |
-
|
| 444 |
-
|
| 445 |
-
|
| 446 |
-
|
|
|
|
|
|
|
| 447 |
|
| 448 |
generated_scenes.append({
|
| 449 |
"scene_id": result["id"],
|
|
@@ -597,28 +599,34 @@ class StoryCreator:
|
|
| 597 |
# Set duration
|
| 598 |
clip = clip.set_duration(duration)
|
| 599 |
|
| 600 |
-
#
|
| 601 |
-
#
|
| 602 |
-
|
| 603 |
-
|
| 604 |
-
# Re-crop to ensure no borders appear during zoom
|
| 605 |
-
clip = clip.crop(x_center=clip.w/2, y_center=clip.h/2, width=TARGET_WIDTH, height=TARGET_HEIGHT)
|
| 606 |
|
| 607 |
# Transitions
|
| 608 |
if i > 0:
|
| 609 |
-
clip = clip.crossfadein(
|
| 610 |
|
| 611 |
clips.append(clip)
|
|
|
|
| 612 |
total_video_duration += duration
|
| 613 |
|
| 614 |
-
# Concatenate clips
|
| 615 |
-
#
|
| 616 |
-
|
|
|
|
|
|
|
|
|
|
| 617 |
|
| 618 |
-
# Final safety:
|
| 619 |
-
|
| 620 |
-
|
| 621 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 622 |
|
| 623 |
video = video.set_audio(audio)
|
| 624 |
|
|
|
|
| 438 |
if result.get("path"):
|
| 439 |
temp_files.append(Path(result["path"]))
|
| 440 |
|
| 441 |
+
# Force 2.0 seconds per image as requested
|
| 442 |
scene_duration = 2.0
|
| 443 |
+
|
| 444 |
+
# (Optional) We can still look up chunk for debug/metadata but duration is fixed
|
| 445 |
+
# for chunk in image_chunks:
|
| 446 |
+
# if chunk['chunk_id'] == result["id"]:
|
| 447 |
+
# # scene_duration = chunk['duration'] # Ignored
|
| 448 |
+
# break
|
| 449 |
|
| 450 |
generated_scenes.append({
|
| 451 |
"scene_id": result["id"],
|
|
|
|
| 599 |
# Set duration
|
| 600 |
clip = clip.set_duration(duration)
|
| 601 |
|
| 602 |
+
# Removed dynamic zoom to fix black screen/rendering issues
|
| 603 |
+
# Images will be static but stable
|
| 604 |
+
|
| 605 |
+
# Transitions
|
|
|
|
|
|
|
| 606 |
|
| 607 |
# Transitions
|
| 608 |
if i > 0:
|
| 609 |
+
clip = clip.crossfadein(0.5) # Simple fade-in for smooth join without overlap
|
| 610 |
|
| 611 |
clips.append(clip)
|
| 612 |
+
# For strict chain method, total is sum of durations
|
| 613 |
total_video_duration += duration
|
| 614 |
|
| 615 |
+
# Concatenate clips (Chain Method: one after another)
|
| 616 |
+
# padding=0 ensures no overlap, preserving exact duration calculation
|
| 617 |
+
if len(clips) > 1:
|
| 618 |
+
video = concatenate_videoclips(clips, method="compose")
|
| 619 |
+
else:
|
| 620 |
+
video = clips[0]
|
| 621 |
|
| 622 |
+
# Final safety: FORCE video duration to match audio exactly
|
| 623 |
+
# If video is longer, cut it. If shorter, it's fine (but our logic tries to match)
|
| 624 |
+
if video.duration > audio_duration:
|
| 625 |
+
logger.info(f"Trimming video from {video.duration}s to match audio {audio_duration}s")
|
| 626 |
+
video = video.subclip(0, audio_duration)
|
| 627 |
+
else:
|
| 628 |
+
# Ideally shouldn't happen with our chain logic, but if slightly short, set duration
|
| 629 |
+
video = video.set_duration(audio_duration)
|
| 630 |
|
| 631 |
video = video.set_audio(audio)
|
| 632 |
|