mirror of
https://github.com/vladmandic/automatic
synced 2026-09-19 17:24:32 +02:00
236d96ef06
Civitai returns a null username for deleted or anonymous creators. The field default only applies when the key is absent, so an explicit null failed str validation and rejected the entire CivitSearchResponse, dropping every model on that page. Coerce null or empty usernames to the default in a pre-validator so one bad creator no longer empties the whole search result.
179 lines
6.3 KiB
Python
179 lines
6.3 KiB
Python
from pydantic import BaseModel, Field, validator # pylint: disable=no-name-in-module
|
|
|
|
|
|
class CivitImage(BaseModel):
|
|
class Config:
|
|
allow_population_by_field_name = True
|
|
id: int = 0
|
|
url: str = ""
|
|
width: int = 0
|
|
height: int = 0
|
|
type: str = "Unknown"
|
|
nsfw_level: int = Field(0, alias="nsfwLevel")
|
|
hash: str | None = None
|
|
meta: dict | None = None
|
|
|
|
|
|
class CivitFileHashes(BaseModel):
|
|
class Config:
|
|
allow_population_by_field_name = True
|
|
sha256: str | None = Field(None, alias="SHA256")
|
|
autov1: str | None = Field(None, alias="AutoV1")
|
|
autov2: str | None = Field(None, alias="AutoV2")
|
|
autov3: str | None = Field(None, alias="AutoV3")
|
|
crc32: str | None = Field(None, alias="CRC32")
|
|
blake3: str | None = Field(None, alias="BLAKE3")
|
|
|
|
|
|
class CivitFile(BaseModel):
|
|
class Config:
|
|
allow_population_by_field_name = True
|
|
id: int = 0
|
|
name: str = "Unknown"
|
|
type: str = "Unknown"
|
|
size_kb: float = Field(0, alias="sizeKB")
|
|
hashes: CivitFileHashes = Field(default_factory=CivitFileHashes)
|
|
download_url: str = Field("", alias="downloadUrl")
|
|
primary: bool | None = None
|
|
|
|
|
|
class CivitStats(BaseModel):
|
|
class Config:
|
|
allow_population_by_field_name = True
|
|
download_count: int = Field(0, alias="downloadCount")
|
|
favorite_count: int = Field(0, alias="favoriteCount")
|
|
thumb_up_count: int = Field(0, alias="thumbsUpCount")
|
|
thumb_down_count: int = Field(0, alias="thumbsDownCount")
|
|
comment_count: int = Field(0, alias="commentCount")
|
|
rating_count: int = Field(0, alias="ratingCount")
|
|
rating: float = 0
|
|
|
|
|
|
class CivitVersion(BaseModel):
|
|
class Config:
|
|
allow_population_by_field_name = True
|
|
id: int = 0
|
|
model_id: int = Field(0, alias="modelId")
|
|
name: str = "Unknown"
|
|
base_model: str = Field("Unknown", alias="baseModel")
|
|
published_at: str | None = Field(None, alias="publishedAt")
|
|
availability: str = "Unknown"
|
|
description: str | None = None
|
|
trained_words: list[str] = Field(default_factory=list, alias="trainedWords")
|
|
stats: CivitStats = Field(default_factory=CivitStats)
|
|
files: list[CivitFile] = Field(default_factory=list)
|
|
images: list[CivitImage] = Field(default_factory=list)
|
|
nsfw_level: int = Field(0, alias="nsfwLevel")
|
|
download_url: str = Field("", alias="downloadUrl")
|
|
|
|
|
|
class CivitCreator(BaseModel):
|
|
class Config:
|
|
allow_population_by_field_name = True
|
|
username: str = "Unknown"
|
|
image: str | None = None
|
|
|
|
@validator('username', pre=True)
|
|
def coerce_null_username(cls, v): # pylint: disable=no-self-argument
|
|
# Deleted/anonymous creators serialize username as null, which failed
|
|
# str validation and rejected the entire search response. Fall back to
|
|
# the default name instead of dropping the whole page.
|
|
return "Unknown" if v in (None, "") else v
|
|
|
|
|
|
class CivitModel(BaseModel):
|
|
class Config:
|
|
allow_population_by_field_name = True
|
|
id: int = 0
|
|
type: str = "Unknown"
|
|
name: str = "Unknown"
|
|
description: str | None = None
|
|
tags: list[str] = Field(default_factory=list)
|
|
nsfw: bool = False
|
|
nsfw_level: int = Field(0, alias="nsfwLevel")
|
|
availability: str = "Unknown"
|
|
stats: CivitStats = Field(default_factory=CivitStats)
|
|
creator: CivitCreator = Field(default_factory=CivitCreator)
|
|
versions: list[CivitVersion] = Field(default_factory=list, alias="modelVersions")
|
|
allow_no_credit: bool = Field(True, alias="allowNoCredit")
|
|
allow_commercial_use: list[str] = Field(default_factory=list, alias="allowCommercialUse")
|
|
allow_derivatives: bool = Field(True, alias="allowDerivatives")
|
|
allow_different_license: bool = Field(True, alias="allowDifferentLicense")
|
|
supports_generation: bool = Field(False, alias="supportsGeneration")
|
|
mode: str | None = None
|
|
poi: bool = False
|
|
minor: bool = False
|
|
sfw_only: bool = Field(False, alias="sfwOnly")
|
|
user_id: int = Field(0, alias="userId")
|
|
cosmetic: dict | None = None
|
|
|
|
@validator('allow_commercial_use', pre=True)
|
|
def coerce_commercial_use(cls, v): # pylint: disable=no-self-argument
|
|
if isinstance(v, str):
|
|
return [v] if v else []
|
|
return v
|
|
|
|
@validator('poi', 'minor', 'sfw_only', pre=True)
|
|
def coerce_null_flags(cls, v): # pylint: disable=no-self-argument
|
|
# These flags serialize as null on some endpoints; treat null as not-set.
|
|
return False if v is None else v
|
|
|
|
|
|
class CivitSearchMetadata(BaseModel):
|
|
class Config:
|
|
allow_population_by_field_name = True
|
|
next_page: str | None = Field(None, alias="nextPage")
|
|
current_page: int | None = Field(None, alias="currentPage")
|
|
page_size: int | None = Field(None, alias="pageSize")
|
|
total_pages: int | None = Field(None, alias="totalPages")
|
|
total_items: int | None = Field(None, alias="totalItems")
|
|
next_cursor: str | None = Field(None, alias="nextCursor")
|
|
|
|
|
|
class CivitSearchResponse(BaseModel):
|
|
class Config:
|
|
allow_population_by_field_name = True
|
|
items: list[CivitModel] = Field(default_factory=list)
|
|
metadata: CivitSearchMetadata = Field(default_factory=CivitSearchMetadata)
|
|
request_url: str | None = Field(None, alias="requestUrl")
|
|
|
|
|
|
class CivitTag(BaseModel):
|
|
class Config:
|
|
allow_population_by_field_name = True
|
|
name: str = ""
|
|
model_count: int = Field(0, alias="modelCount")
|
|
link: str = ""
|
|
|
|
|
|
class CivitTagResponse(BaseModel):
|
|
class Config:
|
|
allow_population_by_field_name = True
|
|
items: list[CivitTag] = Field(default_factory=list)
|
|
metadata: CivitSearchMetadata = Field(default_factory=CivitSearchMetadata)
|
|
|
|
|
|
class CivitCreatorItem(BaseModel):
|
|
class Config:
|
|
allow_population_by_field_name = True
|
|
username: str = ""
|
|
model_count: int = Field(0, alias="modelCount")
|
|
link: str = ""
|
|
image: str | None = None
|
|
|
|
|
|
class CivitCreatorResponse(BaseModel):
|
|
class Config:
|
|
allow_population_by_field_name = True
|
|
items: list[CivitCreatorItem] = Field(default_factory=list)
|
|
metadata: CivitSearchMetadata = Field(default_factory=CivitSearchMetadata)
|
|
|
|
|
|
class CivitUserProfile(BaseModel):
|
|
class Config:
|
|
allow_population_by_field_name = True
|
|
id: int = 0
|
|
username: str = ""
|
|
image: str | None = None
|
|
profile_picture: str | None = Field(None, alias="profilePicture")
|