parent
2d5ff46caa
commit
d232b65335
|
|
@ -84,7 +84,7 @@ class EdgeTTS:
|
||||||
|
|
||||||
|
|
||||||
class GenerateDraft:
|
class GenerateDraft:
|
||||||
"""生成剪映脚本"""
|
"""剪映脚本生成器"""
|
||||||
|
|
||||||
# noinspection PyShadowingNames
|
# noinspection PyShadowingNames
|
||||||
def __init__(
|
def __init__(
|
||||||
|
|
@ -97,7 +97,7 @@ class GenerateDraft:
|
||||||
materials_path: str = r"C:\Users\admin\PycharmProjects\Python\剪映脚本生成自动化\materials",
|
materials_path: str = r"C:\Users\admin\PycharmProjects\Python\剪映脚本生成自动化\materials",
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
初始化草稿
|
初始化生成剪映脚本
|
||||||
:param name: 草稿名称
|
:param name: 草稿名称
|
||||||
:param video_width: 视频宽度
|
:param video_width: 视频宽度
|
||||||
:param video_height: 视频高度
|
:param video_height: 视频高度
|
||||||
|
|
@ -105,7 +105,7 @@ class GenerateDraft:
|
||||||
:param drafts_path: 剪映草稿文件夹路径
|
:param drafts_path: 剪映草稿文件夹路径
|
||||||
:param materials_path: 素材文件夹路径
|
:param materials_path: 素材文件夹路径
|
||||||
"""
|
"""
|
||||||
print("正在生成剪映脚本...")
|
print("正在初始化剪映脚本生成器...", end="")
|
||||||
# noinspection PyBroadException
|
# noinspection PyBroadException
|
||||||
try:
|
try:
|
||||||
# 草稿名称
|
# 草稿名称
|
||||||
|
|
@ -134,13 +134,11 @@ class GenerateDraft:
|
||||||
allow_replace=True, # 允许覆盖
|
allow_replace=True, # 允许覆盖
|
||||||
)
|
)
|
||||||
|
|
||||||
self.draft.add_track(track_type=capcut.TrackType.video)
|
# 脚本持续时长
|
||||||
|
self.duration = 0
|
||||||
# 音频持续时长
|
|
||||||
self.audio_duration = 0
|
|
||||||
|
|
||||||
# 实例化EdgeTTS
|
# 实例化EdgeTTS
|
||||||
self.synthesizer = EdgeTTS(self.materials_path)
|
self.synthesizer = EdgeTTS(self.materials_path)
|
||||||
|
print("已完成")
|
||||||
except Exception as exception:
|
except Exception as exception:
|
||||||
print(f"发生异常:{str(exception)}")
|
print(f"发生异常:{str(exception)}")
|
||||||
raise
|
raise
|
||||||
|
|
@ -159,6 +157,7 @@ class GenerateDraft:
|
||||||
|
|
||||||
def _add_audio(
|
def _add_audio(
|
||||||
self,
|
self,
|
||||||
|
track_name: str,
|
||||||
name: str,
|
name: str,
|
||||||
target_timerange: Tuple[Optional[int, str], Optional[int, str]],
|
target_timerange: Tuple[Optional[int, str], Optional[int, str]],
|
||||||
source_timerange: Optional[Tuple[str, str]] = None,
|
source_timerange: Optional[Tuple[str, str]] = None,
|
||||||
|
|
@ -168,6 +167,7 @@ class GenerateDraft:
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
添加音频片段
|
添加音频片段
|
||||||
|
:param track_name: 轨道名称
|
||||||
:param name: 音频素材名称
|
:param name: 音频素材名称
|
||||||
:param target_timerange: 音频素材在轨道上的范围,包括开始时间和持续时长
|
:param target_timerange: 音频素材在轨道上的范围,包括开始时间和持续时长
|
||||||
:param source_timerange: 截取音频素材范围,包括开始时间和持续时长,默认根据音频素材开始时间根据播放速度截取与音频素材持续时长等长的部分
|
:param source_timerange: 截取音频素材范围,包括开始时间和持续时长,默认根据音频素材开始时间根据播放速度截取与音频素材持续时长等长的部分
|
||||||
|
|
@ -176,7 +176,6 @@ class GenerateDraft:
|
||||||
:param fade: 淡入淡出设置
|
:param fade: 淡入淡出设置
|
||||||
:return: 无
|
:return: 无
|
||||||
"""
|
"""
|
||||||
print(f"正在添加音频片段 {name}...", end="")
|
|
||||||
try:
|
try:
|
||||||
# 构建音频片段
|
# 构建音频片段
|
||||||
audio_segment = capcut.AudioSegment(
|
audio_segment = capcut.AudioSegment(
|
||||||
|
|
@ -192,15 +191,15 @@ class GenerateDraft:
|
||||||
if fade:
|
if fade:
|
||||||
audio_segment.add_fade(*fade)
|
audio_segment.add_fade(*fade)
|
||||||
|
|
||||||
self.draft.add_segment(audio_segment)
|
# 向指定轨道添加音频片段
|
||||||
print("已完成")
|
self.draft.add_segment(segment=audio_segment, track_name=track_name)
|
||||||
return
|
return
|
||||||
except Exception as exception:
|
except Exception:
|
||||||
print(f"发生异常:{str(exception)}")
|
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def _add_video(
|
def _add_video(
|
||||||
self,
|
self,
|
||||||
|
track_name: str,
|
||||||
name: str,
|
name: str,
|
||||||
target_timerange: Tuple[Optional[int, str], Optional[int, str]],
|
target_timerange: Tuple[Optional[int, str], Optional[int, str]],
|
||||||
source_timerange: Optional[
|
source_timerange: Optional[
|
||||||
|
|
@ -216,6 +215,7 @@ class GenerateDraft:
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
添加视频/图片片段
|
添加视频/图片片段
|
||||||
|
:param track_name: 轨道名称
|
||||||
:param name: 视频/图片素材名称
|
:param name: 视频/图片素材名称
|
||||||
:param target_timerange: 视频素材在轨道上的范围,包括开始时间和持续时长
|
:param target_timerange: 视频素材在轨道上的范围,包括开始时间和持续时长
|
||||||
:param source_timerange: 截取视频素材范围,包括开始时间和持续时长
|
:param source_timerange: 截取视频素材范围,包括开始时间和持续时长
|
||||||
|
|
@ -226,11 +226,11 @@ class GenerateDraft:
|
||||||
:param animation: 动画设置
|
:param animation: 动画设置
|
||||||
:param transition: 转场设置
|
:param transition: 转场设置
|
||||||
:param background_filling: 背景填充设置
|
:param background_filling: 背景填充设置
|
||||||
|
:param track_name: 轨道名称
|
||||||
:return: 无
|
:return: 无
|
||||||
"""
|
"""
|
||||||
print(f"正在添加视频/图片片段 {name}...", end="")
|
|
||||||
try:
|
try:
|
||||||
# 构建视频片段
|
# 构建视频/图片片段
|
||||||
video_segment = capcut.VideoSegment(
|
video_segment = capcut.VideoSegment(
|
||||||
material=self._get_material(name),
|
material=self._get_material(name),
|
||||||
target_timerange=trange(*target_timerange),
|
target_timerange=trange(*target_timerange),
|
||||||
|
|
@ -259,15 +259,15 @@ class GenerateDraft:
|
||||||
if background_filling:
|
if background_filling:
|
||||||
video_segment.add_background_filling(*background_filling)
|
video_segment.add_background_filling(*background_filling)
|
||||||
|
|
||||||
self.draft.add_segment(video_segment)
|
# 向指定轨道添加视频/图片片段
|
||||||
print("已完成")
|
self.draft.add_segment(segment=video_segment, track_name=track_name)
|
||||||
return
|
return
|
||||||
except Exception as exception:
|
except Exception:
|
||||||
print(f"发生异常:{str(exception)}")
|
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def _add_text(
|
def _add_text(
|
||||||
self,
|
self,
|
||||||
|
track_name: str,
|
||||||
content: str,
|
content: str,
|
||||||
timerange: Tuple[Optional[int, str], Optional[int, str]],
|
timerange: Tuple[Optional[int, str], Optional[int, str]],
|
||||||
border: Optional[Dict[str, Any]] = None,
|
border: Optional[Dict[str, Any]] = None,
|
||||||
|
|
@ -276,31 +276,31 @@ class GenerateDraft:
|
||||||
style: Optional[Dict[str, Any]] = None,
|
style: Optional[Dict[str, Any]] = None,
|
||||||
clip_settings: Optional[Dict[str, Any]] = None,
|
clip_settings: Optional[Dict[str, Any]] = None,
|
||||||
bubble: Optional[Dict[str, Any]] = None,
|
bubble: Optional[Dict[str, Any]] = None,
|
||||||
effect: Optional[str] = None,
|
effect: Optional[Dict[str, Any]] = None,
|
||||||
animation: Optional[Dict[str, Any]] = None,
|
animation: Optional[Dict[str, Any]] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
构建文本片段
|
添加文本片段
|
||||||
|
:param track_name: 轨道名称
|
||||||
:param content: 文本内容
|
:param content: 文本内容
|
||||||
:param timerange: 文本素材在轨道上的范围,包括开始时间和持续时长
|
:param timerange: 文本素材在轨道上的范围,包括开始时间和持续时长
|
||||||
:param border: 文本描边设置
|
:param border: 文本描边设置
|
||||||
:param background: 文本背景设置
|
:param background: 文本背景设置
|
||||||
:param font: 字体类型
|
:param font: 字体类型
|
||||||
:param style: 字体样式
|
:param style: 字体样式
|
||||||
:param clip_settings: 图像调节设置
|
:param clip_settings: 文本调节设置
|
||||||
:param bubble: 气泡设置
|
:param bubble: 气泡设置
|
||||||
:param effect: 花字设置
|
:param effect: 花字设置
|
||||||
:param animation: 动画设置
|
:param animation: 动画设置
|
||||||
:return: 不返回
|
:return: 无
|
||||||
"""
|
"""
|
||||||
print(f"正在添加文本片段 {content}...", end="")
|
|
||||||
try:
|
try:
|
||||||
# 创建文字片段
|
# 构建文本片段
|
||||||
text_segment = capcut.TextSegment(
|
text_segment = capcut.TextSegment(
|
||||||
text=content,
|
text=content,
|
||||||
timerange=trange(*timerange),
|
timerange=trange(*timerange),
|
||||||
border=capcut.TextBorder(**border) if border else None,
|
border=capcut.TextBorder(**border) if border else None,
|
||||||
background=capcut.TextBackground(**background) if border else None,
|
background=capcut.TextBackground(**background) if background else None,
|
||||||
font=capcut.FontType(font) if font else None,
|
font=capcut.FontType(font) if font else None,
|
||||||
style=capcut.TextStyle(**style) if style else None,
|
style=capcut.TextStyle(**style) if style else None,
|
||||||
clip_settings=(
|
clip_settings=(
|
||||||
|
|
@ -311,49 +311,87 @@ class GenerateDraft:
|
||||||
if bubble:
|
if bubble:
|
||||||
text_segment.add_bubble(**bubble)
|
text_segment.add_bubble(**bubble)
|
||||||
|
|
||||||
# 添加花字,可先保存预设,再在C:/Users/admin/AppData/Local/JianyingPro/User Data/Presets/Text_V2/预设文本?.textpreset获取花字resource_id
|
# 添加花字
|
||||||
if effect:
|
if effect:
|
||||||
text_segment.add_effect(effect)
|
text_segment.add_effect(
|
||||||
|
**effect
|
||||||
|
) # 可先将花字保存预设,再在C:/Users/admin/AppData/Local/JianyingPro/User Data/Presets/Text_V2/预设文本?.textpreset获取花字resource_id
|
||||||
|
|
||||||
# 添加动画
|
# 添加动画
|
||||||
if animation:
|
if animation:
|
||||||
text_segment.add_animation(**animation)
|
text_segment.add_animation(**animation)
|
||||||
|
|
||||||
self.draft.add_segment(text_segment)
|
# 向指定轨道添加文本片段
|
||||||
print("已完成")
|
self.draft.add_segment(segment=text_segment, track_name=track_name)
|
||||||
return
|
return
|
||||||
except Exception as exception:
|
except Exception:
|
||||||
print(f"发生异常:{str(exception)}")
|
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def _save(self) -> None:
|
def _add_sticker(
|
||||||
|
self,
|
||||||
|
track_name: str,
|
||||||
|
resource_id: str,
|
||||||
|
target_timerange: Tuple[Optional[int, str], Optional[int, str]],
|
||||||
|
clip_settings: Optional[Dict[str, Any]] = None,
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
添加贴纸片段
|
||||||
|
:param track_name: 轨道名称
|
||||||
|
:param resource_id: 贴纸 resource_id
|
||||||
|
:param target_timerange: 贴纸在轨道上的范围,包括开始时间和持续时长
|
||||||
|
:param clip_settings: 文本调节设置
|
||||||
|
:return: 无
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 构建贴纸
|
||||||
|
sticker_segment = capcut.StickerSegment(
|
||||||
|
resource_id=resource_id, # 可先将贴纸保存为我的预设,再在C:\Users\admin\AppData\Local\JianyingPro\User Data\Presets\Combination\Presets\我的预设?\preset_draft获取
|
||||||
|
target_timerange=trange(*target_timerange),
|
||||||
|
clip_settings=(
|
||||||
|
capcut.ClipSettings(**clip_settings) if clip_settings else None
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# 向指定轨道添加贴纸
|
||||||
|
self.draft.add_segment(segment=sticker_segment, track_name=track_name)
|
||||||
|
except Exception:
|
||||||
|
raise
|
||||||
|
|
||||||
|
def save(self) -> None:
|
||||||
"""保存草稿"""
|
"""保存草稿"""
|
||||||
try:
|
try:
|
||||||
self.draft.save()
|
self.draft.save()
|
||||||
print("草稿保存成功")
|
except Exception:
|
||||||
except Exception as exception:
|
|
||||||
print(f"发生异常:{str(exception)}")
|
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def create_audio_and_text(
|
def add_subtitle(
|
||||||
self,
|
self,
|
||||||
script: str,
|
script: str,
|
||||||
|
track_name: str = "subtitle",
|
||||||
timbre: Optional[str] = "女声-晓晓",
|
timbre: Optional[str] = "女声-晓晓",
|
||||||
rate: str = "+25%",
|
rate: str = "+25%",
|
||||||
volume: str = "+0%",
|
volume: str = "+0%",
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
根据脚本合成音频和文本素材
|
根据脚本生成文本和音频字幕
|
||||||
|
:param track_name: 轨道名称
|
||||||
:param script: 脚本
|
:param script: 脚本
|
||||||
:param timbre: 声音音色
|
:param timbre: 声音音色
|
||||||
:param rate: 语速
|
:param rate: 语速
|
||||||
:param volume: 音量
|
:param volume: 音量
|
||||||
:return: 无
|
:return: 无
|
||||||
"""
|
"""
|
||||||
print("正在根据脚本合成音频和文本素材...")
|
print("正在根据脚本生成文本和音频字幕...", end="")
|
||||||
# 添加音频和文本轨道
|
# 添加文本轨道
|
||||||
self.draft.add_track(track_type=capcut.TrackType.audio)
|
self.draft.add_track(
|
||||||
self.draft.add_track(track_type=capcut.TrackType.text)
|
track_type=capcut.TrackType.text,
|
||||||
|
track_name=(text_track_name := f"{track_name}:text"),
|
||||||
|
)
|
||||||
|
# 添加音频轨道
|
||||||
|
self.draft.add_track(
|
||||||
|
track_type=capcut.TrackType.audio,
|
||||||
|
track_name=(audio_track_name := f"{track_name}:audio"),
|
||||||
|
)
|
||||||
|
|
||||||
start = 0
|
start = 0
|
||||||
for content in script.split(","):
|
for content in script.split(","):
|
||||||
|
|
@ -361,24 +399,32 @@ class GenerateDraft:
|
||||||
name, duration = self.synthesizer.generate_audio(
|
name, duration = self.synthesizer.generate_audio(
|
||||||
content, timbre, rate, volume
|
content, timbre, rate, volume
|
||||||
)
|
)
|
||||||
# 构建音频片段
|
# 添加文本片段
|
||||||
self._add_audio(name=name, target_timerange=(start, duration))
|
|
||||||
# 构建文本片段
|
|
||||||
self._add_text(
|
self._add_text(
|
||||||
|
track_name=text_track_name,
|
||||||
content=content,
|
content=content,
|
||||||
timerange=(start, duration),
|
timerange=(start, duration),
|
||||||
style={"size": 12.0, "align": 1}, # 字号为12,对齐方式为水平居中
|
style={"size": 12.0, "align": 1}, # 字号为12,对齐方式为水平居中
|
||||||
clip_settings={"transform_y": -0.6}, # 垂直位移
|
clip_settings={"transform_y": -0.5}, # 垂直位移
|
||||||
effect="6896137858998930701", # 第二行第三列花字
|
effect={"effect_id": "6896137858998930701"}, # 第二行第三列花字
|
||||||
|
)
|
||||||
|
# 添加音频片段
|
||||||
|
self._add_audio(
|
||||||
|
track_name=audio_track_name,
|
||||||
|
name=name,
|
||||||
|
target_timerange=(start, duration),
|
||||||
)
|
)
|
||||||
start += duration
|
start += duration
|
||||||
|
|
||||||
self.audio_duration = start
|
# 更新脚本持续时长
|
||||||
|
self.duration = start
|
||||||
|
print("已完成")
|
||||||
|
|
||||||
def create_video(
|
def add_video(
|
||||||
self,
|
self,
|
||||||
|
track_name: str,
|
||||||
name: str,
|
name: str,
|
||||||
target_timerange: Tuple[Optional[int, str], Optional[int, str]],
|
target_timerange: Tuple[Optional[int, str], Optional[int, str]] = None,
|
||||||
source_timerange: Optional[
|
source_timerange: Optional[
|
||||||
Tuple[Optional[int, str], Optional[int, str]],
|
Tuple[Optional[int, str], Optional[int, str]],
|
||||||
] = None,
|
] = None,
|
||||||
|
|
@ -391,15 +437,183 @@ class GenerateDraft:
|
||||||
background_filling: Optional[Tuple[str, Any]] = None,
|
background_filling: Optional[Tuple[str, Any]] = None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
根据脚本合成音频和文本素材
|
向指定轨道添加视频/图片片段
|
||||||
:param script: 脚本
|
:param track_name: 轨道名称
|
||||||
:param timbre: 声音音色
|
:param name: 视频/图片素材名称
|
||||||
:param rate: 语速
|
:param target_timerange: 视频素材在轨道上的范围,包括开始时间和持续时长
|
||||||
:param volume: 音量
|
:param source_timerange: 截取视频素材范围,包括开始时间和持续时长
|
||||||
|
:param speed: 播放速度
|
||||||
|
:param volume: 播放音量
|
||||||
|
:param clip_settings: 图像调节设置
|
||||||
|
:param keyframes: 关键帧设置
|
||||||
|
:param animation: 动画设置
|
||||||
|
:param transition: 转场设置
|
||||||
|
:param background_filling: 背景填充设置
|
||||||
:return: 无
|
:return: 无
|
||||||
"""
|
"""
|
||||||
print("正在根据脚本合成音频和文本素材...")
|
# 预设图像调节设置
|
||||||
# 添加音频和文本轨道
|
CLIPSETTINGS = {
|
||||||
|
"logo": {
|
||||||
|
"scale_x": 0.2,
|
||||||
|
"scale_y": 0.2,
|
||||||
|
"transform_x": -0.68,
|
||||||
|
"transform_y": 0.82,
|
||||||
|
} # 等比缩放至20%,移动至左上角
|
||||||
|
}
|
||||||
|
# 根据轨道名称获取预设文本调节设置
|
||||||
|
if track_name in CLIPSETTINGS and not clip_settings:
|
||||||
|
clip_settings = CLIPSETTINGS.get(track_name)
|
||||||
|
|
||||||
|
print(f"正在向轨道 {track_name} 添加视频/图片片段...", end="")
|
||||||
|
track_name = f"{track_name}:video"
|
||||||
|
|
||||||
|
# 添加视频轨道
|
||||||
|
self.draft.add_track(track_type=capcut.TrackType.video, track_name=track_name)
|
||||||
|
|
||||||
|
# 添加视频片段
|
||||||
|
self._add_video(
|
||||||
|
track_name=track_name,
|
||||||
|
name=name,
|
||||||
|
target_timerange=(
|
||||||
|
target_timerange if target_timerange else (0, self.duration)
|
||||||
|
),
|
||||||
|
source_timerange=source_timerange,
|
||||||
|
speed=speed,
|
||||||
|
volume=volume,
|
||||||
|
clip_settings=clip_settings,
|
||||||
|
keyframes=keyframes,
|
||||||
|
animation=animation,
|
||||||
|
transition=transition,
|
||||||
|
background_filling=background_filling,
|
||||||
|
)
|
||||||
|
print("已完成")
|
||||||
|
|
||||||
|
def add_text(
|
||||||
|
self,
|
||||||
|
track_name: str,
|
||||||
|
content: str,
|
||||||
|
timerange: Tuple[Optional[int, str], Optional[int, str]] = None,
|
||||||
|
border: Optional[Dict[str, Any]] = None,
|
||||||
|
background: Optional[Dict[str, Any]] = None,
|
||||||
|
font: Optional[str] = None,
|
||||||
|
style: Optional[Dict[str, Any]] = None,
|
||||||
|
clip_settings: Optional[Dict[str, Any]] = None,
|
||||||
|
bubble: Optional[Dict[str, Any]] = None,
|
||||||
|
effect: Optional[str] = None,
|
||||||
|
animation: Optional[Dict[str, Any]] = None,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
向指定轨道添加文本片段
|
||||||
|
:param track_name: 轨道名称
|
||||||
|
:param content: 文本内容
|
||||||
|
:param timerange: 文本素材在轨道上的范围,包括开始时间和持续时长
|
||||||
|
:param border: 文本描边设置
|
||||||
|
:param background: 文本背景设置
|
||||||
|
:param font: 字体类型
|
||||||
|
:param style: 字体样式
|
||||||
|
:param clip_settings: 文本调节设置
|
||||||
|
:param bubble: 气泡设置
|
||||||
|
:param effect: 花字设置
|
||||||
|
:param animation: 动画设置
|
||||||
|
:return: 无
|
||||||
|
"""
|
||||||
|
# 预设文本描边设置
|
||||||
|
BORDER = {
|
||||||
|
"disclaimer": {
|
||||||
|
"width": 60.0,
|
||||||
|
} # 描边宽度为60
|
||||||
|
}
|
||||||
|
# 根据轨道名称获取预设文本描边设置
|
||||||
|
if track_name in BORDER and not border:
|
||||||
|
border = BORDER.get(track_name)
|
||||||
|
|
||||||
|
# 预设字体样式
|
||||||
|
STYLE = {
|
||||||
|
"disclaimer": {
|
||||||
|
"size": 8.0,
|
||||||
|
"align": 1,
|
||||||
|
} # 字号为8,对齐方式为水平居中
|
||||||
|
}
|
||||||
|
# 根据轨道名称获取预设字体样式
|
||||||
|
if track_name in STYLE and not style:
|
||||||
|
style = STYLE.get(track_name)
|
||||||
|
|
||||||
|
# 预设文本调节设置
|
||||||
|
CLIPSETTINGS = {
|
||||||
|
"disclaimer": {
|
||||||
|
"transform_y": -0.8,
|
||||||
|
} # 垂直位移
|
||||||
|
}
|
||||||
|
# 根据轨道名称获取预设字体样式
|
||||||
|
if track_name in CLIPSETTINGS and not clip_settings:
|
||||||
|
clip_settings = CLIPSETTINGS.get(track_name)
|
||||||
|
|
||||||
|
print(f"正在向轨道 {track_name} 添加文本片段...", end="")
|
||||||
|
track_name = f"{track_name}:text"
|
||||||
|
|
||||||
|
# 添加文本轨道
|
||||||
|
self.draft.add_track(track_type=capcut.TrackType.text, track_name=track_name)
|
||||||
|
|
||||||
|
# 添加文本片段
|
||||||
|
self._add_text(
|
||||||
|
track_name=track_name,
|
||||||
|
content=content,
|
||||||
|
timerange=(timerange if timerange else (0, self.duration)),
|
||||||
|
border=border,
|
||||||
|
background=background,
|
||||||
|
font=font,
|
||||||
|
style=style,
|
||||||
|
clip_settings=clip_settings,
|
||||||
|
bubble=bubble,
|
||||||
|
effect=effect,
|
||||||
|
animation=animation,
|
||||||
|
)
|
||||||
|
print("已完成")
|
||||||
|
|
||||||
|
def add_sticker(
|
||||||
|
self,
|
||||||
|
track_name: str,
|
||||||
|
resource_id: str,
|
||||||
|
target_timerange: Tuple[Optional[int, str], Optional[int, str]] = None,
|
||||||
|
clip_settings: Optional[Dict[str, Any]] = None,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
向指定轨道添加贴纸
|
||||||
|
:param track_name: 轨道名称
|
||||||
|
:param resource_id: 贴纸 resource_id,可先将贴纸保存为我的预设
|
||||||
|
:param target_timerange: 贴纸在轨道上的范围,包括开始时间和持续时长
|
||||||
|
:param clip_settings: 文本调节设置
|
||||||
|
:return: 无
|
||||||
|
"""
|
||||||
|
# 预设文本描边设置
|
||||||
|
RESOURCEID = {
|
||||||
|
"7026858083393588487": {
|
||||||
|
"scale_x": 0.2,
|
||||||
|
"scale_y": 0.2,
|
||||||
|
"transform_x": -0.75,
|
||||||
|
"transform_y": -0.78,
|
||||||
|
} # 等比缩放至20%,移动至左上角
|
||||||
|
}
|
||||||
|
# 根据轨道名称获取预设文本描边设置
|
||||||
|
if resource_id in RESOURCEID and not clip_settings:
|
||||||
|
clip_settings = RESOURCEID.get(resource_id)
|
||||||
|
|
||||||
|
print(f"正在向轨道 {track_name} 添加贴纸...", end="")
|
||||||
|
track_name = f"{track_name}:video"
|
||||||
|
|
||||||
|
# 添加贴纸轨道
|
||||||
|
self.draft.add_track(track_type=capcut.TrackType.sticker, track_name=track_name)
|
||||||
|
|
||||||
|
# 添加贴纸
|
||||||
|
self._add_sticker(
|
||||||
|
track_name=track_name,
|
||||||
|
resource_id=resource_id,
|
||||||
|
target_timerange=(
|
||||||
|
target_timerange if target_timerange else (0, self.duration)
|
||||||
|
),
|
||||||
|
clip_settings=clip_settings,
|
||||||
|
)
|
||||||
|
print("已完成")
|
||||||
|
|
||||||
|
|
||||||
# ======================== 调用示例(使用抽象后的方法) ========================
|
# ======================== 调用示例(使用抽象后的方法) ========================
|
||||||
|
|
@ -410,18 +624,31 @@ def execute_workflow():
|
||||||
name="demo2",
|
name="demo2",
|
||||||
)
|
)
|
||||||
|
|
||||||
# 根据脚本合成音频和文本素材
|
# 根据脚本生成文本和音频字幕
|
||||||
draft.generate_audio_and_text(
|
draft.add_subtitle(
|
||||||
script="所有人今天准备狂点外卖,是真的0.1元起一杯的霸王茶姬,还外卖到家怎么能不来一杯呢,现在淘宝闪购给大家发福利,最高22元无门槛红包,官方链接就在下方,奶茶脑袋快冲"
|
script="所有人今天准备狂点外卖,是真的0.1元起一杯的霸王茶姬,还外卖到家怎么能不来一杯呢,现在淘宝闪购给大家发福利,最高22元无门槛红包,官方链接就在下方,奶茶脑袋快冲"
|
||||||
)
|
)
|
||||||
|
# 为背景视频添加视频轨道并添加视频片段
|
||||||
|
draft.add_video(
|
||||||
|
track_name="background",
|
||||||
|
name="background.mp4",
|
||||||
|
clip_settings={"scale_x": 2.5, "scale_y": 2.5},
|
||||||
|
)
|
||||||
|
# 为logo添加视频轨道并添加图片片段
|
||||||
|
draft.add_video(
|
||||||
|
track_name="logo",
|
||||||
|
name="logo.png",
|
||||||
|
)
|
||||||
|
# 为免责声明添加视频轨道并添加图片片段
|
||||||
|
draft.add_text(
|
||||||
|
track_name="disclaimer",
|
||||||
|
content="支付需谨慎谨防诈骗\n仅限支付宝用户,详情以活动为准",
|
||||||
|
)
|
||||||
|
|
||||||
# 构建背景视频片段
|
draft.add_sticker(track_name="sticker", resource_id="7026858083393588487")
|
||||||
draft._add_video(name="background.mp4", target_timerange=(0, draft.audio_duration))
|
|
||||||
|
|
||||||
# draft._add_video(name="arrow.gif", target_timerange=(0, draft.audio_duration))
|
|
||||||
|
|
||||||
# 保存草稿
|
# 保存草稿
|
||||||
draft._save()
|
draft.save()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 34 KiB |
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
|
|
@ -175,6 +175,7 @@ if __name__ == "__main__":
|
||||||
AND identity_type = ?
|
AND identity_type = ?
|
||||||
AND identity_number = ?
|
AND identity_number = ?
|
||||||
AND ? BETWEEN commencement_date AND termination_date
|
AND ? BETWEEN commencement_date AND termination_date
|
||||||
|
ORDER BY termination_date
|
||||||
""",
|
""",
|
||||||
parameters=(
|
parameters=(
|
||||||
insurer_company,
|
insurer_company,
|
||||||
|
|
@ -1532,22 +1533,47 @@ if __name__ == "__main__":
|
||||||
# 银行卡识别并整合至赔案档案
|
# 银行卡识别并整合至赔案档案
|
||||||
bank_card_recognize(image)
|
bank_card_recognize(image)
|
||||||
|
|
||||||
|
# noinspection PyShadowingNames
|
||||||
|
def claim_adjust() -> None:
|
||||||
|
"""
|
||||||
|
赔案理算
|
||||||
|
:return: 无
|
||||||
|
"""
|
||||||
|
|
||||||
|
def select_person_policy():
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 基于据拒付规则评估,根据评估结果匹配处理方法
|
||||||
|
if not (result := rule_engine.evaluate(decision="拒付", inputs=dossier)):
|
||||||
|
# TODO: 若评估结果为空值则流转至人工处理
|
||||||
|
raise
|
||||||
|
|
||||||
|
dossier["adjustment_layer"].update(
|
||||||
|
{
|
||||||
|
"conclusion": result["conclusion"],
|
||||||
|
"explanation": result["explanation"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
receipts = pandas.DataFrame(dossier["receipts_layer"])
|
||||||
|
print(receipts["end_date"])
|
||||||
|
|
||||||
# 遍历工作目录中赔案目录并创建赔案档案(模拟自动化域就待自动化任务创建理赔档案)
|
# 遍历工作目录中赔案目录并创建赔案档案(模拟自动化域就待自动化任务创建理赔档案)
|
||||||
for case_path in [x for x in workplace_path.iterdir() if x.is_dir()]:
|
for claim_path in [x for x in workplace_path.iterdir() if x.is_dir()]:
|
||||||
# 初始化赔案档案(保险公司将提供投保公司、保险分公司和报案时间等,TPA作业系统签收后生成赔案号)
|
# 初始化赔案档案(保险公司将提供投保公司、保险分公司和报案时间等,TPA作业系统签收后生成赔案号)
|
||||||
dossier = {
|
dossier = {
|
||||||
"report_layer": {
|
"report_layer": {
|
||||||
"insurer_company": (
|
"insurer_company": (
|
||||||
insurer_company := "中银保险有限公司苏州分公司"
|
insurer_company := "中银保险有限公司苏州分公司"
|
||||||
), # 指定保险分公司
|
), # 指定保险分公司
|
||||||
"report_time": datetime(2005, 7, 25, 12, 0, 0), # 指定报案时间
|
"report_time": datetime(2025, 7, 25, 12, 0, 0), # 指定报案时间
|
||||||
"case_number": case_path.stem, # 设定:赔案目录名称为赔案号
|
"claim_number": claim_path.stem, # 设定:赔案目录名称为赔案号
|
||||||
}, # 报案层
|
}, # 报案层
|
||||||
"images_layer": [], # 影像件层
|
"images_layer": [], # 影像件层
|
||||||
"insured_person_layer": {}, # 出险人层
|
"insured_person_layer": {}, # 出险人层
|
||||||
"person_policies_layer": [], # 个单层
|
"person_policies_layer": [], # 个单层
|
||||||
"receipts_layer": [], # 票据层
|
"receipts_layer": [], # 票据层
|
||||||
"claim_layer": {}, # 理赔层
|
"adjustment_layer": {}, # 理算层
|
||||||
}
|
}
|
||||||
|
|
||||||
# 遍历赔案目录中影像件
|
# 遍历赔案目录中影像件
|
||||||
|
|
@ -1555,7 +1581,7 @@ if __name__ == "__main__":
|
||||||
sorted(
|
sorted(
|
||||||
[
|
[
|
||||||
x
|
x
|
||||||
for x in case_path.glob(pattern="*")
|
for x in claim_path.glob(pattern="*")
|
||||||
if x.is_file() and x.suffix.lower() in [".jpg", ".jpeg", ".png"]
|
if x.is_file() and x.suffix.lower() in [".jpg", ".jpeg", ".png"]
|
||||||
], # 实际作业亦仅支持JPG、JPEG或PNG
|
], # 实际作业亦仅支持JPG、JPEG或PNG
|
||||||
key=lambda x: x.stat().st_ctime, # 根据影像件创建时间顺序排序
|
key=lambda x: x.stat().st_ctime, # 根据影像件创建时间顺序排序
|
||||||
|
|
@ -1592,21 +1618,9 @@ if __name__ == "__main__":
|
||||||
# 就票据层按照事故止期和票据号顺序排序
|
# 就票据层按照事故止期和票据号顺序排序
|
||||||
dossier["receipts_layer"].sort(key=lambda x: (x["end_date"], x["number"]))
|
dossier["receipts_layer"].sort(key=lambda x: (x["end_date"], x["number"]))
|
||||||
|
|
||||||
# 根据拒付规则评估结果匹配处理方法
|
claim_adjust()
|
||||||
match result := rule_engine.evaluate(decision="拒付", inputs=dossier):
|
|
||||||
case _ if result.get("conclusion") == "拒付":
|
|
||||||
dossier["claim_layer"].update(
|
|
||||||
{
|
|
||||||
"conclusion": result["conclusion"],
|
|
||||||
"explanation": result["explanation"],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
case _ if result.get("conclusion") == "赔付":
|
|
||||||
pass
|
|
||||||
case _:
|
|
||||||
raise RuntimeError("未匹配到规则")
|
|
||||||
|
|
||||||
print(result)
|
print(dossier["adjustment_layer"])
|
||||||
|
|
||||||
for receipt in dossier["receipts_layer"]:
|
for receipt in dossier["receipts_layer"]:
|
||||||
print(receipt)
|
print(receipt)
|
||||||
|
|
|
||||||
|
|
@ -10,56 +10,26 @@
|
||||||
{
|
{
|
||||||
"_id": "172b5ab5-25c4-4785-9cf9-d106730ee55d",
|
"_id": "172b5ab5-25c4-4785-9cf9-d106730ee55d",
|
||||||
"_description": "",
|
"_description": "",
|
||||||
"268ca3e3-4f15-4c6a-a8f2-bbb5338c1f43": "true",
|
"268ca3e3-4f15-4c6a-a8f2-bbb5338c1f43": "startOf(date(insured_person_layer.commencement_date), \"day\") >= date(report_layer.report_time)",
|
||||||
"3320dce8-b9ca-415b-8b3c-adcfebb06f79": "\"证件有效起期大于等于报案时间\"",
|
"3320dce8-b9ca-415b-8b3c-adcfebb06f79": "\"证件有效起期大于等于报案时间\"",
|
||||||
"3404e5f2-f063-40d1-b27a-d3ecfb41c652": "\"中银保险有限公司苏州分公司\"",
|
"3404e5f2-f063-40d1-b27a-d3ecfb41c652": "\"中银保险有限公司苏州分公司\"",
|
||||||
"c5488920-2ae6-4123-92be-cd53cbc401f6": "startOf(date(insured_person_layer.commencement_date), \"day\") >= date(report_layer.report_time)",
|
"c9e2be65-c7a5-463b-8d62-0a5f503c844e": "\"R001\""
|
||||||
"c9e2be65-c7a5-463b-8d62-0a5f503c844e": "\"R001-1\""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "4497043e-4dfa-4edf-a65f-1fa4a998067d",
|
|
||||||
"_description": "",
|
|
||||||
"268ca3e3-4f15-4c6a-a8f2-bbb5338c1f43": "false",
|
|
||||||
"3320dce8-b9ca-415b-8b3c-adcfebb06f79": "",
|
|
||||||
"3404e5f2-f063-40d1-b27a-d3ecfb41c652": "\"中银保险有限公司苏州分公司\"",
|
|
||||||
"c5488920-2ae6-4123-92be-cd53cbc401f6": "startOf(date(insured_person_layer.commencement_date), \"day\") <= date(report_layer.report_time)",
|
|
||||||
"c9e2be65-c7a5-463b-8d62-0a5f503c844e": "\"R001-0\""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"_id": "bf01e2f6-572f-47f5-8773-d5b81a02e7c8",
|
"_id": "bf01e2f6-572f-47f5-8773-d5b81a02e7c8",
|
||||||
"_description": "",
|
"_description": "",
|
||||||
"268ca3e3-4f15-4c6a-a8f2-bbb5338c1f43": "true",
|
"268ca3e3-4f15-4c6a-a8f2-bbb5338c1f43": "endOf(date(insured_person_layer.termination_date), \"day\") <= date(report_layer.report_time)",
|
||||||
"3320dce8-b9ca-415b-8b3c-adcfebb06f79": "\"证件有效止期小于等于报案时间\"",
|
"3320dce8-b9ca-415b-8b3c-adcfebb06f79": "\"证件有效止期小于等于报案时间\"",
|
||||||
"3404e5f2-f063-40d1-b27a-d3ecfb41c652": "\"中银保险有限公司苏州分公司\"",
|
"3404e5f2-f063-40d1-b27a-d3ecfb41c652": "\"中银保险有限公司苏州分公司\"",
|
||||||
"c5488920-2ae6-4123-92be-cd53cbc401f6": "endOf(date(insured_person_layer.termination_date), \"day\") <= date(report_layer.report_time)",
|
"c9e2be65-c7a5-463b-8d62-0a5f503c844e": "\"R002\""
|
||||||
"c9e2be65-c7a5-463b-8d62-0a5f503c844e": "\"R002-1\""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "2a7d3c73-840b-4dc8-8363-07f81fa27579",
|
|
||||||
"_description": "",
|
|
||||||
"268ca3e3-4f15-4c6a-a8f2-bbb5338c1f43": "false",
|
|
||||||
"3320dce8-b9ca-415b-8b3c-adcfebb06f79": "",
|
|
||||||
"3404e5f2-f063-40d1-b27a-d3ecfb41c652": "\"中银保险有限公司苏州分公司\"",
|
|
||||||
"c5488920-2ae6-4123-92be-cd53cbc401f6": "endOf(date(insured_person_layer.termination_date), \"day\") >= date(report_layer.report_time)",
|
|
||||||
"c9e2be65-c7a5-463b-8d62-0a5f503c844e": "\"R002-0\""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"_id": "9fa2cd42-2297-4719-bf35-559026e0dc20",
|
"_id": "9fa2cd42-2297-4719-bf35-559026e0dc20",
|
||||||
"_description": "",
|
"_description": "",
|
||||||
"268ca3e3-4f15-4c6a-a8f2-bbb5338c1f43": "true",
|
"268ca3e3-4f15-4c6a-a8f2-bbb5338c1f43": "insured_person_layer.account != insured_person_layer.insured_person",
|
||||||
"3320dce8-b9ca-415b-8b3c-adcfebb06f79": "\"领款人和被保险人不一致\"",
|
"3320dce8-b9ca-415b-8b3c-adcfebb06f79": "\"领款人和被保险人不一致\"",
|
||||||
"3404e5f2-f063-40d1-b27a-d3ecfb41c652": "\"中银保险有限公司苏州分公司\"",
|
"3404e5f2-f063-40d1-b27a-d3ecfb41c652": "\"中银保险有限公司苏州分公司\"",
|
||||||
"c5488920-2ae6-4123-92be-cd53cbc401f6": "insured_person_layer.account != insured_person_layer.insured_person",
|
"c9e2be65-c7a5-463b-8d62-0a5f503c844e": "\"R003\""
|
||||||
"c9e2be65-c7a5-463b-8d62-0a5f503c844e": "\"R003-1\""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "76fe12c1-70fc-4221-9c78-6ea4d2d9af24",
|
|
||||||
"_description": "",
|
|
||||||
"268ca3e3-4f15-4c6a-a8f2-bbb5338c1f43": "false",
|
|
||||||
"3320dce8-b9ca-415b-8b3c-adcfebb06f79": "",
|
|
||||||
"3404e5f2-f063-40d1-b27a-d3ecfb41c652": "\"中银保险有限公司苏州分公司\"",
|
|
||||||
"c5488920-2ae6-4123-92be-cd53cbc401f6": "insured_person_layer.account == insured_person_layer.insured_person",
|
|
||||||
"c9e2be65-c7a5-463b-8d62-0a5f503c844e": "\"R003-0\""
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"inputs": [
|
"inputs": [
|
||||||
|
|
@ -67,26 +37,22 @@
|
||||||
"id": "3404e5f2-f063-40d1-b27a-d3ecfb41c652",
|
"id": "3404e5f2-f063-40d1-b27a-d3ecfb41c652",
|
||||||
"name": "保险分公司",
|
"name": "保险分公司",
|
||||||
"field": "report_layer.insurer_company"
|
"field": "report_layer.insurer_company"
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "c5488920-2ae6-4123-92be-cd53cbc401f6",
|
|
||||||
"name": "规则"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
|
||||||
"id": "268ca3e3-4f15-4c6a-a8f2-bbb5338c1f43",
|
|
||||||
"name": "命中",
|
|
||||||
"field": "hit"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "c9e2be65-c7a5-463b-8d62-0a5f503c844e",
|
"id": "c9e2be65-c7a5-463b-8d62-0a5f503c844e",
|
||||||
"name": "规则代码",
|
"name": "规则编码",
|
||||||
"field": "code"
|
"field": "code"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "268ca3e3-4f15-4c6a-a8f2-bbb5338c1f43",
|
||||||
|
"name": "命是规则",
|
||||||
|
"field": "hit"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "3320dce8-b9ca-415b-8b3c-adcfebb06f79",
|
"id": "3320dce8-b9ca-415b-8b3c-adcfebb06f79",
|
||||||
"name": "命是说明",
|
"name": "规则说明",
|
||||||
"field": "explanation"
|
"field": "explanation"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
@ -133,7 +99,7 @@
|
||||||
"rules": [
|
"rules": [
|
||||||
{
|
{
|
||||||
"_id": "09f82f88-75be-42b2-bd40-23457ec5aed9",
|
"_id": "09f82f88-75be-42b2-bd40-23457ec5aed9",
|
||||||
"_description": "命中规则数为0,例如保险分公司未配置规则",
|
"_description": "保险分公司未配置拒付规则",
|
||||||
"722fc57b-5166-4457-b9f0-c3437c6159b4": "len(hits) == 0",
|
"722fc57b-5166-4457-b9f0-c3437c6159b4": "len(hits) == 0",
|
||||||
"b2b97b2a-5e59-4c23-a116-7e49e59e65c3": "",
|
"b2b97b2a-5e59-4c23-a116-7e49e59e65c3": "",
|
||||||
"bd0ca899-7c74-40e4-9b61-1c058ef5e83f": ""
|
"bd0ca899-7c74-40e4-9b61-1c058ef5e83f": ""
|
||||||
|
|
@ -141,9 +107,9 @@
|
||||||
{
|
{
|
||||||
"_id": "843e7960-c5d8-4867-a283-b2641d31f3d7",
|
"_id": "843e7960-c5d8-4867-a283-b2641d31f3d7",
|
||||||
"_description": "",
|
"_description": "",
|
||||||
"722fc57b-5166-4457-b9f0-c3437c6159b4": "count(hits, #.hit) == 0",
|
"722fc57b-5166-4457-b9f0-c3437c6159b4": "none(hits, #.hit)",
|
||||||
"b2b97b2a-5e59-4c23-a116-7e49e59e65c3": "\"赔付\"",
|
"b2b97b2a-5e59-4c23-a116-7e49e59e65c3": "\"赔付\"",
|
||||||
"bd0ca899-7c74-40e4-9b61-1c058ef5e83f": ""
|
"bd0ca899-7c74-40e4-9b61-1c058ef5e83f": "\"\""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"_id": "e5dbd162-d188-4170-9b6b-9fc7077c47bf",
|
"_id": "e5dbd162-d188-4170-9b6b-9fc7077c47bf",
|
||||||
|
|
@ -188,8 +154,8 @@
|
||||||
"name": "function",
|
"name": "function",
|
||||||
"type": "functionNode",
|
"type": "functionNode",
|
||||||
"content": {
|
"content": {
|
||||||
"source": "import zen from 'zen';\n\n/** @type {Handler} **/\nexport const handler = async (input) => {\n const explanations = input.hits.reduce((arr, obj) => {\n const element = obj[\"explanation\"];\n if (element) {\n arr.push(element);\n }\n return arr; \n }, []);\n return {\n hits: input.hits,\n explanations: explanations,\n };\n};\n",
|
"source": "import zen from 'zen';\r\n\r\n/** @type {Handler} **/\r\nexport const handler = async (input) => {\r\n const explanations = input.hits.reduce((arr, obj) => {\r\n const hit = obj[\"hit\"];\r\n const explanation = obj[\"explanation\"];\r\n if (hit && explanation) {\r\n arr.push(explanation);\r\n }\r\n return arr; \r\n }, [])\r\n .join('; ');\r\n return {\r\n hits: input.hits,\r\n explanations: explanations,\r\n };\r\n};\r\n",
|
||||||
"omitNodes": true
|
"omitNodes": false
|
||||||
},
|
},
|
||||||
"position": {
|
"position": {
|
||||||
"x": 750,
|
"x": 750,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue