可用的海报模块
This commit is contained in:
parent
29551abc2e
commit
c988ea2911
@ -20,6 +20,8 @@ class PosterGenerateRequest(BaseModel):
|
|||||||
scenicSpotId: Optional[int] = Field(None, description="景区ID,用于AI生成内容")
|
scenicSpotId: Optional[int] = Field(None, description="景区ID,用于AI生成内容")
|
||||||
numVariations: int = Field(3, description="要生成的海报变体数量, 默认为3", ge=1, le=5)
|
numVariations: int = Field(3, description="要生成的海报变体数量, 默认为3", ge=1, le=5)
|
||||||
forceLlmGeneration: bool = Field(False, description="是否强制使用LLM重新生成内容")
|
forceLlmGeneration: bool = Field(False, description="是否强制使用LLM重新生成内容")
|
||||||
|
generatePsd: bool = Field(False, description="是否生成PSD分层文件")
|
||||||
|
psdOutputPath: Optional[str] = Field(None, description="PSD文件输出路径(可选,默认自动生成)")
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
json_schema_extra = {
|
json_schema_extra = {
|
||||||
@ -28,6 +30,8 @@ class PosterGenerateRequest(BaseModel):
|
|||||||
"imagesBase64": "",
|
"imagesBase64": "",
|
||||||
"numVariations": 1,
|
"numVariations": 1,
|
||||||
"forceLlmGeneration":False,
|
"forceLlmGeneration":False,
|
||||||
|
"generatePsd": True,
|
||||||
|
"psdOutputPath": "custom_poster.psd",
|
||||||
"contentId":1,
|
"contentId":1,
|
||||||
"productId":1,
|
"productId":1,
|
||||||
"scenicSpotId":1,
|
"scenicSpotId":1,
|
||||||
@ -65,6 +69,7 @@ class PosterGenerateResponse(BaseModel):
|
|||||||
requestId: str
|
requestId: str
|
||||||
templateId: str
|
templateId: str
|
||||||
resultImagesBase64: List[Dict[str, Any]] = Field(description="生成的海报图像(base64编码)列表")
|
resultImagesBase64: List[Dict[str, Any]] = Field(description="生成的海报图像(base64编码)列表")
|
||||||
|
psdFiles: Optional[List[Dict[str, Any]]] = Field(None, description="生成的PSD文件信息列表")
|
||||||
metadata: Dict[str, Any] = Field(default_factory=dict)
|
metadata: Dict[str, Any] = Field(default_factory=dict)
|
||||||
|
|
||||||
class ImageUsageRequest(BaseModel):
|
class ImageUsageRequest(BaseModel):
|
||||||
|
|||||||
@ -87,11 +87,12 @@ async def generate_poster(
|
|||||||
- **content_id**: 内容ID(可选)
|
- **content_id**: 内容ID(可选)
|
||||||
- **product_id**: 产品ID(可选)
|
- **product_id**: 产品ID(可选)
|
||||||
- **scenic_spot_id**: 景区ID(可选)
|
- **scenic_spot_id**: 景区ID(可选)
|
||||||
- **image_ids**: 图像ID列表(可选)
|
- **images_base64**: 图像base64编码(可选)
|
||||||
- **template_id**: 模板ID(默认为vibrant)
|
- **template_id**: 模板ID(默认为vibrant)
|
||||||
- **generate_collage**: 是否生成拼图
|
|
||||||
- **poster_content**: 用户提供的海报内容(可选)
|
- **poster_content**: 用户提供的海报内容(可选)
|
||||||
- **force_llm_generation**: 是否强制使用LLM生成内容(可选)
|
- **force_llm_generation**: 是否强制使用LLM生成内容(可选)
|
||||||
|
- **generate_psd**: 是否生成PSD分层文件(可选)
|
||||||
|
- **psd_output_path**: PSD文件输出路径(可选)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
result = await poster_service.generate_poster(
|
result = await poster_service.generate_poster(
|
||||||
@ -102,7 +103,9 @@ async def generate_poster(
|
|||||||
scenic_spot_id=request.scenicSpotId,
|
scenic_spot_id=request.scenicSpotId,
|
||||||
images_base64=request.imagesBase64,
|
images_base64=request.imagesBase64,
|
||||||
num_variations=request.numVariations,
|
num_variations=request.numVariations,
|
||||||
force_llm_generation=request.forceLlmGeneration
|
force_llm_generation=request.forceLlmGeneration,
|
||||||
|
generate_psd=request.generatePsd,
|
||||||
|
psd_output_path=request.psdOutputPath
|
||||||
)
|
)
|
||||||
|
|
||||||
return PosterGenerateResponse(**result)
|
return PosterGenerateResponse(**result)
|
||||||
|
|||||||
@ -161,7 +161,9 @@ class PosterService:
|
|||||||
scenic_spot_id: Optional[int],
|
scenic_spot_id: Optional[int],
|
||||||
images_base64: Optional[List[str]] ,
|
images_base64: Optional[List[str]] ,
|
||||||
num_variations: int = 1,
|
num_variations: int = 1,
|
||||||
force_llm_generation: bool = False) -> Dict[str, Any]:
|
force_llm_generation: bool = False,
|
||||||
|
generate_psd: bool = False,
|
||||||
|
psd_output_path: Optional[str] = None) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
统一的海报生成入口
|
统一的海报生成入口
|
||||||
|
|
||||||
@ -171,12 +173,14 @@ class PosterService:
|
|||||||
content_id: 内容ID,用于从数据库获取内容(可选)
|
content_id: 内容ID,用于从数据库获取内容(可选)
|
||||||
product_id: 产品ID,用于从数据库获取产品信息(可选)
|
product_id: 产品ID,用于从数据库获取产品信息(可选)
|
||||||
scenic_spot_id: 景点ID,用于从数据库获取景点信息(可选)
|
scenic_spot_id: 景点ID,用于从数据库获取景点信息(可选)
|
||||||
image_ids: 图片ID列表,用于从数据库获取图片(可选)
|
images_base64: 图片base64编码,用于生成海报(可选)
|
||||||
num_variations: 需要生成的变体数量
|
num_variations: 需要生成的变体数量
|
||||||
force_llm_generation: 是否强制使用LLM生成内容
|
force_llm_generation: 是否强制使用LLM生成内容
|
||||||
|
generate_psd: 是否生成PSD分层文件
|
||||||
|
psd_output_path: PSD文件输出路径(可选,默认自动生成)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
生成结果字典
|
生成结果字典,包含PNG图像和可选的PSD文件
|
||||||
"""
|
"""
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
|
|
||||||
@ -230,6 +234,7 @@ class PosterService:
|
|||||||
|
|
||||||
# 5. 保存海报并返回结果
|
# 5. 保存海报并返回结果
|
||||||
variations = []
|
variations = []
|
||||||
|
psd_files = []
|
||||||
i=0 ## 用于多个海报时,指定海报的编号,此时只有一个没有用上,但是接口开放着。
|
i=0 ## 用于多个海报时,指定海报的编号,此时只有一个没有用上,但是接口开放着。
|
||||||
output_path = self._save_poster(posters, template_id, i)
|
output_path = self._save_poster(posters, template_id, i)
|
||||||
if output_path:
|
if output_path:
|
||||||
@ -238,6 +243,15 @@ class PosterService:
|
|||||||
"poster_path": str(output_path),
|
"poster_path": str(output_path),
|
||||||
"base64": self._image_to_base64(posters)
|
"base64": self._image_to_base64(posters)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# 6. 如果需要,生成PSD分层文件
|
||||||
|
if generate_psd:
|
||||||
|
psd_result = self._generate_psd_file(
|
||||||
|
template_handler, images, final_content,
|
||||||
|
template_id, i, psd_output_path
|
||||||
|
)
|
||||||
|
if psd_result:
|
||||||
|
psd_files.append(psd_result)
|
||||||
|
|
||||||
# 记录模板使用情况
|
# 记录模板使用情况
|
||||||
self._update_template_stats(template_id, bool(variations), time.time() - start_time)
|
self._update_template_stats(template_id, bool(variations), time.time() - start_time)
|
||||||
@ -246,10 +260,12 @@ class PosterService:
|
|||||||
"requestId": f"poster-{datetime.now().strftime('%Y%m%d-%H%M%S')}-{str(uuid.uuid4())[:8]}",
|
"requestId": f"poster-{datetime.now().strftime('%Y%m%d-%H%M%S')}-{str(uuid.uuid4())[:8]}",
|
||||||
"templateId": template_id,
|
"templateId": template_id,
|
||||||
"resultImagesBase64": variations,
|
"resultImagesBase64": variations,
|
||||||
|
"psdFiles": psd_files if psd_files else None,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"generation_time": f"{time.time() - start_time:.2f}s",
|
"generation_time": f"{time.time() - start_time:.2f}s",
|
||||||
"model_used": self.ai_agent.config.model if force_llm_generation or not poster_content else None,
|
"model_used": self.ai_agent.config.model if force_llm_generation or not poster_content else None,
|
||||||
"num_variations": len(variations)
|
"num_variations": len(variations),
|
||||||
|
"psd_generated": bool(psd_files)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -336,4 +352,104 @@ class PosterService:
|
|||||||
return None
|
return None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"生成内容时发生错误: {e}", exc_info=True)
|
logger.error(f"生成内容时发生错误: {e}", exc_info=True)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _generate_psd_file(self, template_handler: BaseTemplate, images: Image.Image,
|
||||||
|
content: Dict[str, Any], template_id: str,
|
||||||
|
variation_id: int, custom_output_path: Optional[str] = None) -> Optional[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
生成PSD分层文件
|
||||||
|
|
||||||
|
Args:
|
||||||
|
template_handler: 模板处理器实例
|
||||||
|
images: 图像数据
|
||||||
|
content: 海报内容
|
||||||
|
template_id: 模板ID
|
||||||
|
variation_id: 变体ID
|
||||||
|
custom_output_path: 自定义输出路径
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
PSD文件信息字典,包含文件路径、base64编码等
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 检查模板是否支持PSD生成
|
||||||
|
if not hasattr(template_handler, 'generate_layered_psd'):
|
||||||
|
logger.warning(f"模板 {template_id} 不支持PSD分层输出")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 生成PSD文件路径
|
||||||
|
if custom_output_path:
|
||||||
|
psd_filename = custom_output_path
|
||||||
|
if not psd_filename.endswith('.psd'):
|
||||||
|
psd_filename += '.psd'
|
||||||
|
else:
|
||||||
|
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||||
|
psd_filename = f"{template_id}_layered_v{variation_id}_{timestamp}.psd"
|
||||||
|
|
||||||
|
# 获取输出目录
|
||||||
|
topic_id = f"poster_{template_id}_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
||||||
|
output_dir = self.output_manager.get_topic_dir(topic_id)
|
||||||
|
psd_path = output_dir / psd_filename
|
||||||
|
|
||||||
|
# 调用模板的PSD生成方法
|
||||||
|
logger.info(f"开始生成PSD分层文件: {psd_path}")
|
||||||
|
generated_psd_path = template_handler.generate_layered_psd(
|
||||||
|
images=images,
|
||||||
|
content=content,
|
||||||
|
output_path=str(psd_path)
|
||||||
|
)
|
||||||
|
|
||||||
|
if not generated_psd_path or not Path(generated_psd_path).exists():
|
||||||
|
logger.error("PSD文件生成失败或文件不存在")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 获取文件信息
|
||||||
|
file_size = Path(generated_psd_path).stat().st_size
|
||||||
|
|
||||||
|
# 可选:生成PSD的base64编码(注意:PSD文件通常较大)
|
||||||
|
psd_base64 = None
|
||||||
|
if file_size < 10 * 1024 * 1024: # 如果文件小于10MB,才生成base64
|
||||||
|
try:
|
||||||
|
with open(generated_psd_path, 'rb') as f:
|
||||||
|
psd_base64 = base64.b64encode(f.read()).decode('utf-8')
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"生成PSD文件base64编码失败: {e}")
|
||||||
|
|
||||||
|
# 生成预览图(从PSD合成PNG预览)
|
||||||
|
preview_base64 = None
|
||||||
|
try:
|
||||||
|
from psd_tools import PSDImage
|
||||||
|
psd = PSDImage.open(generated_psd_path)
|
||||||
|
preview_image = psd.composite()
|
||||||
|
if preview_image:
|
||||||
|
preview_base64 = self._image_to_base64(preview_image)
|
||||||
|
logger.info("PSD预览图生成成功")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"生成PSD预览图失败: {e}")
|
||||||
|
|
||||||
|
logger.info(f"PSD文件生成成功: {generated_psd_path} ({file_size/1024:.1f}KB)")
|
||||||
|
|
||||||
|
return {
|
||||||
|
"variation_id": variation_id,
|
||||||
|
"psd_path": str(generated_psd_path),
|
||||||
|
"file_name": psd_filename,
|
||||||
|
"file_size": file_size,
|
||||||
|
"base64": psd_base64,
|
||||||
|
"preview_base64": preview_base64,
|
||||||
|
"layer_count": self._get_psd_layer_count(generated_psd_path),
|
||||||
|
"generation_time": datetime.now().isoformat()
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"生成PSD文件时发生错误: {e}", exc_info=True)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _get_psd_layer_count(self, psd_path: str) -> Optional[int]:
|
||||||
|
"""获取PSD文件的图层数量"""
|
||||||
|
try:
|
||||||
|
from psd_tools import PSDImage
|
||||||
|
psd = PSDImage.open(psd_path)
|
||||||
|
return len(list(psd))
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"获取PSD图层数量失败: {e}")
|
||||||
return None
|
return None
|
||||||
Loading…
x
Reference in New Issue
Block a user