第一帧视频没有转场特效

This commit is contained in:
Larkspur-Wang 2025-04-25 00:13:46 +08:00
parent f0b8fbbbfb
commit e90c195e9e
4 changed files with 68 additions and 25 deletions

View File

@ -93,7 +93,7 @@ class VideoParams(BaseModel):
subtitle_enabled: Optional[bool] = True
subtitle_position: Optional[str] = "custom" # top, bottom, center, custom
custom_position: float = 70.0
custom_position: float = 85.0
font_name: Optional[str] = "STHeitiMedium.ttc"
text_fore_color: Optional[str] = "#FFFFFF"
text_background_color: Union[bool, str] = True

View File

@ -58,6 +58,11 @@ def pulse_animation(clip: ImageClip, speed: float = 1.0) -> ImageClip:
:param speed: 动画速度
:return: 应用了动画效果的剪辑
"""
# 确保剪辑有持续时间
if not hasattr(clip, 'duration') or clip.duration is None:
logger.warning("Clip has no duration, using default duration of 5 seconds")
clip = clip.with_duration(5)
# 应用整体缩放效果
return clip.resized(lambda t: 1 + 0.1 * np.sin(speed * 2 * np.pi * t))
@ -70,6 +75,11 @@ def whole_bounce_animation(clip: ImageClip, speed: float = 1.0) -> ImageClip:
:param speed: 动画速度
:return: 应用了动画效果的剪辑
"""
# 确保剪辑有持续时间
if not hasattr(clip, 'duration') or clip.duration is None:
logger.warning("Clip has no duration, using default duration of 5 seconds")
clip = clip.with_duration(5)
# 获取原始位置
original_position = clip.pos
@ -129,10 +139,13 @@ def light_sweep_animation(clip: ImageClip, duration: float, speed: float = 1.0)
# 创建一个白色光照剪辑
light_clip = ColorClip(size=clip.size, color=(255, 255, 255))
light_clip = light_clip.with_mask(light_mask)
light_clip = light_clip.with_duration(duration)
# 合成原始剪辑和光照剪辑
# 由于 ColorClip 没有 with_blend 方法,我们直接使用 CompositeVideoClip
return CompositeVideoClip([clip, light_clip])
composite_clip = CompositeVideoClip([clip, light_clip])
composite_clip = composite_clip.with_duration(duration)
return composite_clip
def fade_animation(clip: ImageClip, speed: float = 1.0) -> ImageClip:
@ -143,6 +156,11 @@ def fade_animation(clip: ImageClip, speed: float = 1.0) -> ImageClip:
:param speed: 动画速度
:return: 应用了动画效果的剪辑
"""
# 确保剪辑有持续时间
if not hasattr(clip, 'duration') or clip.duration is None:
logger.warning("Clip has no duration, using default duration of 5 seconds")
clip = clip.with_duration(5)
# 创建一个函数,根据时间返回不透明度
def make_frame_opacity(t):
# 计算总时长
@ -191,6 +209,11 @@ def wave_animation(clip: ImageClip, speed: float = 1.0) -> ImageClip:
:param speed: 动画速度
:return: 应用了动画效果的剪辑
"""
# 确保剪辑有持续时间
if not hasattr(clip, 'duration') or clip.duration is None:
logger.warning("Clip has no duration, using default duration of 5 seconds")
clip = clip.with_duration(5)
# 获取原始位置
original_position = clip.pos
@ -220,6 +243,11 @@ def bounce_animation(clip: ImageClip, speed: float = 1.0) -> ImageClip:
:param speed: 动画速度
:return: 应用了动画效果的剪辑
"""
# 确保剪辑有持续时间
if not hasattr(clip, 'duration') or clip.duration is None:
logger.warning("Clip has no duration, using default duration of 5 seconds")
clip = clip.with_duration(5)
# 注意:这个函数需要对每个字符单独处理
# 但由于我们已经将文本渲染为图像,无法直接操作单个字符
# 这里我们实现一个模拟效果,通过对图像应用波浪变形来模拟字符跳动
@ -235,9 +263,14 @@ def bounce_animation(clip: ImageClip, speed: float = 1.0) -> ImageClip:
# 获取原始帧
frame = clip.get_frame(t)
# 创建一个新的帧
# 创建一个新的帧,保持透明通道
# 使用与原始帧相同的数据类型和形状,但初始化为透明
new_frame = np.zeros_like(frame)
# 如果有alpha通道RGBA将所有像素设置为完全透明
if frame.shape[2] == 4:
new_frame[:, :, 3] = 0 # 设置alpha通道为0完全透明
# 对每一列应用不同的垂直偏移,创造波浪效果
for x in range(w):
# 计算该列的垂直偏移量,使用正弦函数创建波浪效果

View File

@ -93,6 +93,9 @@ def combine_videos(
random.shuffle(raw_clips)
# Add downloaded clips over and over until the duration of the audio (max_duration) has been reached
# 用于跟踪是否是第一个视频片段
is_first_clip = True
while video_duration < audio_duration:
for clip in raw_clips:
# Check if clip is longer than the remaining audio
@ -139,27 +142,34 @@ def combine_videos(
f"resizing video to {video_width} x {video_height}, clip size: {clip_w} x {clip_h}"
)
shuffle_side = random.choice(["left", "right", "top", "bottom"])
logger.info(f"Using transition mode: {video_transition_mode}")
if video_transition_mode.value == VideoTransitionMode.none.value:
clip = clip
elif video_transition_mode.value == VideoTransitionMode.fade_in.value:
clip = video_effects.fadein_transition(clip, 1)
elif video_transition_mode.value == VideoTransitionMode.fade_out.value:
clip = video_effects.fadeout_transition(clip, 1)
elif video_transition_mode.value == VideoTransitionMode.slide_in.value:
clip = video_effects.slidein_transition(clip, 1, shuffle_side)
elif video_transition_mode.value == VideoTransitionMode.slide_out.value:
clip = video_effects.slideout_transition(clip, 1, shuffle_side)
elif video_transition_mode.value == VideoTransitionMode.shuffle.value:
transition_funcs = [
lambda c: video_effects.fadein_transition(c, 1),
lambda c: video_effects.fadeout_transition(c, 1),
lambda c: video_effects.slidein_transition(c, 1, shuffle_side),
lambda c: video_effects.slideout_transition(c, 1, shuffle_side),
]
shuffle_transition = random.choice(transition_funcs)
clip = shuffle_transition(clip)
# 如果是第一个视频片段,不应用转场效果
if is_first_clip:
logger.info("First clip: no transition effect applied")
# 不应用任何转场效果
is_first_clip = False # 更新标志,后续片段将应用转场效果
else:
# 对后续视频片段应用转场效果
shuffle_side = random.choice(["left", "right", "top", "bottom"])
logger.info(f"Using transition mode: {video_transition_mode}")
if video_transition_mode.value == VideoTransitionMode.none.value:
clip = clip
elif video_transition_mode.value == VideoTransitionMode.fade_in.value:
clip = video_effects.fadein_transition(clip, 1)
elif video_transition_mode.value == VideoTransitionMode.fade_out.value:
clip = video_effects.fadeout_transition(clip, 1)
elif video_transition_mode.value == VideoTransitionMode.slide_in.value:
clip = video_effects.slidein_transition(clip, 1, shuffle_side)
elif video_transition_mode.value == VideoTransitionMode.slide_out.value:
clip = video_effects.slideout_transition(clip, 1, shuffle_side)
elif video_transition_mode.value == VideoTransitionMode.shuffle.value:
transition_funcs = [
lambda c: video_effects.fadein_transition(c, 1),
lambda c: video_effects.fadeout_transition(c, 1),
lambda c: video_effects.slidein_transition(c, 1, shuffle_side),
lambda c: video_effects.slideout_transition(c, 1, shuffle_side),
]
shuffle_transition = random.choice(transition_funcs)
clip = shuffle_transition(clip)
if clip.duration > max_clip_duration:
clip = clip.subclipped(0, max_clip_duration)

View File

@ -762,7 +762,7 @@ with right_panel:
if params.subtitle_position == "custom":
custom_position = st.text_input(
tr("Custom Position (% from top)"), value="65.0"
tr("Custom Position (% from top)"), value="85.0"
)
try:
params.custom_position = float(custom_position)