# -*- coding: utf-8 -*- import random from pathlib import Path from typing import Any, Dict, Literal from capcut import CapCutDraft class WorkFlow: """ 生成 CapCut草稿的工作流,支持: 1、初始化素材文件夹内所有素材 2、就工作流添加工作 3、基于工作流生成草稿 """ # noinspection PyShadowingNames def __init__( self, materials_folder_path: str, video_width: int = 1080, video_height: int = 1920, video_fps: int = 30, ): """ 初始化 :param materials_folder_path: 素材文件夹路径 :param video_width: 视频宽度,默认为 1080像素 :param video_height: 视频高度,默认为 1920像素 :param video_fps: 视频帧率(单位为帧/秒),默认为 30 """ print("正在初始化 DraftsGenerator...", end="") # noinspection PyBroadException try: self.materials_folder_path = Path(materials_folder_path) if not self.materials_folder_path.exists(): raise RuntimeError("素材文件夹路径不存在") # 构建项目名称 self.project_name = self.materials_folder_path.stem # 初始化工作流 self.workflow = [ "add_subtitles", "add_background_video", "add_logo", "add_statement", "add_sticker", "save", ] self.video_width = video_width self.video_height = video_height self.video_fps = video_fps # 初始化素材文件夹内所有素材 self.materials = {} self._init_materials() # 初始化所有工作配置 self.configurations = { "add_subtitles": { "text": self.materials["subtitles_text"], "timbre": [ "zh-CN-XiaoxiaoNeural", "zh-CN-XiaoyiNeural", "zh-CN-YunjianNeural", "zh-CN-YunxiNeural", ], "style": [{"size": 8}, {"size": 10}], "effect": [ {"effect_id": "7127561998556089631"}, {"effect_id": "7166467215410187552"}, {"effect_id": "6896138122774498567"}, ], }, "add_background_video": { "track_name": ["background_video"], "material_path": self.materials["background_video_material_path"], "clip_settings": [ None, { "transform_x": 0.2, }, { "transform_x": -0.2, }, { "transform_y": 0.2, }, { "transform_y": -0.2, }, ], }, "add_logo": { "track_name": ["logo"], "material_path": self.materials["logo_material_path"], "clip_settings": [ { "scale_x": 0.2, "scale_y": 0.2, "transform_x": -0.68, "transform_y": 0.82, }, { "scale_x": 0.2, "scale_y": 0.2, "transform_x": 0, "transform_y": 0.82, }, { "scale_x": 0.2, "scale_y": 0.2, "transform_x": 0.68, "transform_y": 0.82, }, ], }, "add_statement": { "track_name": ["statement"], "text": self.materials["statement_text"], "border": [ {"width": 40.0}, {"width": 50.0}, {"width": 60.0}, ], # 描边宽度 "style": [{"size": 8.0, "align": 1}], # 文本样式 "clip_settings": [ { "transform_y": -0.8, } ], # 图像调节设置 }, "add_sticker": { "track_name": ["sticker"], "resource_id": ["7026858083393588487"], "clip_settings": [ { "scale_x": 0.2, "scale_y": 0.2, "transform_x": -0.75, "transform_y": -0.78, } ], # 图像调节设置 }, } print("已完成") except Exception as exception: raise RuntimeError(f"发生异常:{str(exception)}") def _init_materials(self) -> None: """ 初始化素材文件夹内所有素材 :return: 无 """ # 字幕文本 subtitles_path = self.materials_folder_path / "字幕文本.txt" if subtitles_path.exists() and subtitles_path.is_file(): with open(subtitles_path, "r", encoding="utf-8") as file: subtitles_text = file.readlines() if not subtitles_text: raise RuntimeError("字幕文本为空") self.materials["subtitles_text"] = subtitles_text else: raise RuntimeError("字幕文本不存在") # 背景视频 background_videos_path = self.materials_folder_path / "背景视频" if background_videos_path.exists() and background_videos_path.is_dir(): background_video_material_path = [ file_path for file_path in background_videos_path.rglob("*.mp4") if file_path.is_file() ] if not background_video_material_path: raise RuntimeError("背景视频为空") self.materials["background_video_material_path"] = ( background_video_material_path ) else: raise RuntimeError("背景视频文件夹不存在") # 标识 logo_path = self.materials_folder_path / "标识.png" if logo_path.exists() and logo_path.is_file(): self.materials["logo_material_path"] = [logo_path] # 有且只有一张标识 else: raise RuntimeError("标识不存在") # 声明文本 statement_path = self.materials_folder_path / "声明文本.txt" if statement_path.exists() and statement_path.is_file(): with open(statement_path, "r", encoding="utf-8") as file: statement_text = file.readlines() if not statement_text: raise RuntimeError("声明文本为空") self.materials["statement_text"] = statement_text else: raise RuntimeError("声明不存在") def add_work( self, work: Literal[ "add_subtitles", "add_background_video", "add_logo", "add_statement", "add_sticker", "save", ], ) -> None: """ 就工作流添加工作 :param work: 工作,目前仅支持添加字幕、添加背景视频、添加标识、添加声明、添加贴纸和保存 :return: 无 """ self.workflow.append(work) def generate( self, counts: int = 1, ) -> None: """ 基于工作流生成草稿 :param counts: 生成 CapCut草稿数 :return: 无 """ for idx in range(counts): draft_name = self.project_name + f"{idx + 1:03d}" # 实例化 CapCutDraft draft = CapCutDraft( draft_name=draft_name, video_width=self.video_width, video_height=self.video_height, video_fps=self.video_fps, materials_folder_path=self.materials_folder_path, ) for work in self.workflow: match work: # 添加字幕 case "add_subtitles": draft.add_subtitles(**self._random(work=work)) # 添加背景视频 case "add_background_video": draft.add_video_segment(**self._random(work=work)) # 添加标识 case "add_logo": draft.add_video_segment(**self._random(work=work)) # 添加声明 case "add_statement": draft.add_text_segment(**self._random(work=work)) # 添加贴纸 case "add_sticker": draft.add_sticker(**self._random(work=work)) # 将草稿保存至 CapCut草稿文件夹内 case "save": draft.save() def _random( self, work: Literal[ "add_subtitles", "add_background_video", "add_logo", "add_statement", "add_sticker", ], ) -> Dict[str, Any]: """ 随机获取工作配置 :param work: 工作,包括添加字幕、添加背景视频、添加标识、添加声明和添加贴纸 :return: 工作配置 """ return { key: random.choice(value) for key, value in self.configurations[work].items() } a = WorkFlow(materials_folder_path=r"E:\projects\251225") a.generate(counts=5)