add video impl

This commit is contained in:
sun-guannan
2025-07-13 16:12:23 +08:00
parent 707c54c41b
commit 6ba9d1aab9

View File

@@ -25,134 +25,134 @@ def add_video_track(
speed: float = 1.0, speed: float = 1.0,
track_name: str = "main", track_name: str = "main",
relative_index: int = 0, relative_index: int = 0,
duration: Optional[float] = None, # 新增 duration 参数 duration: Optional[float] = None, # Added duration parameter
transition: Optional[str] = None, # 转场类型 transition: Optional[str] = None, # Transition type
transition_duration: Optional[float] = 0.5, # 转场时长(秒) transition_duration: Optional[float] = 0.5, # Transition duration (seconds)
# 蒙版相关参数 # Mask related parameters
mask_type: Optional[str] = None, # 蒙版类型 mask_type: Optional[str] = None, # Mask type
mask_center_x: float = 0.5, # 蒙版中心X坐标0-1 mask_center_x: float = 0.5, # Mask center X coordinate (0-1)
mask_center_y: float = 0.5, # 蒙版中心Y坐标0-1 mask_center_y: float = 0.5, # Mask center Y coordinate (0-1)
mask_size: float = 1.0, # 蒙版大小(0-1 mask_size: float = 1.0, # Mask size (0-1)
mask_rotation: float = 0.0, # 蒙版旋转角度(度) mask_rotation: float = 0.0, # Mask rotation angle (degrees)
mask_feather: float = 0.0, # 蒙版羽化程度(0-1 mask_feather: float = 0.0, # Mask feather level (0-1)
mask_invert: bool = False, # 是否反转蒙版 mask_invert: bool = False, # Whether to invert mask
mask_rect_width: Optional[float] = None, # 矩形蒙版宽度(仅矩形蒙版) mask_rect_width: Optional[float] = None, # Rectangle mask width (only for rectangle mask)
mask_round_corner: Optional[float] = None, # 矩形蒙版圆角(仅矩形蒙版,0-100) mask_round_corner: Optional[float] = None, # Rectangle mask rounded corner (only for rectangle mask, 0-100)
volume: float = 1.0 # 音量大小,默认1.0 volume: float = 1.0 # Volume level, default 1.0
) -> Dict[str, str]: ) -> Dict[str, str]:
""" """
向指定草稿添加视频轨道 Add video track to specified draft
:param draft_folder: 草稿文件夹路径,可选参数 :param draft_folder: Draft folder path, optional parameter
:param video_url: 视频URL :param video_url: Video URL
:param width: 视频宽度,默认1080 :param width: Video width, default 1080
:param height: 视频高度,默认1920 :param height: Video height, default 1920
:param start: 源视频开始时间(秒),默认0 :param start: Source video start time (seconds), default 0
:param end: 源视频结束时间默认None使用视频总时长 :param end: Source video end time (seconds), default None (use total video duration)
:param target_start: 目标视频开始时间(秒),默认0 :param target_start: Target video start time (seconds), default 0
:param draft_id: 草稿ID如果为None或找不到对应的zip文件则创建新草稿 :param draft_id: Draft ID, if None or corresponding zip file not found, create new draft
:param transform_y: Y轴变换,默认0 :param transform_y: Y-axis transform, default 0
:param scale_x: X轴缩放,默认1 :param scale_x: X-axis scale, default 1
:param scale_y: Y轴缩放,默认1 :param scale_y: Y-axis scale, default 1
:param transform_x: X轴变换,默认0 :param transform_x: X-axis transform, default 0
:param speed: 视频播放速度,默认1.0 :param speed: Video playback speed, default 1.0
:param track_name: 当只有一个视频的时候,轨道名称可以省略 :param track_name: When there is only one video, track name can be omitted
:param relative_index: 轨道渲染顺序索引,默认0 :param relative_index: Track rendering order index, default 0
:param duration: 视频时长(秒),如果提供则跳过时长检测 :param duration: Video duration (seconds), if provided, skip duration detection
:param transition: 转场类型,可选参数 :param transition: Transition type, optional parameter
:param transition_duration: 转场时长(秒),默认使用转场类型的默认时长 :param transition_duration: Transition duration (seconds), default uses the default duration of transition type
:param mask_type: 蒙版类型(线性、镜面、圆形、矩形、爱心、星形),可选参数 :param mask_type: Mask type (linear, mirror, circle, rectangle, heart, star), optional parameter
:param mask_center_x: 蒙版中心X坐标0-1默认0.5 :param mask_center_x: Mask center X coordinate (0-1), default 0.5
:param mask_center_y: 蒙版中心Y坐标0-1默认0.5 :param mask_center_y: Mask center Y coordinate (0-1), default 0.5
:param mask_size: 蒙版大小0-1默认1.0 :param mask_size: Mask size (0-1), default 1.0
:param mask_rotation: 蒙版旋转角度(度),默认0.0 :param mask_rotation: Mask rotation angle (degrees), default 0.0
:param mask_feather: 蒙版羽化程度0-1默认0.0 :param mask_feather: Mask feather level (0-1), default 0.0
:param mask_invert: 是否反转蒙版,默认False :param mask_invert: Whether to invert mask, default False
:param mask_rect_width: 矩形蒙版的宽度,仅在蒙版类型为矩形时允许设置,以占素材宽度的比例表示 :param mask_rect_width: Rectangle mask width, only allowed when mask type is rectangle, represented as a proportion of material width
:param mask_round_corner: 矩形蒙版的圆角参数,仅在蒙版类型为矩形时允许设置,取值范围0~100 :param mask_round_corner: Rectangle mask rounded corner parameter, only allowed when mask type is rectangle, range 0~100
:param volume: 音量大小,默认1.00.0为静音1.0为原音量) :param volume: Volume level, default 1.0 (0.0 is mute, 1.0 is original volume)
:return: 更新后的草稿信息,包含draft_iddraft_url :return: Updated draft information, including draft_id and draft_url
""" """
# 获取或创建草稿 # Get or create draft
draft_id, script = get_or_create_draft( draft_id, script = get_or_create_draft(
draft_id=draft_id, draft_id=draft_id,
width=width, width=width,
height=height height=height
) )
# 检查是否存在视频轨道,如果不存在则添加一条默认视频轨道 # Check if video track exists, if not, add a default video track
try: try:
script.get_track(draft.Track_type.video, track_name=None) script.get_track(draft.Track_type.video, track_name=None)
except exceptions.TrackNotFound: except exceptions.TrackNotFound:
script.add_track(draft.Track_type.video, relative_index=0) script.add_track(draft.Track_type.video, relative_index=0)
except NameError: except NameError:
# 如果存在多个视频轨道(NameError),则不做任何操作 # If multiple video tracks exist (NameError), do nothing
pass pass
# 添加视频轨道(仅当轨道不存在时) # Add video track (only when track doesn't exist)
if track_name is not None: if track_name is not None:
try: try:
imported_track=script.get_imported_track(draft.Track_type.video, name=track_name) imported_track=script.get_imported_track(draft.Track_type.video, name=track_name)
# 如果没有抛出异常,说明轨道已存在 # If no exception is thrown, the track already exists
except exceptions.TrackNotFound: except exceptions.TrackNotFound:
# 轨道不存在,创建新轨道 # Track doesn't exist, create new track
script.add_track(draft.Track_type.video, track_name=track_name, relative_index=relative_index) script.add_track(draft.Track_type.video, track_name=track_name, relative_index=relative_index)
else: else:
script.add_track(draft.Track_type.video, relative_index=relative_index) script.add_track(draft.Track_type.video, relative_index=relative_index)
# 如果传递了 duration 参数优先使用它否则使用默认时长0秒在下载草稿时才会获取真实时长 # If duration parameter is passed, use it preferentially; otherwise use default duration of 0 seconds, and get the real duration when downloading the draft
if duration is not None: if duration is not None:
# 使用传递进来的 duration跳过时长获取和检查 # Use the passed duration, skip duration retrieval and check
video_duration = duration video_duration = duration
else: else:
# 使用默认时长0秒在下载草稿时才会获取真实时长 # Use default duration of 0 seconds, and get the real duration when downloading the draft
video_duration = 0.0 # 默认视频时长为0秒 video_duration = 0.0 # Default video duration is 0 seconds
# duration_result = get_video_duration(video_url) # duration_result = get_video_duration(video_url)
# if not duration_result["success"]: # if not duration_result["success"]:
# print(f"获取视频时长失败: {duration_result['error']}") # print(f"Failed to get video duration: {duration_result['error']}")
# # 检查视频时长是否超过2分钟 # # Check if video duration exceeds 2 minutes
# if duration_result["output"] > 120: # 120秒 = 2分钟 # if duration_result["output"] > 120: # 120 seconds = 2 minutes
# raise Exception(f"视频时长超过2分钟限制当前时长: {duration_result['output']}") # raise Exception(f"Video duration exceeds 2-minute limit, current duration: {duration_result['output']} seconds")
# video_duration = duration_result["output"] # video_duration = duration_result["output"]
# 生成本地文件名 # Generate local filename
material_name = f"video_{url_to_hash(video_url)}.mp4" material_name = f"video_{url_to_hash(video_url)}.mp4"
# local_video_path = download_video(video_url, draft_dir) # local_video_path = download_video(video_url, draft_dir)
# 构建draft_video_path # Build draft_video_path
draft_video_path = None draft_video_path = None
if draft_folder: if draft_folder:
# 检测输入路径类型并处理 # Detect input path type and process
if is_windows_path(draft_folder): if is_windows_path(draft_folder):
# Windows路径处理 # Windows path processing
windows_drive, windows_path = re.match(r'([a-zA-Z]:)(.*)', draft_folder).groups() windows_drive, windows_path = re.match(r'([a-zA-Z]:)(.*)', draft_folder).groups()
parts = [p for p in windows_path.split('\\') if p] # 分割路径并过滤空部分 parts = [p for p in windows_path.split('\\') if p] # Split path and filter empty parts
draft_video_path = os.path.join(windows_drive, *parts, draft_id, "assets", "video", material_name) draft_video_path = os.path.join(windows_drive, *parts, draft_id, "assets", "video", material_name)
# 规范化路径(确保分隔符一致) # Normalize path (ensure consistent separators)
draft_video_path = draft_video_path.replace('/', '\\') draft_video_path = draft_video_path.replace('/', '\\')
else: else:
# macOS/Linux路径处理 # macOS/Linux path processing
draft_video_path = os.path.join(draft_folder, draft_id, "assets", "video", material_name) draft_video_path = os.path.join(draft_folder, draft_id, "assets", "video", material_name)
# 打印路径信息 # Print path information
print('replace_path:', draft_video_path) print('replace_path:', draft_video_path)
# 设置视频结束时间 # Set video end time
video_end = end if end is not None else video_duration video_end = end if end is not None else video_duration
# 计算源视频时长 # Calculate source video duration
source_duration = video_end - start source_duration = video_end - start
# 计算目标视频时长(考虑速度因素) # Calculate target video duration (considering speed factor)
target_duration = source_duration / speed target_duration = source_duration / speed
# 创建视频片段 # Create video clip
if draft_video_path: if draft_video_path:
video_material = draft.Video_material(material_type='video', replace_path=draft_video_path, remote_url=video_url, material_name=material_name, duration=video_duration, width=0, height=0) video_material = draft.Video_material(material_type='video', replace_path=draft_video_path, remote_url=video_url, material_name=material_name, duration=video_duration, width=0, height=0)
else: else:
video_material = draft.Video_material(material_type='video', remote_url=video_url, material_name=material_name, duration = video_duration, width=0, height=0) video_material = draft.Video_material(material_type='video', remote_url=video_url, material_name=material_name, duration = video_duration, width=0, height=0)
# 创建source_timerangetarget_timerange # Create source_timerange and target_timerange
source_timerange = trange(f"{start}s", f"{source_duration}s") source_timerange = trange(f"{start}s", f"{source_duration}s")
target_timerange = trange(f"{target_start}s", f"{target_duration}s") target_timerange = trange(f"{target_start}s", f"{target_duration}s")
@@ -170,24 +170,24 @@ def add_video_track(
volume=volume volume=volume
) )
# 添加转场效果 # Add transition effect
if transition: if transition:
try: try:
# 获取转场类型 # Get transition type
if IS_CAPCUT_ENV: if IS_CAPCUT_ENV:
transition_type = getattr(draft.CapCut_Transition_type, transition) transition_type = getattr(draft.CapCut_Transition_type, transition)
else: else:
transition_type = getattr(draft.Transition_type, transition) transition_type = getattr(draft.Transition_type, transition)
# 设置转场时长(转换为微秒) # Set transition duration (convert to microseconds)
duration_microseconds = int(transition_duration * 1e6) duration_microseconds = int(transition_duration * 1e6)
# 添加转场 # Add transition
video_segment.add_transition(transition_type, duration=duration_microseconds) video_segment.add_transition(transition_type, duration=duration_microseconds)
except AttributeError: except AttributeError:
raise ValueError(f"不支持的转场类型: {transition},已跳过转场设置") raise ValueError(f"Unsupported transition type: {transition}, transition setting skipped")
# 添加蒙版效果 # Add mask effect
if mask_type: if mask_type:
try: try:
if IS_CAPCUT_ENV: if IS_CAPCUT_ENV:
@@ -207,9 +207,9 @@ def add_video_track(
round_corner=mask_round_corner round_corner=mask_round_corner
) )
except: except:
raise ValueError(f"不支持的蒙版类型 {mask_type},支持的类型包括:线性、镜面、圆形、矩形、爱心、星形") raise ValueError(f"Unsupported mask type {mask_type}, supported types include: linear, mirror, circle, rectangle, heart, star")
# 添加视频片段到轨道 # Add video segment to track
# if imported_track is not None: # if imported_track is not None:
# imported_track.add_segment(video_segment) # imported_track.add_segment(video_segment)
# else: # else: