mirror of
https://github.com/sun-guannan/CapCutAPI.git
synced 2025-11-25 03:15:00 +08:00
554 lines
23 KiB
Python
554 lines
23 KiB
Python
"""定义视频片段及其相关类
|
|
|
|
包含图像调节设置、动画效果、特效、转场等相关类
|
|
"""
|
|
|
|
import uuid
|
|
from copy import deepcopy
|
|
|
|
from typing import Optional, Literal, Union, overload
|
|
from typing import Dict, List, Tuple, Any
|
|
|
|
from pyJianYingDraft.metadata.capcut_effect_meta import CapCut_Video_character_effect_type, CapCut_Video_scene_effect_type
|
|
from pyJianYingDraft.metadata.capcut_mask_meta import CapCut_Mask_type
|
|
from settings import IS_CAPCUT_ENV
|
|
|
|
from .time_util import tim, Timerange
|
|
from .segment import Visual_segment, Clip_settings
|
|
from .local_materials import Video_material
|
|
from .animation import Segment_animations, Video_animation
|
|
|
|
from .metadata import Effect_meta, Effect_param_instance
|
|
from .metadata import Mask_meta, Mask_type, Filter_type, Transition_type, CapCut_Transition_type
|
|
from .metadata import Intro_type, Outro_type, Group_animation_type
|
|
from .metadata import CapCut_Intro_type, CapCut_Outro_type, CapCut_Group_animation_type
|
|
from .metadata import Video_scene_effect_type, Video_character_effect_type
|
|
|
|
|
|
class Mask:
|
|
"""蒙版对象"""
|
|
|
|
mask_meta: Mask_meta
|
|
"""蒙版元数据"""
|
|
global_id: str
|
|
"""蒙版全局id, 由程序自动生成"""
|
|
|
|
center_x: float
|
|
"""蒙版中心x坐标, 以半素材宽为单位"""
|
|
center_y: float
|
|
"""蒙版中心y坐标, 以半素材高为单位"""
|
|
width: float
|
|
height: float
|
|
aspect_ratio: float
|
|
|
|
rotation: float
|
|
invert: bool
|
|
feather: float
|
|
"""羽化程度, 0-1"""
|
|
round_corner: float
|
|
"""矩形蒙版的圆角, 0-1"""
|
|
|
|
def __init__(self, mask_meta: Mask_meta,
|
|
cx: float, cy: float, w: float, h: float,
|
|
ratio: float, rot: float, inv: bool, feather: float, round_corner: float):
|
|
self.mask_meta = mask_meta
|
|
self.global_id = uuid.uuid4().hex
|
|
|
|
self.center_x, self.center_y = cx, cy
|
|
self.width, self.height = w, h
|
|
self.aspect_ratio = ratio
|
|
|
|
self.rotation = rot
|
|
self.invert = inv
|
|
self.feather = feather
|
|
self.round_corner = round_corner
|
|
|
|
def export_json(self) -> Dict[str, Any]:
|
|
return {
|
|
"config": {
|
|
"aspectRatio": self.aspect_ratio,
|
|
"centerX": self.center_x,
|
|
"centerY": self.center_y,
|
|
"feather": self.feather,
|
|
"height": self.height,
|
|
"invert": self.invert,
|
|
"rotation": self.rotation,
|
|
"roundCorner": self.round_corner,
|
|
"width": self.width
|
|
},
|
|
"category": "video",
|
|
"category_id": "",
|
|
"category_name": "",
|
|
"id": self.global_id,
|
|
"name": self.mask_meta.name,
|
|
"platform": "all",
|
|
"position_info": "",
|
|
"resource_type": self.mask_meta.resource_type,
|
|
"resource_id": self.mask_meta.resource_id,
|
|
"type": "mask"
|
|
# 不导出path字段
|
|
}
|
|
|
|
class Video_effect:
|
|
"""视频特效素材"""
|
|
|
|
name: str
|
|
"""特效名称"""
|
|
global_id: str
|
|
"""特效全局id, 由程序自动生成"""
|
|
effect_id: str
|
|
"""某种特效id, 由剪映本身提供"""
|
|
resource_id: str
|
|
"""资源id, 由剪映本身提供"""
|
|
|
|
effect_type: Literal["video_effect", "face_effect"]
|
|
apply_target_type: Literal[0, 2]
|
|
"""应用目标类型, 0: 片段, 2: 全局"""
|
|
|
|
adjust_params: List[Effect_param_instance]
|
|
|
|
def __init__(self, effect_meta: Union[Video_scene_effect_type, Video_character_effect_type],
|
|
params: Optional[List[Optional[float]]] = None, *,
|
|
apply_target_type: Literal[0, 2] = 0):
|
|
"""根据给定的特效元数据及参数列表构造一个视频特效对象, params的范围是0~100"""
|
|
|
|
self.name = effect_meta.value.name
|
|
self.global_id = uuid.uuid4().hex
|
|
self.effect_id = effect_meta.value.effect_id
|
|
self.resource_id = effect_meta.value.resource_id
|
|
self.adjust_params = []
|
|
|
|
if IS_CAPCUT_ENV:
|
|
if isinstance(effect_meta, CapCut_Video_scene_effect_type):
|
|
self.effect_type = "video_effect"
|
|
elif isinstance(effect_meta, CapCut_Video_character_effect_type):
|
|
self.effect_type = "face_effect"
|
|
else:
|
|
raise TypeError("Invalid effect meta type %s" % type(effect_meta))
|
|
else:
|
|
if isinstance(effect_meta, Video_scene_effect_type):
|
|
self.effect_type = "video_effect"
|
|
elif isinstance(effect_meta, Video_character_effect_type):
|
|
self.effect_type = "face_effect"
|
|
else:
|
|
raise TypeError("Invalid effect meta type %s" % type(effect_meta))
|
|
|
|
self.apply_target_type = apply_target_type
|
|
|
|
self.adjust_params = effect_meta.value.parse_params(params)
|
|
|
|
def export_json(self) -> Dict[str, Any]:
|
|
return {
|
|
"adjust_params": [param.export_json() for param in self.adjust_params],
|
|
"apply_target_type": self.apply_target_type,
|
|
"apply_time_range": None,
|
|
"category_id": "", # 一律设为空
|
|
"category_name": "", # 一律设为空
|
|
"common_keyframes": [],
|
|
"disable_effect_faces": [],
|
|
"effect_id": self.effect_id,
|
|
"formula_id": "",
|
|
"id": self.global_id,
|
|
"name": self.name,
|
|
"platform": "all",
|
|
"render_index": 11000,
|
|
"resource_id": self.resource_id,
|
|
"source_platform": 0,
|
|
"time_range": None,
|
|
"track_render_index": 0,
|
|
"type": self.effect_type,
|
|
"value": 1.0,
|
|
"version": ""
|
|
# 不导出path、request_id和algorithm_artifact_path字段
|
|
}
|
|
|
|
class Filter:
|
|
"""滤镜素材"""
|
|
|
|
global_id: str
|
|
"""滤镜全局id, 由程序自动生成"""
|
|
|
|
effect_meta: Effect_meta
|
|
"""滤镜的元数据"""
|
|
intensity: float
|
|
"""滤镜强度(滤镜的唯一参数)"""
|
|
|
|
apply_target_type: Literal[0, 2]
|
|
"""应用目标类型, 0: 片段, 2: 全局"""
|
|
|
|
def __init__(self, meta: Effect_meta, intensity: float, *,
|
|
apply_target_type: Literal[0, 2] = 0):
|
|
"""根据给定的滤镜元数据及强度构造滤镜素材对象"""
|
|
|
|
self.global_id = uuid.uuid4().hex
|
|
self.effect_meta = meta
|
|
self.intensity = intensity
|
|
self.apply_target_type = apply_target_type
|
|
|
|
def export_json(self) -> Dict[str, Any]:
|
|
return {
|
|
"adjust_params": [],
|
|
"algorithm_artifact_path": "",
|
|
"apply_target_type": self.apply_target_type,
|
|
"bloom_params": None,
|
|
"category_id": "", # 一律设为空
|
|
"category_name": "", # 一律设为空
|
|
"color_match_info": {
|
|
"source_feature_path": "",
|
|
"target_feature_path": "",
|
|
"target_image_path": ""
|
|
},
|
|
"effect_id": self.effect_meta.effect_id,
|
|
"enable_skin_tone_correction": False,
|
|
"exclusion_group": [],
|
|
"face_adjust_params": [],
|
|
"formula_id": "",
|
|
"id": self.global_id,
|
|
"intensity_key": "",
|
|
"multi_language_current": "",
|
|
"name": self.effect_meta.name,
|
|
"panel_id": "",
|
|
"platform": "all",
|
|
"resource_id": self.effect_meta.resource_id,
|
|
"source_platform": 1,
|
|
"sub_type": "none",
|
|
"time_range": None,
|
|
"type": "filter",
|
|
"value": self.intensity,
|
|
"version": ""
|
|
# 不导出path和request_id
|
|
}
|
|
|
|
class Transition:
|
|
"""转场对象"""
|
|
|
|
name: str
|
|
"""转场名称"""
|
|
global_id: str
|
|
"""转场全局id, 由程序自动生成"""
|
|
effect_id: str
|
|
"""转场效果id, 由剪映本身提供"""
|
|
resource_id: str
|
|
"""资源id, 由剪映本身提供"""
|
|
|
|
duration: int
|
|
"""转场持续时间, 单位为微秒"""
|
|
is_overlap: bool
|
|
"""是否与上一个片段重叠(?)"""
|
|
|
|
def __init__(self, effect_meta: Union[Transition_type, CapCut_Transition_type], duration: Optional[int] = None):
|
|
"""根据给定的转场元数据及持续时间构造一个转场对象"""
|
|
self.name = effect_meta.value.name
|
|
self.global_id = uuid.uuid4().hex
|
|
self.effect_id = effect_meta.value.effect_id
|
|
self.resource_id = effect_meta.value.resource_id
|
|
|
|
self.duration = duration if duration is not None else effect_meta.value.default_duration
|
|
self.is_overlap = effect_meta.value.is_overlap
|
|
|
|
def export_json(self) -> Dict[str, Any]:
|
|
return {
|
|
"category_id": "", # 一律设为空
|
|
"category_name": "", # 一律设为空
|
|
"duration": self.duration,
|
|
"effect_id": self.effect_id,
|
|
"id": self.global_id,
|
|
"is_overlap": self.is_overlap,
|
|
"name": self.name,
|
|
"platform": "all",
|
|
"resource_id": self.resource_id,
|
|
"type": "transition"
|
|
# 不导出path和request_id字段
|
|
}
|
|
|
|
class BackgroundFilling:
|
|
"""背景填充对象"""
|
|
|
|
global_id: str
|
|
"""背景填充全局id, 由程序自动生成"""
|
|
fill_type: Literal["canvas_blur", "canvas_color"]
|
|
"""背景填充类型"""
|
|
blur: float
|
|
"""模糊程度, 0-1"""
|
|
color: str
|
|
"""背景颜色, 格式为'#RRGGBBAA'"""
|
|
|
|
def __init__(self, fill_type: Literal["canvas_blur", "canvas_color"], blur: float, color: str):
|
|
self.global_id = uuid.uuid4().hex
|
|
self.fill_type = fill_type
|
|
self.blur = blur
|
|
self.color = color
|
|
|
|
def export_json(self) -> Dict[str, Any]:
|
|
return {
|
|
"id": self.global_id,
|
|
"type": self.fill_type,
|
|
"blur": self.blur,
|
|
"color": self.color,
|
|
"source_platform": 0,
|
|
}
|
|
|
|
class Video_segment(Visual_segment):
|
|
"""安放在轨道上的一个视频/图片片段"""
|
|
|
|
material_instance: Video_material
|
|
"""素材实例"""
|
|
material_size: Tuple[int, int]
|
|
"""素材尺寸"""
|
|
|
|
effects: List[Video_effect]
|
|
"""特效列表
|
|
|
|
在放入轨道时自动添加到素材列表中
|
|
"""
|
|
filters: List[Filter]
|
|
"""滤镜列表
|
|
|
|
在放入轨道时自动添加到素材列表中
|
|
"""
|
|
mask: Optional[Mask]
|
|
"""蒙版实例, 可能为空
|
|
|
|
在放入轨道时自动添加到素材列表中
|
|
"""
|
|
transition: Optional[Transition]
|
|
"""转场实例, 可能为空
|
|
|
|
在放入轨道时自动添加到素材列表中
|
|
"""
|
|
background_filling: Optional[BackgroundFilling]
|
|
"""背景填充实例, 可能为空
|
|
|
|
在放入轨道时自动添加到素材列表中
|
|
"""
|
|
|
|
visible: Optional[bool]
|
|
"""是否可见
|
|
默认为True
|
|
"""
|
|
|
|
# TODO: material参数接受path进行便捷构造
|
|
def __init__(self, material: Video_material, target_timerange: Timerange, *,
|
|
source_timerange: Optional[Timerange] = None, speed: Optional[float] = None, volume: float = 1.0,
|
|
clip_settings: Optional[Clip_settings] = None):
|
|
"""利用给定的视频/图片素材构建一个轨道片段, 并指定其时间信息及图像调节设置
|
|
|
|
Args:
|
|
material (`Video_material`): 素材实例
|
|
target_timerange (`Timerange`): 片段在轨道上的目标时间范围
|
|
source_timerange (`Timerange`, optional): 截取的素材片段的时间范围, 默认从开头根据`speed`截取与`target_timerange`等长的一部分
|
|
speed (`float`, optional): 播放速度, 默认为1.0. 此项与`source_timerange`同时指定时, 将覆盖`target_timerange`中的时长
|
|
volume (`float`, optional): 音量, 默认为1.0
|
|
clip_settings (`Clip_settings`, optional): 图像调节设置, 默认不作任何变换
|
|
|
|
Raises:
|
|
`ValueError`: 指定的或计算出的`source_timerange`超出了素材的时长范围
|
|
"""
|
|
# if source_timerange is not None and speed is not None:
|
|
# target_timerange = Timerange(target_timerange.start, round(source_timerange.duration / speed))
|
|
# elif source_timerange is not None and speed is None:
|
|
# speed = source_timerange.duration / target_timerange.duration
|
|
# else: # source_timerange is None
|
|
# speed = speed if speed is not None else 1.0
|
|
# source_timerange = Timerange(0, round(target_timerange.duration * speed))
|
|
|
|
# if source_timerange.end > material.duration:
|
|
# source_timerange = Timerange(source_timerange.start, material.duration - source_timerange.start)
|
|
# # 重新计算目标时间范围
|
|
# target_timerange = Timerange(target_timerange.start, round(source_timerange.duration / speed))
|
|
|
|
super().__init__(material.material_id, source_timerange, target_timerange, speed, volume, clip_settings=clip_settings)
|
|
|
|
self.material_instance = deepcopy(material)
|
|
self.material_size = (material.width, material.height)
|
|
self.effects = []
|
|
self.filters = []
|
|
self.transition = None
|
|
self.mask = None
|
|
self.background_filling = None
|
|
|
|
def add_animation(self, animation_type: Union[Intro_type, Outro_type, Group_animation_type, CapCut_Intro_type, CapCut_Outro_type, CapCut_Group_animation_type],
|
|
duration: Optional[Union[int, str]] = None) -> "Video_segment":
|
|
"""将给定的入场/出场/组合动画添加到此片段的动画列表中
|
|
|
|
Args:
|
|
animation_type (`Intro_type`, `Outro_type`, or `Group_animation_type`): 动画类型
|
|
duration (`int` or `str`, optional): 动画持续时间, 单位为微秒. 若传入字符串则会调用`tim()`函数进行解析.
|
|
若不指定则使用动画类型定义的默认值. 理论上只适用于入场和出场动画.
|
|
"""
|
|
if duration is not None:
|
|
duration = tim(duration)
|
|
if (isinstance(animation_type, Intro_type) or isinstance(animation_type, CapCut_Intro_type)):
|
|
start = 0
|
|
duration = duration or animation_type.value.duration
|
|
elif isinstance(animation_type, Outro_type) or isinstance(animation_type, CapCut_Outro_type):
|
|
duration = duration or animation_type.value.duration
|
|
start = self.target_timerange.duration - duration
|
|
elif isinstance(animation_type, Group_animation_type) or isinstance(animation_type, CapCut_Group_animation_type):
|
|
start = 0
|
|
duration = duration or self.target_timerange.duration
|
|
else:
|
|
raise TypeError("Invalid animation type %s" % type(animation_type))
|
|
|
|
if self.animations_instance is None:
|
|
self.animations_instance = Segment_animations()
|
|
self.extra_material_refs.append(self.animations_instance.animation_id)
|
|
|
|
self.animations_instance.add_animation(Video_animation(animation_type, start, duration))
|
|
|
|
return self
|
|
|
|
def add_effect(self, effect_type: Union[Video_scene_effect_type, Video_character_effect_type],
|
|
params: Optional[List[Optional[float]]] = None) -> "Video_segment":
|
|
"""为视频片段添加一个作用于整个片段的特效
|
|
|
|
Args:
|
|
effect_type (`Video_scene_effect_type` or `Video_character_effect_type`): 特效类型
|
|
params (`List[Optional[float]]`, optional): 特效参数列表, 参数列表中未提供或为None的项使用默认值.
|
|
参数取值范围(0~100)与剪映中一致. 某个特效类型有何参数以及具体参数顺序以枚举类成员的annotation为准.
|
|
|
|
Raises:
|
|
`ValueError`: 提供的参数数量超过了该特效类型的参数数量, 或参数值超出范围.
|
|
"""
|
|
if params is not None and len(params) > len(effect_type.value.params):
|
|
raise ValueError("为音频效果 %s 传入了过多的参数" % effect_type.value.name)
|
|
|
|
effect_inst = Video_effect(effect_type, params)
|
|
self.effects.append(effect_inst)
|
|
self.extra_material_refs.append(effect_inst.global_id)
|
|
|
|
return self
|
|
|
|
def add_filter(self, filter_type: Filter_type, intensity: float = 100.0) -> "Video_segment":
|
|
"""为视频片段添加一个滤镜
|
|
|
|
Args:
|
|
filter_type (`Filter_type`): 滤镜类型
|
|
intensity (`float`, optional): 滤镜强度(0-100), 仅当所选滤镜能够调节强度时有效. 默认为100.
|
|
"""
|
|
filter_inst = Filter(filter_type.value, intensity / 100.0) # 转化为0~1范围
|
|
self.filters.append(filter_inst)
|
|
self.extra_material_refs.append(filter_inst.global_id)
|
|
|
|
return self
|
|
|
|
def add_mask(self, draft: "Script_file", mask_type: Union[Mask_type, CapCut_Mask_type], *, center_x: float = 0.0, center_y: float = 0.0, size: float = 0.5,
|
|
rotation: float = 0.0, feather: float = 0.0, invert: bool = False,
|
|
rect_width: Optional[float] = None, round_corner: Optional[float] = None) -> "Video_segment":
|
|
"""为视频片段添加蒙版
|
|
|
|
Args:
|
|
mask_type (`Mask_type`): 蒙版类型
|
|
center_x (`float`, optional): 蒙版中心点X坐标(以素材的像素为单位), 默认设置在素材中心
|
|
center_y (`float`, optional): 蒙版中心点Y坐标(以素材的像素为单位), 默认设置在素材中心
|
|
size (`float`, optional): 蒙版的"主要尺寸"(镜面的可视部分高度/圆形直径/爱心高度等), 以占素材高度的比例表示, 默认为0.5
|
|
rotation (`float`, optional): 蒙版顺时针旋转的**角度**, 默认不旋转
|
|
feather (`float`, optional): 蒙版的羽化参数, 取值范围0~100, 默认无羽化
|
|
invert (`bool`, optional): 是否反转蒙版, 默认不反转
|
|
rect_width (`float`, optional): 矩形蒙版的宽度, 仅在蒙版类型为矩形时允许设置, 以占素材宽度的比例表示, 默认与`size`相同
|
|
round_corner (`float`, optional): 矩形蒙版的圆角参数, 仅在蒙版类型为矩形时允许设置, 取值范围0~100, 默认为0
|
|
|
|
Raises:
|
|
`ValueError`: 试图添加多个蒙版或不正确地设置了`rect_width`及`round_corner`
|
|
"""
|
|
|
|
if self.mask is not None:
|
|
raise ValueError("当前片段已有蒙版, 不能再添加新的蒙版")
|
|
if (rect_width is not None or round_corner is not None) and (mask_type != Mask_type.矩形 and mask_type != CapCut_Mask_type.Rectangle):
|
|
raise ValueError("`rect_width` 以及 `round_corner` 仅在蒙版类型为矩形时允许设置")
|
|
if rect_width is None and (mask_type == Mask_type.矩形 or mask_type == CapCut_Mask_type.Rectangle):
|
|
rect_width = size
|
|
if round_corner is None:
|
|
round_corner = 0
|
|
|
|
# 获取草稿的宽高,而不是使用素材的宽高
|
|
draft_width = draft.width
|
|
draft_height = draft.height
|
|
|
|
width = rect_width or size * draft_height * mask_type.value.default_aspect_ratio / draft_width
|
|
self.mask = Mask(mask_type.value, center_x / (draft_width / 2), center_y / (draft_height / 2),
|
|
w=width, h=size, ratio=mask_type.value.default_aspect_ratio,
|
|
rot=rotation, inv=invert, feather=feather/100, round_corner=round_corner/100)
|
|
self.extra_material_refs.append(self.mask.global_id)
|
|
return self
|
|
|
|
def add_transition(self, transition_type: Union[Transition_type, CapCut_Transition_type], *, duration: Optional[Union[int, str]] = None) -> "Video_segment":
|
|
"""为视频片段添加转场, 注意转场应当添加在**前面的**片段上
|
|
|
|
Args:
|
|
transition_type (`Transition_type` or `CapCut_Transition_type`): 转场类型
|
|
duration (`int` or `str`, optional): 转场持续时间, 单位为微秒. 若传入字符串则会调用`tim()`函数进行解析. 若不指定则使用转场类型定义的默认值.
|
|
|
|
Raises:
|
|
`ValueError`: 试图添加多个转场.
|
|
"""
|
|
if self.transition is not None:
|
|
raise ValueError("当前片段已有转场, 不能再添加新的转场")
|
|
if isinstance(duration, str): duration = tim(duration)
|
|
|
|
self.transition = Transition(transition_type, duration)
|
|
self.extra_material_refs.append(self.transition.global_id)
|
|
return self
|
|
|
|
def add_background_filling(self, fill_type: Literal["blur", "color"], blur: float = 0.0625, color: str = "#00000000") -> "Video_segment":
|
|
"""为视频片段添加背景填充
|
|
|
|
注意: 背景填充仅对底层视频轨道上的片段生效
|
|
|
|
Args:
|
|
fill_type (`blur` or `color`): 填充类型, `blur`表示模糊, `color`表示颜色.
|
|
blur (`float`, optional): 模糊程度, 0.0-1.0. 仅在`fill_type`为`blur`时有效. 剪映中的四档模糊数值分别为0.0625, 0.375, 0.75和1.0, 默认为0.0625.
|
|
color (`str`, optional): 填充颜色, 格式为'#RRGGBBAA'. 仅在`fill_type`为`color`时有效.
|
|
|
|
Raises:
|
|
`ValueError`: 当前片段已有背景填充效果或`fill_type`无效.
|
|
"""
|
|
if self.background_filling is not None:
|
|
raise ValueError("当前片段已有背景填充效果")
|
|
|
|
if fill_type == "blur":
|
|
self.background_filling = BackgroundFilling("canvas_blur", blur, color)
|
|
elif fill_type == "color":
|
|
self.background_filling = BackgroundFilling("canvas_color", blur, color)
|
|
else:
|
|
raise ValueError(f"无效的背景填充类型 {fill_type}")
|
|
|
|
self.extra_material_refs.append(self.background_filling.global_id)
|
|
return self
|
|
|
|
def export_json(self) -> Dict[str, Any]:
|
|
json_dict = super().export_json()
|
|
json_dict.update({
|
|
"hdr_settings": {"intensity": 1.0, "mode": 1, "nits": 1000},
|
|
})
|
|
return json_dict
|
|
|
|
class Sticker_segment(Visual_segment):
|
|
"""安放在轨道上的一个贴纸片段"""
|
|
|
|
resource_id: str
|
|
"""贴纸资源id"""
|
|
|
|
def __init__(self, resource_id: str, target_timerange: Timerange, *, clip_settings: Optional[Clip_settings] = None):
|
|
"""根据贴纸resource_id构建一个贴纸片段, 并指定其时间信息及图像调节设置
|
|
|
|
片段创建完成后, 可通过`Script_file.add_segment`方法将其添加到轨道中
|
|
|
|
Args:
|
|
resource_id (`str`): 贴纸resource_id, 可通过`Script_file.inspect_material`从模板中获取
|
|
target_timerange (`Timerange`): 片段在轨道上的目标时间范围
|
|
clip_settings (`Clip_settings`, optional): 图像调节设置, 默认不作任何变换
|
|
"""
|
|
super().__init__(uuid.uuid4().hex, None, target_timerange, 1.0, 1.0, clip_settings=clip_settings)
|
|
self.resource_id = resource_id
|
|
|
|
def export_material(self) -> Dict[str, Any]:
|
|
"""创建极简的贴纸素材对象, 以此不再单独定义贴纸素材类"""
|
|
return {
|
|
"id": self.material_id,
|
|
"resource_id": self.resource_id,
|
|
"sticker_id": self.resource_id,
|
|
"source_platform": 1,
|
|
"type": "sticker",
|
|
}
|