From 0eb7d7032bd3b6da048a2837ddba14d90b38a44d Mon Sep 17 00:00:00 2001 From: CalamitousFelicitousness Date: Sun, 28 Jun 2026 02:43:03 +0100 Subject: [PATCH] feat(civitai): filter search results by nsfw level The nsfw boolean leaks Mature+ content (nsfw=false returned levels up to XXX) and /models rejects server-side level filtering, so filter search results on each model's aggregate nsfwLevel: nsfw off keeps SFW (None + Soft), nsfw on keeps everything. --- modules/civitai/client_civitai.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/modules/civitai/client_civitai.py b/modules/civitai/client_civitai.py index 0368705ab..3593136e8 100644 --- a/modules/civitai/client_civitai.py +++ b/modules/civitai/client_civitai.py @@ -8,6 +8,9 @@ from modules.civitai.models_civitai import CivitModel, CivitVersion, CivitImage, options_cache: dict = {} options_cache_time: float = 0 OPTIONS_TTL = 3600 # 1 hour +# Civitai nsfwLevel bitmask: 1=PG/None 2=PG-13/Soft 4=R/Mature 8=X 16=XXX 32=Blocked +NSFW_LEVEL_SFW = 3 # None + Soft: Civitai's SFW browsing boundary +NSFW_LEVEL_ALL = 63 # every level set: disables filtering class CivitaiClient: @@ -76,10 +79,17 @@ class CivitaiClient: except Exception: return CivitSearchResponse() try: - return CivitSearchResponse.parse_obj(data) + response = CivitSearchResponse.parse_obj(data) except Exception as e: log.error(f'CivitAI search parse error: {e}') return CivitSearchResponse() + # /models rejects server-side level filtering and its nsfw boolean leaks + # Mature+ content, so filter on each model's aggregate nsfwLevel here: + # nsfw on keeps every level, nsfw off/unset keeps SFW (None + Soft). + level = NSFW_LEVEL_ALL if nsfw else NSFW_LEVEL_SFW + if level < NSFW_LEVEL_ALL: + response.items = [m for m in response.items if (m.nsfw_level & ~level) == 0] + return response def get_model(self, model_id: int, *, token: str | None = None) -> CivitModel | None: r = self._get(f'/models/{model_id}', token=token)