Add File
This commit is contained in:
318
src/landppt/services/image/models.py
Normal file
318
src/landppt/services/image/models.py
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
"""
|
||||||
|
图片服务数据模型
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import List, Optional, Dict, Any, Union
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from enum import Enum
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
class ImageSourceType(str, Enum):
|
||||||
|
"""图片来源类型"""
|
||||||
|
AI_GENERATED = "ai_generated"
|
||||||
|
WEB_SEARCH = "web_search"
|
||||||
|
LOCAL_STORAGE = "local_storage"
|
||||||
|
|
||||||
|
|
||||||
|
class ImageProvider(str, Enum):
|
||||||
|
"""图片提供者"""
|
||||||
|
# AI生成
|
||||||
|
DALLE = "dalle"
|
||||||
|
STABLE_DIFFUSION = "stable_diffusion"
|
||||||
|
SILICONFLOW = "siliconflow"
|
||||||
|
POLLINATIONS = "pollinations"
|
||||||
|
|
||||||
|
# 网络搜索
|
||||||
|
UNSPLASH = "unsplash"
|
||||||
|
PIXABAY = "pixabay"
|
||||||
|
SEARXNG = "searxng"
|
||||||
|
GOOGLE_IMAGES = "google_images"
|
||||||
|
|
||||||
|
# 本地存储
|
||||||
|
LOCAL_STORAGE = "local_storage"
|
||||||
|
USER_UPLOAD = "user_upload"
|
||||||
|
SYSTEM_DEFAULT = "system_default"
|
||||||
|
|
||||||
|
|
||||||
|
class ImageFormat(str, Enum):
|
||||||
|
"""支持的图片格式"""
|
||||||
|
JPEG = "jpeg"
|
||||||
|
JPG = "jpg"
|
||||||
|
PNG = "png"
|
||||||
|
GIF = "gif"
|
||||||
|
WEBP = "webp"
|
||||||
|
BMP = "bmp"
|
||||||
|
TIFF = "tiff"
|
||||||
|
|
||||||
|
|
||||||
|
class ImagePosition(str, Enum):
|
||||||
|
"""图片在幻灯片中的位置"""
|
||||||
|
BACKGROUND = "background"
|
||||||
|
CONTENT = "content"
|
||||||
|
ICON = "icon"
|
||||||
|
DECORATION = "decoration"
|
||||||
|
HEADER = "header"
|
||||||
|
FOOTER = "footer"
|
||||||
|
|
||||||
|
|
||||||
|
class ImageLicense(str, Enum):
|
||||||
|
"""图片许可证类型"""
|
||||||
|
PUBLIC_DOMAIN = "public_domain"
|
||||||
|
CC0 = "cc0"
|
||||||
|
CC_BY = "cc_by"
|
||||||
|
CC_BY_SA = "cc_by_sa"
|
||||||
|
UNSPLASH_LICENSE = "unsplash_license"
|
||||||
|
PIXABAY_LICENSE = "pixabay_license"
|
||||||
|
CUSTOM = "custom"
|
||||||
|
UNKNOWN = "unknown"
|
||||||
|
|
||||||
|
|
||||||
|
class ImageMetadata(BaseModel):
|
||||||
|
"""图片元数据"""
|
||||||
|
width: int
|
||||||
|
height: int
|
||||||
|
format: ImageFormat
|
||||||
|
file_size: int
|
||||||
|
color_mode: Optional[str] = None
|
||||||
|
has_transparency: bool = False
|
||||||
|
dominant_colors: List[str] = Field(default_factory=list)
|
||||||
|
average_color: Optional[str] = None
|
||||||
|
brightness: Optional[float] = None
|
||||||
|
contrast: Optional[float] = None
|
||||||
|
|
||||||
|
|
||||||
|
class ImageTag(BaseModel):
|
||||||
|
"""图片标签"""
|
||||||
|
name: str
|
||||||
|
category: Optional[str] = None
|
||||||
|
confidence: float = 1.0
|
||||||
|
source: Optional[str] = None # 标签来源:user, ai, api等
|
||||||
|
|
||||||
|
|
||||||
|
class ImageInfo(BaseModel):
|
||||||
|
"""图片信息"""
|
||||||
|
image_id: str
|
||||||
|
source_type: ImageSourceType
|
||||||
|
provider: ImageProvider
|
||||||
|
original_url: Optional[str] = None
|
||||||
|
local_path: str
|
||||||
|
filename: str
|
||||||
|
title: Optional[str] = None
|
||||||
|
description: Optional[str] = None
|
||||||
|
alt_text: Optional[str] = None
|
||||||
|
|
||||||
|
# 元数据
|
||||||
|
metadata: ImageMetadata
|
||||||
|
|
||||||
|
# 标签和关键词
|
||||||
|
tags: List[ImageTag] = Field(default_factory=list)
|
||||||
|
keywords: List[str] = Field(default_factory=list)
|
||||||
|
|
||||||
|
# 版权信息
|
||||||
|
license: ImageLicense = ImageLicense.UNKNOWN
|
||||||
|
license_info: Optional[str] = None
|
||||||
|
author: Optional[str] = None
|
||||||
|
author_url: Optional[str] = None
|
||||||
|
source_url: Optional[str] = None
|
||||||
|
|
||||||
|
# 使用统计
|
||||||
|
usage_count: int = 0
|
||||||
|
last_used_at: Optional[float] = None
|
||||||
|
|
||||||
|
# 时间戳
|
||||||
|
created_at: float = Field(default_factory=time.time)
|
||||||
|
updated_at: float = Field(default_factory=time.time)
|
||||||
|
|
||||||
|
def add_tag(self, name: str, category: Optional[str] = None, confidence: float = 1.0):
|
||||||
|
"""添加标签"""
|
||||||
|
tag = ImageTag(name=name, category=category, confidence=confidence)
|
||||||
|
if tag not in self.tags:
|
||||||
|
self.tags.append(tag)
|
||||||
|
|
||||||
|
def remove_tag(self, name: str):
|
||||||
|
"""移除标签"""
|
||||||
|
self.tags = [tag for tag in self.tags if tag.name != name]
|
||||||
|
|
||||||
|
def get_tags_by_category(self, category: str) -> List[ImageTag]:
|
||||||
|
"""按分类获取标签"""
|
||||||
|
return [tag for tag in self.tags if tag.category == category]
|
||||||
|
|
||||||
|
def update_usage(self):
|
||||||
|
"""更新使用统计"""
|
||||||
|
self.usage_count += 1
|
||||||
|
self.last_used_at = time.time()
|
||||||
|
self.updated_at = time.time()
|
||||||
|
|
||||||
|
|
||||||
|
class ImageSearchRequest(BaseModel):
|
||||||
|
"""图片搜索请求"""
|
||||||
|
query: str
|
||||||
|
keywords: List[str] = Field(default_factory=list)
|
||||||
|
tags: List[str] = Field(default_factory=list)
|
||||||
|
|
||||||
|
# 搜索参数
|
||||||
|
page: int = 1
|
||||||
|
per_page: int = 20
|
||||||
|
orientation: Optional[str] = None # landscape, portrait, squarish
|
||||||
|
category: Optional[str] = None
|
||||||
|
color: Optional[str] = None
|
||||||
|
|
||||||
|
# 尺寸要求
|
||||||
|
min_width: Optional[int] = None
|
||||||
|
min_height: Optional[int] = None
|
||||||
|
max_width: Optional[int] = None
|
||||||
|
max_height: Optional[int] = None
|
||||||
|
|
||||||
|
# 许可证要求
|
||||||
|
license_types: List[ImageLicense] = Field(default_factory=list)
|
||||||
|
|
||||||
|
# 提供者偏好
|
||||||
|
preferred_providers: List[ImageProvider] = Field(default_factory=list)
|
||||||
|
excluded_providers: List[ImageProvider] = Field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
|
class ImageGenerationRequest(BaseModel):
|
||||||
|
"""AI图片生成请求"""
|
||||||
|
prompt: str
|
||||||
|
negative_prompt: Optional[str] = None
|
||||||
|
|
||||||
|
# 生成参数
|
||||||
|
width: int = 1024
|
||||||
|
height: int = 1024
|
||||||
|
quality: str = "standard" # standard, hd
|
||||||
|
style: Optional[str] = None # vivid, natural
|
||||||
|
|
||||||
|
# 提供者特定参数
|
||||||
|
provider: ImageProvider = ImageProvider.DALLE
|
||||||
|
model: Optional[str] = None
|
||||||
|
steps: Optional[int] = None
|
||||||
|
guidance_scale: Optional[float] = None
|
||||||
|
seed: Optional[int] = None
|
||||||
|
|
||||||
|
# 批量生成
|
||||||
|
num_images: int = 1
|
||||||
|
|
||||||
|
|
||||||
|
class ImageUploadRequest(BaseModel):
|
||||||
|
"""图片上传请求"""
|
||||||
|
filename: str
|
||||||
|
content_type: str
|
||||||
|
file_size: int
|
||||||
|
|
||||||
|
# 可选信息
|
||||||
|
title: Optional[str] = None
|
||||||
|
description: Optional[str] = None
|
||||||
|
tags: List[str] = Field(default_factory=list)
|
||||||
|
category: Optional[str] = None
|
||||||
|
|
||||||
|
# 来源信息
|
||||||
|
source_type: Optional[ImageSourceType] = None # 实际来源类型,如果为None则默认为LOCAL_STORAGE
|
||||||
|
original_url: Optional[str] = None # 原始URL(用于网络图片)
|
||||||
|
|
||||||
|
# 处理选项
|
||||||
|
auto_resize: bool = True
|
||||||
|
auto_optimize: bool = True
|
||||||
|
generate_thumbnails: bool = True
|
||||||
|
|
||||||
|
|
||||||
|
class ImageProcessingOptions(BaseModel):
|
||||||
|
"""图片处理选项"""
|
||||||
|
# 尺寸调整
|
||||||
|
resize_width: Optional[int] = None
|
||||||
|
resize_height: Optional[int] = None
|
||||||
|
maintain_aspect_ratio: bool = True
|
||||||
|
resize_method: str = "lanczos" # lanczos, bicubic, bilinear
|
||||||
|
|
||||||
|
# 格式转换
|
||||||
|
output_format: Optional[ImageFormat] = None
|
||||||
|
|
||||||
|
# 质量优化
|
||||||
|
quality: Optional[int] = None # 1-100
|
||||||
|
optimize: bool = True
|
||||||
|
progressive: bool = False
|
||||||
|
|
||||||
|
# 图片增强
|
||||||
|
auto_enhance: bool = False
|
||||||
|
brightness: Optional[float] = None
|
||||||
|
contrast: Optional[float] = None
|
||||||
|
saturation: Optional[float] = None
|
||||||
|
sharpness: Optional[float] = None
|
||||||
|
|
||||||
|
# 水印
|
||||||
|
add_watermark: bool = False
|
||||||
|
watermark_text: Optional[str] = None
|
||||||
|
watermark_position: str = "bottom_right"
|
||||||
|
watermark_opacity: float = 0.5
|
||||||
|
|
||||||
|
|
||||||
|
class ImageSearchResult(BaseModel):
|
||||||
|
"""图片搜索结果"""
|
||||||
|
images: List[ImageInfo]
|
||||||
|
total_count: int
|
||||||
|
page: int
|
||||||
|
per_page: int
|
||||||
|
has_next: bool
|
||||||
|
has_prev: bool
|
||||||
|
|
||||||
|
# 搜索统计
|
||||||
|
search_time: float
|
||||||
|
provider_results: Dict[str, int] = Field(default_factory=dict)
|
||||||
|
provider: ImageProvider
|
||||||
|
error: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class ImageOperationResult(BaseModel):
|
||||||
|
"""图片操作结果"""
|
||||||
|
success: bool
|
||||||
|
message: str
|
||||||
|
image_info: Optional[ImageInfo] = None
|
||||||
|
error_code: Optional[str] = None
|
||||||
|
processing_time: Optional[float] = None
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectImageAssociation(BaseModel):
|
||||||
|
"""项目图片关联"""
|
||||||
|
project_id: str
|
||||||
|
image_id: str
|
||||||
|
slide_index: int
|
||||||
|
position: ImagePosition
|
||||||
|
|
||||||
|
# 位置和样式信息
|
||||||
|
x: Optional[float] = None
|
||||||
|
y: Optional[float] = None
|
||||||
|
width: Optional[float] = None
|
||||||
|
height: Optional[float] = None
|
||||||
|
z_index: Optional[int] = None
|
||||||
|
|
||||||
|
# 样式属性
|
||||||
|
opacity: float = 1.0
|
||||||
|
rotation: float = 0.0
|
||||||
|
border_radius: Optional[float] = None
|
||||||
|
shadow: Optional[Dict[str, Any]] = None
|
||||||
|
|
||||||
|
# 动画效果
|
||||||
|
animation: Optional[Dict[str, Any]] = None
|
||||||
|
|
||||||
|
created_at: float = Field(default_factory=time.time)
|
||||||
|
|
||||||
|
|
||||||
|
class ImageCacheInfo(BaseModel):
|
||||||
|
"""图片缓存信息"""
|
||||||
|
cache_key: str
|
||||||
|
file_path: str
|
||||||
|
file_size: int
|
||||||
|
created_at: float
|
||||||
|
last_accessed: float
|
||||||
|
access_count: int
|
||||||
|
expires_at: Optional[float] = None
|
||||||
|
|
||||||
|
def is_expired(self) -> bool:
|
||||||
|
"""检查是否过期 - 图片永不过期"""
|
||||||
|
return False
|
||||||
|
|
||||||
|
def update_access(self):
|
||||||
|
"""更新访问信息"""
|
||||||
|
self.last_accessed = time.time()
|
||||||
|
self.access_count += 1
|
||||||
Reference in New Issue
Block a user