288 lines
10 KiB
Python
288 lines
10 KiB
Python
# -*- 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)
|