初始化提交
This commit is contained in:
0
app/models/__init__.py
Normal file
0
app/models/__init__.py
Normal file
25
app/models/const.py
Normal file
25
app/models/const.py
Normal file
@@ -0,0 +1,25 @@
|
||||
PUNCTUATIONS = [
|
||||
"?",
|
||||
",",
|
||||
".",
|
||||
"、",
|
||||
";",
|
||||
":",
|
||||
"!",
|
||||
"…",
|
||||
"?",
|
||||
",",
|
||||
"。",
|
||||
"、",
|
||||
";",
|
||||
":",
|
||||
"!",
|
||||
"...",
|
||||
]
|
||||
|
||||
TASK_STATE_FAILED = -1
|
||||
TASK_STATE_COMPLETE = 1
|
||||
TASK_STATE_PROCESSING = 4
|
||||
|
||||
FILE_TYPE_VIDEOS = ["mp4", "mov", "mkv", "webm"]
|
||||
FILE_TYPE_IMAGES = ["jpg", "jpeg", "png", "bmp"]
|
||||
28
app/models/exception.py
Normal file
28
app/models/exception.py
Normal file
@@ -0,0 +1,28 @@
|
||||
import traceback
|
||||
from typing import Any
|
||||
|
||||
from loguru import logger
|
||||
|
||||
|
||||
class HttpException(Exception):
|
||||
def __init__(
|
||||
self, task_id: str, status_code: int, message: str = "", data: Any = None
|
||||
):
|
||||
self.message = message
|
||||
self.status_code = status_code
|
||||
self.data = data
|
||||
# Retrieve the exception stack trace information.
|
||||
tb_str = traceback.format_exc().strip()
|
||||
if not tb_str or tb_str == "NoneType: None":
|
||||
msg = f"HttpException: {status_code}, {task_id}, {message}"
|
||||
else:
|
||||
msg = f"HttpException: {status_code}, {task_id}, {message}\n{tb_str}"
|
||||
|
||||
if status_code == 400:
|
||||
logger.warning(msg)
|
||||
else:
|
||||
logger.error(msg)
|
||||
|
||||
|
||||
class FileNotFoundException(Exception):
|
||||
pass
|
||||
342
app/models/schema.py
Normal file
342
app/models/schema.py
Normal file
@@ -0,0 +1,342 @@
|
||||
import warnings
|
||||
from enum import Enum
|
||||
from typing import Any, List, Optional, Union
|
||||
|
||||
import pydantic
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from app.config import config
|
||||
|
||||
# 忽略 Pydantic 的特定警告
|
||||
warnings.filterwarnings(
|
||||
"ignore",
|
||||
category=UserWarning,
|
||||
message="Field name.*shadows an attribute in parent.*",
|
||||
)
|
||||
|
||||
|
||||
class VideoConcatMode(str, Enum):
|
||||
random = "random"
|
||||
sequential = "sequential"
|
||||
|
||||
|
||||
class VideoTransitionMode(str, Enum):
|
||||
none = None
|
||||
shuffle = "Shuffle"
|
||||
fade_in = "FadeIn"
|
||||
fade_out = "FadeOut"
|
||||
slide_in = "SlideIn"
|
||||
slide_out = "SlideOut"
|
||||
|
||||
|
||||
class VideoAspect(str, Enum):
|
||||
landscape = "16:9"
|
||||
portrait = "9:16"
|
||||
square = "1:1"
|
||||
|
||||
def to_resolution(self):
|
||||
if self == VideoAspect.landscape.value:
|
||||
return 1920, 1080
|
||||
elif self == VideoAspect.portrait.value:
|
||||
return 1080, 1920
|
||||
elif self == VideoAspect.square.value:
|
||||
return 1080, 1080
|
||||
return 1080, 1920
|
||||
|
||||
|
||||
class _Config:
|
||||
arbitrary_types_allowed = True
|
||||
|
||||
|
||||
@pydantic.dataclasses.dataclass(config=_Config)
|
||||
class MaterialInfo:
|
||||
provider: str = "pexels"
|
||||
url: str = ""
|
||||
duration: int = 0
|
||||
|
||||
|
||||
class VideoParams(BaseModel):
|
||||
"""
|
||||
{
|
||||
"video_subject": "",
|
||||
"video_aspect": "横屏 16:9(西瓜视频)",
|
||||
"voice_name": "女生-晓晓",
|
||||
"bgm_name": "random",
|
||||
"font_name": "STHeitiMedium 黑体-中",
|
||||
"text_color": "#FFFFFF",
|
||||
"font_size": 60,
|
||||
"stroke_color": "#000000",
|
||||
"stroke_width": 1.5
|
||||
}
|
||||
"""
|
||||
|
||||
video_subject: str
|
||||
video_script: str = "" # Script used to generate the video
|
||||
video_terms: Optional[str | list] = None # Keywords used to generate the video
|
||||
video_aspect: Optional[VideoAspect] = VideoAspect.portrait.value
|
||||
video_concat_mode: Optional[VideoConcatMode] = VideoConcatMode.random.value
|
||||
video_transition_mode: Optional[VideoTransitionMode] = None
|
||||
video_clip_duration: Optional[int] = 5
|
||||
video_count: Optional[int] = 1
|
||||
|
||||
video_source: Optional[str] = "pexels"
|
||||
video_materials: Optional[List[MaterialInfo]] = (
|
||||
None # Materials used to generate the video
|
||||
)
|
||||
|
||||
custom_audio_file: Optional[str] = None # Custom audio file path, will ignore video_script and disable subtitle
|
||||
video_language: Optional[str] = "" # auto detect
|
||||
|
||||
voice_name: Optional[str] = ""
|
||||
voice_volume: Optional[float] = 1.0
|
||||
voice_rate: Optional[float] = 1.0
|
||||
bgm_type: Optional[str] = "random"
|
||||
bgm_file: Optional[str] = ""
|
||||
bgm_volume: Optional[float] = 0.2
|
||||
|
||||
subtitle_enabled: Optional[bool] = True
|
||||
subtitle_position: Optional[str] = config.ui.get("subtitle_position", "bottom") # top, bottom, center, custom
|
||||
custom_position: float = config.ui.get("custom_position", 70.0)
|
||||
font_name: Optional[str] = "STHeitiMedium.ttc"
|
||||
text_fore_color: Optional[str] = "#FFFFFF"
|
||||
text_background_color: Union[bool, str] = True
|
||||
|
||||
font_size: int = 60
|
||||
stroke_color: Optional[str] = "#000000"
|
||||
stroke_width: float = 1.5
|
||||
n_threads: Optional[int] = 2
|
||||
paragraph_number: int = Field(default=1, ge=1, le=10)
|
||||
video_script_prompt: str = Field(default="", max_length=2000)
|
||||
custom_system_prompt: str = Field(default="", max_length=8000)
|
||||
|
||||
|
||||
class SubtitleRequest(BaseModel):
|
||||
video_script: str
|
||||
video_language: Optional[str] = ""
|
||||
voice_name: Optional[str] = "zh-CN-XiaoxiaoNeural-Female"
|
||||
voice_volume: Optional[float] = 1.0
|
||||
voice_rate: Optional[float] = 1.2
|
||||
bgm_type: Optional[str] = "random"
|
||||
bgm_file: Optional[str] = ""
|
||||
bgm_volume: Optional[float] = 0.2
|
||||
subtitle_position: Optional[str] = config.ui.get("subtitle_position", "bottom")
|
||||
font_name: Optional[str] = "STHeitiMedium.ttc"
|
||||
text_fore_color: Optional[str] = "#FFFFFF"
|
||||
text_background_color: Union[bool, str] = True
|
||||
font_size: int = 60
|
||||
stroke_color: Optional[str] = "#000000"
|
||||
stroke_width: float = 1.5
|
||||
video_source: Optional[str] = "local"
|
||||
subtitle_enabled: Optional[str] = "true"
|
||||
|
||||
|
||||
class AudioRequest(BaseModel):
|
||||
video_script: str
|
||||
video_language: Optional[str] = ""
|
||||
voice_name: Optional[str] = "zh-CN-XiaoxiaoNeural-Female"
|
||||
voice_volume: Optional[float] = 1.0
|
||||
voice_rate: Optional[float] = 1.2
|
||||
bgm_type: Optional[str] = "random"
|
||||
bgm_file: Optional[str] = ""
|
||||
bgm_volume: Optional[float] = 0.2
|
||||
video_source: Optional[str] = "local"
|
||||
|
||||
|
||||
class VideoScriptParams:
|
||||
"""
|
||||
{
|
||||
"video_subject": "春天的花海",
|
||||
"video_language": "",
|
||||
"paragraph_number": 1,
|
||||
"video_script_prompt": "",
|
||||
"custom_system_prompt": ""
|
||||
}
|
||||
"""
|
||||
|
||||
video_subject: Optional[str] = "春天的花海"
|
||||
video_language: Optional[str] = ""
|
||||
paragraph_number: int = Field(default=1, ge=1, le=10)
|
||||
video_script_prompt: str = Field(default="", max_length=2000)
|
||||
custom_system_prompt: str = Field(default="", max_length=8000)
|
||||
|
||||
|
||||
class VideoTermsParams:
|
||||
"""
|
||||
{
|
||||
"video_subject": "",
|
||||
"video_script": "",
|
||||
"amount": 5
|
||||
}
|
||||
"""
|
||||
|
||||
video_subject: Optional[str] = "春天的花海"
|
||||
video_script: Optional[str] = (
|
||||
"春天的花海,如诗如画般展现在眼前。万物复苏的季节里,大地披上了一袭绚丽多彩的盛装。金黄的迎春、粉嫩的樱花、洁白的梨花、艳丽的郁金香……"
|
||||
)
|
||||
amount: Optional[int] = 5
|
||||
|
||||
|
||||
class BaseResponse(BaseModel):
|
||||
status: int = 200
|
||||
message: Optional[str] = "success"
|
||||
data: Any = None
|
||||
|
||||
|
||||
class TaskVideoRequest(VideoParams, BaseModel):
|
||||
pass
|
||||
|
||||
|
||||
class TaskQueryRequest(BaseModel):
|
||||
pass
|
||||
|
||||
|
||||
class VideoScriptRequest(VideoScriptParams, BaseModel):
|
||||
pass
|
||||
|
||||
|
||||
class VideoTermsRequest(VideoTermsParams, BaseModel):
|
||||
pass
|
||||
|
||||
|
||||
######################################################################################################
|
||||
######################################################################################################
|
||||
######################################################################################################
|
||||
######################################################################################################
|
||||
class TaskResponse(BaseResponse):
|
||||
class TaskResponseData(BaseModel):
|
||||
task_id: str
|
||||
|
||||
data: TaskResponseData
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"status": 200,
|
||||
"message": "success",
|
||||
"data": {"task_id": "6c85c8cc-a77a-42b9-bc30-947815aa0558"},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class TaskQueryResponse(BaseResponse):
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"status": 200,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"state": 1,
|
||||
"progress": 100,
|
||||
"videos": [
|
||||
"http://127.0.0.1:8080/tasks/6c85c8cc-a77a-42b9-bc30-947815aa0558/final-1.mp4"
|
||||
],
|
||||
"combined_videos": [
|
||||
"http://127.0.0.1:8080/tasks/6c85c8cc-a77a-42b9-bc30-947815aa0558/combined-1.mp4"
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class TaskDeletionResponse(BaseResponse):
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"status": 200,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"state": 1,
|
||||
"progress": 100,
|
||||
"videos": [
|
||||
"http://127.0.0.1:8080/tasks/6c85c8cc-a77a-42b9-bc30-947815aa0558/final-1.mp4"
|
||||
],
|
||||
"combined_videos": [
|
||||
"http://127.0.0.1:8080/tasks/6c85c8cc-a77a-42b9-bc30-947815aa0558/combined-1.mp4"
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class VideoScriptResponse(BaseResponse):
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"status": 200,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"video_script": "春天的花海,是大自然的一幅美丽画卷。在这个季节里,大地复苏,万物生长,花朵争相绽放,形成了一片五彩斑斓的花海..."
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class VideoTermsResponse(BaseResponse):
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"status": 200,
|
||||
"message": "success",
|
||||
"data": {"video_terms": ["sky", "tree"]},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class BgmRetrieveResponse(BaseResponse):
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"status": 200,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"files": [
|
||||
{
|
||||
"name": "output013.mp3",
|
||||
"size": 1891269,
|
||||
"file": "/MoneyPrinterTurbo/resource/songs/output013.mp3",
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class BgmUploadResponse(BaseResponse):
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"status": 200,
|
||||
"message": "success",
|
||||
"data": {"file": "/MoneyPrinterTurbo/resource/songs/example.mp3"},
|
||||
},
|
||||
}
|
||||
|
||||
class VideoMaterialRetrieveResponse(BaseResponse):
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"status": 200,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"files": [
|
||||
{
|
||||
"name": "example.mp4",
|
||||
"size": 12345678,
|
||||
"file": "/MoneyPrinterTurbo/resource/videos/example.mp4",
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
class VideoMaterialUploadResponse(BaseResponse):
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"status": 200,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"file": "/MoneyPrinterTurbo/resource/videos/example.mp4",
|
||||
},
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user