feat(civitai): add the mini version endpoint

GET /sdapi/v2/civitai/version/mini/{id} exposes /model-versions/mini:
the primary file flattened onto the version, every hash, and the
permission flags a download needs. CivitVersionMini is its own model
because its field set is per-file, not per-version.
This commit is contained in:
CalamitousFelicitousness
2026-09-11 23:47:46 +01:00
parent ca6ad175d6
commit 4cf734dbd6
3 changed files with 53 additions and 1 deletions
+10
View File
@@ -158,6 +158,15 @@ def get_version_by_hash(hash_str: str, token: str | None = None):
return version_to_dict(version)
def get_version_mini(version_id: int, token: str | None = None):
"""Download-shaped version view carrying permission and early-access flags."""
from modules.civitai.client_civitai import client
version = client.get_version_mini(version_id, token=token)
if version is None:
return JSONResponse(content={"error": "version not found"}, status_code=404)
return version_to_dict(version)
def get_options():
"""Get valid types, sort, period, base_models from CivitAI API discovery."""
from modules.civitai.client_civitai import client
@@ -687,6 +696,7 @@ def register_api(api):
api.add_api_route("/sdapi/v2/civitai/model/{model_id}", get_model, methods=["GET"], tags=["CivitAI"])
api.add_api_route("/sdapi/v2/civitai/version/{version_id}", get_version, methods=["GET"], tags=["CivitAI"])
api.add_api_route("/sdapi/v2/civitai/version/by-hash/{hash_str}", get_version_by_hash, methods=["GET"], tags=["CivitAI"])
api.add_api_route("/sdapi/v2/civitai/version/mini/{version_id}", get_version_mini, methods=["GET"], tags=["CivitAI"])
api.add_api_route("/sdapi/v2/civitai/options", get_options, methods=["GET"], tags=["CivitAI"])
api.add_api_route("/sdapi/v2/civitai/tags", get_tags, methods=["GET"], tags=["CivitAI"])
api.add_api_route("/sdapi/v2/civitai/creators", get_creators, methods=["GET"], tags=["CivitAI"])
+12 -1
View File
@@ -1,7 +1,7 @@
import os
import time
from modules.logger import log
from modules.civitai.models_civitai import CivitModel, CivitVersion, CivitImage, CivitSearchResponse, CivitTagResponse, CivitCreatorResponse, CivitUserProfile
from modules.civitai.models_civitai import CivitModel, CivitVersion, CivitVersionMini, CivitImage, CivitSearchResponse, CivitTagResponse, CivitCreatorResponse, CivitUserProfile
options_cache: dict = {}
@@ -122,6 +122,17 @@ class CivitaiClient:
log.error(f'CivitAI get version by hash parse error: hash={hash_str} {e}')
return None
def get_version_mini(self, version_id: int, *, token: str | None = None) -> CivitVersionMini | None:
r = self._get(f'/model-versions/mini/{version_id}', token=token)
if r.status_code != 200:
log.error(f'CivitAI get version mini: id={version_id} code={r.status_code}')
return None
try:
return CivitVersionMini.parse_obj(r.json())
except Exception as e:
log.error(f'CivitAI get version mini parse error: id={version_id} {e}')
return None
def get_images(self, *, model_version_id: int | None = None, limit: int | None = None, token: str | None = None) -> list[CivitImage]:
params: dict = {}
if model_version_id is not None:
+31
View File
@@ -23,6 +23,7 @@ class CivitFileHashes(BaseModel):
autov3: str | None = Field(None, alias="AutoV3")
crc32: str | None = Field(None, alias="CRC32")
blake3: str | None = Field(None, alias="BLAKE3")
sha256_12: str | None = Field(None, alias="SHA256_12")
class CivitFileMetadata(BaseModel):
@@ -88,6 +89,36 @@ class CivitVersion(BaseModel):
return "Unknown" if v in (None, "") else v
class CivitVersionMini(BaseModel):
# primary file flattened onto the version plus permission flags; earlyAccessEndsAt and freeTrialLimit exist only during early access
class Config:
allow_population_by_field_name = True
air: str = ""
version_name: str = Field("", alias="versionName")
model_name: str = Field("", alias="modelName")
user_id: int = Field(0, alias="userId")
base_model: str = Field("Unknown", alias="baseModel")
availability: str = "Unknown"
published_at: str | None = Field(None, alias="publishedAt")
size: float = 0
file_type: str = Field("", alias="fileType")
file_name: str = Field("", alias="fileName")
format: str = ""
hashes: CivitFileHashes = Field(default_factory=CivitFileHashes)
download_urls: list[str] = Field(default_factory=list, alias="downloadUrls")
can_generate: bool = Field(False, alias="canGenerate")
is_featured: bool = Field(False, alias="isFeatured")
require_auth: bool = Field(False, alias="requireAuth")
check_permission: bool = Field(False, alias="checkPermission")
additional_resource_charge: bool = Field(False, alias="additionalResourceCharge")
payout_enabled: bool = Field(False, alias="payoutEnabled")
minor: bool = False
sfw_only: bool = Field(False, alias="sfwOnly")
fees: list = Field(default_factory=list)
early_access_ends_at: str | None = Field(None, alias="earlyAccessEndsAt")
free_trial_limit: int | None = Field(None, alias="freeTrialLimit")
class CivitCreator(BaseModel):
class Config:
allow_population_by_field_name = True