Python/剪映脚本生成自动化/workflow.py

288 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- 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)