Add ethnicity list node and regional filters
This commit is contained in:
@@ -18,6 +18,7 @@ The node is registered as:
|
|||||||
- `prompt_builder / SxCP Category Preset`
|
- `prompt_builder / SxCP Category Preset`
|
||||||
- `prompt_builder / SxCP Cast Control`
|
- `prompt_builder / SxCP Cast Control`
|
||||||
- `prompt_builder / SxCP Generation Profile`
|
- `prompt_builder / SxCP Generation Profile`
|
||||||
|
- `prompt_builder / SxCP Ethnicity List`
|
||||||
- `prompt_builder / SxCP Advanced Filters`
|
- `prompt_builder / SxCP Advanced Filters`
|
||||||
- `prompt_builder / SxCP Prompt Builder From Configs`
|
- `prompt_builder / SxCP Prompt Builder From Configs`
|
||||||
- `prompt_builder / SxCP Woman Slot`
|
- `prompt_builder / SxCP Woman Slot`
|
||||||
@@ -51,6 +52,12 @@ node. For cleaner workflows, use the split nodes:
|
|||||||
- `SxCP Generation Profile` outputs `generation_profile` for common behavior
|
- `SxCP Generation Profile` outputs `generation_profile` for common behavior
|
||||||
presets such as casual-clean, evocative-softcore, hardcore-intense,
|
presets such as casual-clean, evocative-softcore, hardcore-intense,
|
||||||
Krea2-friendly, or Flux-original.
|
Krea2-friendly, or Flux-original.
|
||||||
|
- `SxCP Ethnicity List` outputs a reusable ethnicity filter from checkboxes.
|
||||||
|
It includes broad groups and narrower European/Mediterranean groups such as
|
||||||
|
`french_european`, `germanic_european`, `nordic_european`,
|
||||||
|
`slavic_european`, `italian_mediterranean`, and `iberian_mediterranean`.
|
||||||
|
Connect `ethnicity` to a prompt or slot `ethnicity_list` input, or connect
|
||||||
|
`filter_config` to generator-level `filter_config`.
|
||||||
- `SxCP Advanced Filters` outputs `filter_config` for appearance include
|
- `SxCP Advanced Filters` outputs `filter_config` for appearance include
|
||||||
checkboxes, figure, and plus-size inclusion.
|
checkboxes, figure, and plus-size inclusion.
|
||||||
- `SxCP Prompt Builder From Configs` consumes those config outputs and produces
|
- `SxCP Prompt Builder From Configs` consumes those config outputs and produces
|
||||||
@@ -125,6 +132,11 @@ as one stable character across scene, pose, outfit, or row rerolls. This seed is
|
|||||||
shared by that slot's random age/body/appearance choices, so you can keep the
|
shared by that slot's random age/body/appearance choices, so you can keep the
|
||||||
same participant while changing other generation axes.
|
same participant while changing other generation axes.
|
||||||
|
|
||||||
|
Connect `SxCP Ethnicity List.ethnicity` to a slot's `ethnicity_list` input when
|
||||||
|
that character should randomize inside a selected heritage list. This is useful
|
||||||
|
for narrowing broad groups, for example choosing French/Germanic/Nordic/Slavic
|
||||||
|
European entries instead of the entire `european` pool.
|
||||||
|
|
||||||
Use `Woman Slot` for women because it exposes woman-focused body choices and a
|
Use `Woman Slot` for women because it exposes woman-focused body choices and a
|
||||||
`figure_bias` selector. Use `Man Slot` for men because it exposes man-focused
|
`figure_bias` selector. Use `Man Slot` for men because it exposes man-focused
|
||||||
body choices and omits figure bias. The older generic `SxCP Character Slot`
|
body choices and omits figure bias. The older generic `SxCP Character Slot`
|
||||||
|
|||||||
+115
-6
@@ -20,6 +20,7 @@ try:
|
|||||||
build_category_config_json,
|
build_category_config_json,
|
||||||
build_character_slot_json,
|
build_character_slot_json,
|
||||||
build_character_profile_json,
|
build_character_profile_json,
|
||||||
|
build_ethnicity_list_json,
|
||||||
build_filter_config_json,
|
build_filter_config_json,
|
||||||
build_generation_profile_json,
|
build_generation_profile_json,
|
||||||
build_insta_of_options_json,
|
build_insta_of_options_json,
|
||||||
@@ -72,6 +73,7 @@ except ImportError:
|
|||||||
build_category_config_json,
|
build_category_config_json,
|
||||||
build_character_slot_json,
|
build_character_slot_json,
|
||||||
build_character_profile_json,
|
build_character_profile_json,
|
||||||
|
build_ethnicity_list_json,
|
||||||
build_filter_config_json,
|
build_filter_config_json,
|
||||||
build_generation_profile_json,
|
build_generation_profile_json,
|
||||||
build_insta_of_options_json,
|
build_insta_of_options_json,
|
||||||
@@ -155,6 +157,7 @@ class SxCPPromptBuilder:
|
|||||||
"prepend_trigger_to_prompt": ("BOOLEAN", {"default": True}),
|
"prepend_trigger_to_prompt": ("BOOLEAN", {"default": True}),
|
||||||
},
|
},
|
||||||
"optional": {
|
"optional": {
|
||||||
|
"ethnicity_list": ("STRING", {"default": "", "multiline": True}),
|
||||||
"seed_config": ("STRING", {"default": "", "multiline": True}),
|
"seed_config": ("STRING", {"default": "", "multiline": True}),
|
||||||
"camera_config": ("STRING", {"default": "", "multiline": True}),
|
"camera_config": ("STRING", {"default": "", "multiline": True}),
|
||||||
"character_profile": ("STRING", {"default": "", "multiline": True}),
|
"character_profile": ("STRING", {"default": "", "multiline": True}),
|
||||||
@@ -197,6 +200,7 @@ class SxCPPromptBuilder:
|
|||||||
extra_negative="",
|
extra_negative="",
|
||||||
no_plus_women=False,
|
no_plus_women=False,
|
||||||
no_black=False,
|
no_black=False,
|
||||||
|
ethnicity_list="",
|
||||||
):
|
):
|
||||||
row = build_prompt(
|
row = build_prompt(
|
||||||
category=category,
|
category=category,
|
||||||
@@ -205,7 +209,7 @@ class SxCPPromptBuilder:
|
|||||||
start_index=start_index,
|
start_index=start_index,
|
||||||
seed=seed,
|
seed=seed,
|
||||||
clothing=clothing,
|
clothing=clothing,
|
||||||
ethnicity=ethnicity,
|
ethnicity=ethnicity_list or ethnicity,
|
||||||
poses=poses,
|
poses=poses,
|
||||||
expression_enabled=expression_enabled,
|
expression_enabled=expression_enabled,
|
||||||
expression_intensity=expression_intensity,
|
expression_intensity=expression_intensity,
|
||||||
@@ -700,6 +704,99 @@ class SxCPAdvancedFilters:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SxCPEthnicityList:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"include_european": ("BOOLEAN", {"default": False}),
|
||||||
|
"include_mediterranean_mena": ("BOOLEAN", {"default": False}),
|
||||||
|
"include_latina": ("BOOLEAN", {"default": False}),
|
||||||
|
"include_east_asian": ("BOOLEAN", {"default": False}),
|
||||||
|
"include_southeast_asian": ("BOOLEAN", {"default": False}),
|
||||||
|
"include_south_asian": ("BOOLEAN", {"default": False}),
|
||||||
|
"include_black_african": ("BOOLEAN", {"default": False}),
|
||||||
|
"include_indigenous": ("BOOLEAN", {"default": False}),
|
||||||
|
"include_mixed": ("BOOLEAN", {"default": False}),
|
||||||
|
"include_asian": ("BOOLEAN", {"default": False}),
|
||||||
|
"include_white_asian": ("BOOLEAN", {"default": False}),
|
||||||
|
"include_western_european": ("BOOLEAN", {"default": False}),
|
||||||
|
"include_french_european": ("BOOLEAN", {"default": False}),
|
||||||
|
"include_germanic_european": ("BOOLEAN", {"default": False}),
|
||||||
|
"include_nordic_european": ("BOOLEAN", {"default": False}),
|
||||||
|
"include_celtic_european": ("BOOLEAN", {"default": False}),
|
||||||
|
"include_slavic_european": ("BOOLEAN", {"default": False}),
|
||||||
|
"include_baltic_european": ("BOOLEAN", {"default": False}),
|
||||||
|
"include_alpine_european": ("BOOLEAN", {"default": False}),
|
||||||
|
"include_balkan_european": ("BOOLEAN", {"default": False}),
|
||||||
|
"include_greek_mediterranean": ("BOOLEAN", {"default": False}),
|
||||||
|
"include_italian_mediterranean": ("BOOLEAN", {"default": False}),
|
||||||
|
"include_iberian_mediterranean": ("BOOLEAN", {"default": False}),
|
||||||
|
"strict_excludes": ("BOOLEAN", {"default": True}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("STRING", "STRING", "STRING")
|
||||||
|
RETURN_NAMES = ("ethnicity", "filter_config", "summary")
|
||||||
|
FUNCTION = "build"
|
||||||
|
CATEGORY = "prompt_builder"
|
||||||
|
|
||||||
|
def build(
|
||||||
|
self,
|
||||||
|
include_european,
|
||||||
|
include_mediterranean_mena,
|
||||||
|
include_latina,
|
||||||
|
include_east_asian,
|
||||||
|
include_southeast_asian,
|
||||||
|
include_south_asian,
|
||||||
|
include_black_african,
|
||||||
|
include_indigenous,
|
||||||
|
include_mixed,
|
||||||
|
include_asian,
|
||||||
|
include_white_asian,
|
||||||
|
include_western_european,
|
||||||
|
include_french_european,
|
||||||
|
include_germanic_european,
|
||||||
|
include_nordic_european,
|
||||||
|
include_celtic_european,
|
||||||
|
include_slavic_european,
|
||||||
|
include_baltic_european,
|
||||||
|
include_alpine_european,
|
||||||
|
include_balkan_european,
|
||||||
|
include_greek_mediterranean,
|
||||||
|
include_italian_mediterranean,
|
||||||
|
include_iberian_mediterranean,
|
||||||
|
strict_excludes,
|
||||||
|
):
|
||||||
|
result = build_ethnicity_list_json(
|
||||||
|
include_european=include_european,
|
||||||
|
include_mediterranean_mena=include_mediterranean_mena,
|
||||||
|
include_latina=include_latina,
|
||||||
|
include_east_asian=include_east_asian,
|
||||||
|
include_southeast_asian=include_southeast_asian,
|
||||||
|
include_south_asian=include_south_asian,
|
||||||
|
include_black_african=include_black_african,
|
||||||
|
include_indigenous=include_indigenous,
|
||||||
|
include_mixed=include_mixed,
|
||||||
|
include_asian=include_asian,
|
||||||
|
include_white_asian=include_white_asian,
|
||||||
|
include_western_european=include_western_european,
|
||||||
|
include_french_european=include_french_european,
|
||||||
|
include_germanic_european=include_germanic_european,
|
||||||
|
include_nordic_european=include_nordic_european,
|
||||||
|
include_celtic_european=include_celtic_european,
|
||||||
|
include_slavic_european=include_slavic_european,
|
||||||
|
include_baltic_european=include_baltic_european,
|
||||||
|
include_alpine_european=include_alpine_european,
|
||||||
|
include_balkan_european=include_balkan_european,
|
||||||
|
include_greek_mediterranean=include_greek_mediterranean,
|
||||||
|
include_italian_mediterranean=include_italian_mediterranean,
|
||||||
|
include_iberian_mediterranean=include_iberian_mediterranean,
|
||||||
|
strict_excludes=strict_excludes,
|
||||||
|
)
|
||||||
|
return result["ethnicity"], result["filter_config"], result["summary"]
|
||||||
|
|
||||||
|
|
||||||
class SxCPPromptBuilderFromConfigs:
|
class SxCPPromptBuilderFromConfigs:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(cls):
|
def INPUT_TYPES(cls):
|
||||||
@@ -714,6 +811,7 @@ class SxCPPromptBuilderFromConfigs:
|
|||||||
"cast_config": ("STRING", {"default": "", "multiline": True}),
|
"cast_config": ("STRING", {"default": "", "multiline": True}),
|
||||||
"generation_profile": ("STRING", {"default": "", "multiline": True}),
|
"generation_profile": ("STRING", {"default": "", "multiline": True}),
|
||||||
"filter_config": ("STRING", {"default": "", "multiline": True}),
|
"filter_config": ("STRING", {"default": "", "multiline": True}),
|
||||||
|
"ethnicity_list": ("STRING", {"default": "", "multiline": True}),
|
||||||
"seed_config": ("STRING", {"default": "", "multiline": True}),
|
"seed_config": ("STRING", {"default": "", "multiline": True}),
|
||||||
"camera_config": ("STRING", {"default": "", "multiline": True}),
|
"camera_config": ("STRING", {"default": "", "multiline": True}),
|
||||||
"character_profile": ("STRING", {"default": "", "multiline": True}),
|
"character_profile": ("STRING", {"default": "", "multiline": True}),
|
||||||
@@ -737,6 +835,7 @@ class SxCPPromptBuilderFromConfigs:
|
|||||||
cast_config="",
|
cast_config="",
|
||||||
generation_profile="",
|
generation_profile="",
|
||||||
filter_config="",
|
filter_config="",
|
||||||
|
ethnicity_list="",
|
||||||
seed_config="",
|
seed_config="",
|
||||||
camera_config="",
|
camera_config="",
|
||||||
character_profile="",
|
character_profile="",
|
||||||
@@ -751,7 +850,7 @@ class SxCPPromptBuilderFromConfigs:
|
|||||||
category_config=category_config or "",
|
category_config=category_config or "",
|
||||||
cast_config=cast_config or "",
|
cast_config=cast_config or "",
|
||||||
generation_profile=generation_profile or "",
|
generation_profile=generation_profile or "",
|
||||||
filter_config=filter_config or "",
|
filter_config=ethnicity_list or filter_config or "",
|
||||||
seed_config=seed_config or "",
|
seed_config=seed_config or "",
|
||||||
camera_config=camera_config or "",
|
camera_config=camera_config or "",
|
||||||
character_profile=character_profile or "",
|
character_profile=character_profile or "",
|
||||||
@@ -798,6 +897,7 @@ class SxCPCharacterSlot:
|
|||||||
"hardcore_clothing": ("STRING", {"default": ""}),
|
"hardcore_clothing": ("STRING", {"default": ""}),
|
||||||
},
|
},
|
||||||
"optional": {
|
"optional": {
|
||||||
|
"ethnicity_list": ("STRING", {"default": "", "multiline": True}),
|
||||||
"character_cast": ("STRING", {"default": "", "multiline": True}),
|
"character_cast": ("STRING", {"default": "", "multiline": True}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -832,6 +932,7 @@ class SxCPCharacterSlot:
|
|||||||
softcore_outfit="",
|
softcore_outfit="",
|
||||||
hardcore_clothing="",
|
hardcore_clothing="",
|
||||||
character_cast="",
|
character_cast="",
|
||||||
|
ethnicity_list="",
|
||||||
):
|
):
|
||||||
result = build_character_slot_json(
|
result = build_character_slot_json(
|
||||||
subject_type=subject_type,
|
subject_type=subject_type,
|
||||||
@@ -839,7 +940,7 @@ class SxCPCharacterSlot:
|
|||||||
slot_seed=slot_seed,
|
slot_seed=slot_seed,
|
||||||
age=age,
|
age=age,
|
||||||
manual_age=manual_age,
|
manual_age=manual_age,
|
||||||
ethnicity=ethnicity,
|
ethnicity=ethnicity_list or ethnicity,
|
||||||
figure=figure,
|
figure=figure,
|
||||||
body=body,
|
body=body,
|
||||||
manual_body=manual_body,
|
manual_body=manual_body,
|
||||||
@@ -888,6 +989,7 @@ class SxCPWomanSlot:
|
|||||||
"hardcore_clothing": ("STRING", {"default": ""}),
|
"hardcore_clothing": ("STRING", {"default": ""}),
|
||||||
},
|
},
|
||||||
"optional": {
|
"optional": {
|
||||||
|
"ethnicity_list": ("STRING", {"default": "", "multiline": True}),
|
||||||
"character_cast": ("STRING", {"default": "", "multiline": True}),
|
"character_cast": ("STRING", {"default": "", "multiline": True}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -920,6 +1022,7 @@ class SxCPWomanSlot:
|
|||||||
softcore_outfit="",
|
softcore_outfit="",
|
||||||
hardcore_clothing="",
|
hardcore_clothing="",
|
||||||
character_cast="",
|
character_cast="",
|
||||||
|
ethnicity_list="",
|
||||||
):
|
):
|
||||||
result = build_character_slot_json(
|
result = build_character_slot_json(
|
||||||
subject_type="woman",
|
subject_type="woman",
|
||||||
@@ -927,7 +1030,7 @@ class SxCPWomanSlot:
|
|||||||
slot_seed=slot_seed,
|
slot_seed=slot_seed,
|
||||||
age=age,
|
age=age,
|
||||||
manual_age=manual_age,
|
manual_age=manual_age,
|
||||||
ethnicity=ethnicity,
|
ethnicity=ethnicity_list or ethnicity,
|
||||||
figure=figure_bias,
|
figure=figure_bias,
|
||||||
body=body,
|
body=body,
|
||||||
manual_body=manual_body,
|
manual_body=manual_body,
|
||||||
@@ -975,6 +1078,7 @@ class SxCPManSlot:
|
|||||||
"hardcore_clothing": ("STRING", {"default": ""}),
|
"hardcore_clothing": ("STRING", {"default": ""}),
|
||||||
},
|
},
|
||||||
"optional": {
|
"optional": {
|
||||||
|
"ethnicity_list": ("STRING", {"default": "", "multiline": True}),
|
||||||
"character_cast": ("STRING", {"default": "", "multiline": True}),
|
"character_cast": ("STRING", {"default": "", "multiline": True}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -1007,6 +1111,7 @@ class SxCPManSlot:
|
|||||||
softcore_outfit="",
|
softcore_outfit="",
|
||||||
hardcore_clothing="",
|
hardcore_clothing="",
|
||||||
character_cast="",
|
character_cast="",
|
||||||
|
ethnicity_list="",
|
||||||
):
|
):
|
||||||
result = build_character_slot_json(
|
result = build_character_slot_json(
|
||||||
subject_type="man",
|
subject_type="man",
|
||||||
@@ -1014,7 +1119,7 @@ class SxCPManSlot:
|
|||||||
slot_seed=slot_seed,
|
slot_seed=slot_seed,
|
||||||
age=age,
|
age=age,
|
||||||
manual_age=manual_age,
|
manual_age=manual_age,
|
||||||
ethnicity=ethnicity,
|
ethnicity=ethnicity_list or ethnicity,
|
||||||
figure="random",
|
figure="random",
|
||||||
body=body,
|
body=body,
|
||||||
manual_body=manual_body,
|
manual_body=manual_body,
|
||||||
@@ -1397,6 +1502,7 @@ class SxCPInstaOFPromptPair:
|
|||||||
"seed_config": ("STRING", {"default": "", "multiline": True}),
|
"seed_config": ("STRING", {"default": "", "multiline": True}),
|
||||||
"options_json": ("STRING", {"default": "", "multiline": True}),
|
"options_json": ("STRING", {"default": "", "multiline": True}),
|
||||||
"filter_config": ("STRING", {"default": "", "multiline": True}),
|
"filter_config": ("STRING", {"default": "", "multiline": True}),
|
||||||
|
"ethnicity_list": ("STRING", {"default": "", "multiline": True}),
|
||||||
"camera_config": ("STRING", {"default": "", "multiline": True}),
|
"camera_config": ("STRING", {"default": "", "multiline": True}),
|
||||||
"softcore_camera_config": ("STRING", {"default": "", "multiline": True}),
|
"softcore_camera_config": ("STRING", {"default": "", "multiline": True}),
|
||||||
"hardcore_camera_config": ("STRING", {"default": "", "multiline": True}),
|
"hardcore_camera_config": ("STRING", {"default": "", "multiline": True}),
|
||||||
@@ -1433,6 +1539,7 @@ class SxCPInstaOFPromptPair:
|
|||||||
seed_config="",
|
seed_config="",
|
||||||
options_json="",
|
options_json="",
|
||||||
filter_config="",
|
filter_config="",
|
||||||
|
ethnicity_list="",
|
||||||
camera_config="",
|
camera_config="",
|
||||||
softcore_camera_config="",
|
softcore_camera_config="",
|
||||||
hardcore_camera_config="",
|
hardcore_camera_config="",
|
||||||
@@ -1455,7 +1562,7 @@ class SxCPInstaOFPromptPair:
|
|||||||
prepend_trigger_to_prompt=prepend_trigger_to_prompt,
|
prepend_trigger_to_prompt=prepend_trigger_to_prompt,
|
||||||
seed_config=seed_config or "",
|
seed_config=seed_config or "",
|
||||||
options_json=options_json or "",
|
options_json=options_json or "",
|
||||||
filter_config=filter_config or "",
|
filter_config=ethnicity_list or filter_config or "",
|
||||||
camera_config=camera_config or "",
|
camera_config=camera_config or "",
|
||||||
softcore_camera_config=softcore_camera_config or "",
|
softcore_camera_config=softcore_camera_config or "",
|
||||||
hardcore_camera_config=hardcore_camera_config or "",
|
hardcore_camera_config=hardcore_camera_config or "",
|
||||||
@@ -1487,6 +1594,7 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
"SxCPCategoryPreset": SxCPCategoryPreset,
|
"SxCPCategoryPreset": SxCPCategoryPreset,
|
||||||
"SxCPCastControl": SxCPCastControl,
|
"SxCPCastControl": SxCPCastControl,
|
||||||
"SxCPGenerationProfile": SxCPGenerationProfile,
|
"SxCPGenerationProfile": SxCPGenerationProfile,
|
||||||
|
"SxCPEthnicityList": SxCPEthnicityList,
|
||||||
"SxCPAdvancedFilters": SxCPAdvancedFilters,
|
"SxCPAdvancedFilters": SxCPAdvancedFilters,
|
||||||
"SxCPPromptBuilderFromConfigs": SxCPPromptBuilderFromConfigs,
|
"SxCPPromptBuilderFromConfigs": SxCPPromptBuilderFromConfigs,
|
||||||
"SxCPWomanSlot": SxCPWomanSlot,
|
"SxCPWomanSlot": SxCPWomanSlot,
|
||||||
@@ -1512,6 +1620,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
|||||||
"SxCPCategoryPreset": "SxCP Category Preset",
|
"SxCPCategoryPreset": "SxCP Category Preset",
|
||||||
"SxCPCastControl": "SxCP Cast Control",
|
"SxCPCastControl": "SxCP Cast Control",
|
||||||
"SxCPGenerationProfile": "SxCP Generation Profile",
|
"SxCPGenerationProfile": "SxCP Generation Profile",
|
||||||
|
"SxCPEthnicityList": "SxCP Ethnicity List",
|
||||||
"SxCPAdvancedFilters": "SxCP Advanced Filters",
|
"SxCPAdvancedFilters": "SxCP Advanced Filters",
|
||||||
"SxCPPromptBuilderFromConfigs": "SxCP Prompt Builder From Configs",
|
"SxCPPromptBuilderFromConfigs": "SxCP Prompt Builder From Configs",
|
||||||
"SxCPWomanSlot": "SxCP Woman Slot",
|
"SxCPWomanSlot": "SxCP Woman Slot",
|
||||||
|
|||||||
@@ -2782,6 +2782,18 @@ WHITE_KEYWORDS = (
|
|||||||
"french",
|
"french",
|
||||||
"mediterranean",
|
"mediterranean",
|
||||||
)
|
)
|
||||||
|
WESTERN_EUROPEAN_KEYWORDS = ("western european", "french", "germanic", "german", "dutch")
|
||||||
|
FRENCH_EUROPEAN_KEYWORDS = ("french",)
|
||||||
|
GERMANIC_EUROPEAN_KEYWORDS = ("germanic", "german")
|
||||||
|
NORDIC_EUROPEAN_KEYWORDS = ("nordic", "swedish", "norwegian", "danish", "scandinavian")
|
||||||
|
CELTIC_EUROPEAN_KEYWORDS = ("celtic", "irish", "scottish")
|
||||||
|
SLAVIC_EUROPEAN_KEYWORDS = ("slavic", "polish", "ukrainian")
|
||||||
|
BALTIC_EUROPEAN_KEYWORDS = ("baltic",)
|
||||||
|
ALPINE_EUROPEAN_KEYWORDS = ("alpine",)
|
||||||
|
BALKAN_EUROPEAN_KEYWORDS = ("balkan",)
|
||||||
|
GREEK_MEDITERRANEAN_KEYWORDS = ("greek",)
|
||||||
|
ITALIAN_MEDITERRANEAN_KEYWORDS = ("italian",)
|
||||||
|
IBERIAN_MEDITERRANEAN_KEYWORDS = ("spanish", "portuguese", "iberian")
|
||||||
EAST_ASIAN_KEYWORDS = (
|
EAST_ASIAN_KEYWORDS = (
|
||||||
"east asian",
|
"east asian",
|
||||||
"japanese",
|
"japanese",
|
||||||
@@ -2865,6 +2877,18 @@ ETHNICITY_KEYWORD_GROUPS = {
|
|||||||
"asian": ASIAN_KEYWORDS,
|
"asian": ASIAN_KEYWORDS,
|
||||||
"white_asian": WHITE_KEYWORDS + ASIAN_KEYWORDS,
|
"white_asian": WHITE_KEYWORDS + ASIAN_KEYWORDS,
|
||||||
"european": WHITE_KEYWORDS,
|
"european": WHITE_KEYWORDS,
|
||||||
|
"western_european": WESTERN_EUROPEAN_KEYWORDS,
|
||||||
|
"french_european": FRENCH_EUROPEAN_KEYWORDS,
|
||||||
|
"germanic_european": GERMANIC_EUROPEAN_KEYWORDS,
|
||||||
|
"nordic_european": NORDIC_EUROPEAN_KEYWORDS,
|
||||||
|
"celtic_european": CELTIC_EUROPEAN_KEYWORDS,
|
||||||
|
"slavic_european": SLAVIC_EUROPEAN_KEYWORDS,
|
||||||
|
"baltic_european": BALTIC_EUROPEAN_KEYWORDS,
|
||||||
|
"alpine_european": ALPINE_EUROPEAN_KEYWORDS,
|
||||||
|
"balkan_european": BALKAN_EUROPEAN_KEYWORDS,
|
||||||
|
"greek_mediterranean": GREEK_MEDITERRANEAN_KEYWORDS,
|
||||||
|
"italian_mediterranean": ITALIAN_MEDITERRANEAN_KEYWORDS,
|
||||||
|
"iberian_mediterranean": IBERIAN_MEDITERRANEAN_KEYWORDS,
|
||||||
"mediterranean_mena": MEDITERRANEAN_MENA_KEYWORDS,
|
"mediterranean_mena": MEDITERRANEAN_MENA_KEYWORDS,
|
||||||
"latina": LATINA_KEYWORDS,
|
"latina": LATINA_KEYWORDS,
|
||||||
"east_asian": EAST_ASIAN_KEYWORDS,
|
"east_asian": EAST_ASIAN_KEYWORDS,
|
||||||
|
|||||||
+166
-14
@@ -76,7 +76,47 @@ ETHNICITY_FILTER_CHOICES = [
|
|||||||
"mixed",
|
"mixed",
|
||||||
"asian",
|
"asian",
|
||||||
"white_asian",
|
"white_asian",
|
||||||
|
"western_european",
|
||||||
|
"french_european",
|
||||||
|
"germanic_european",
|
||||||
|
"nordic_european",
|
||||||
|
"celtic_european",
|
||||||
|
"slavic_european",
|
||||||
|
"baltic_european",
|
||||||
|
"alpine_european",
|
||||||
|
"balkan_european",
|
||||||
|
"greek_mediterranean",
|
||||||
|
"italian_mediterranean",
|
||||||
|
"iberian_mediterranean",
|
||||||
]
|
]
|
||||||
|
ETHNICITY_LIST_KEYS = tuple(choice for choice in ETHNICITY_FILTER_CHOICES if choice != "any")
|
||||||
|
ETHNICITY_BASE_LIST_KEYS = (
|
||||||
|
"european",
|
||||||
|
"mediterranean_mena",
|
||||||
|
"latina",
|
||||||
|
"east_asian",
|
||||||
|
"southeast_asian",
|
||||||
|
"south_asian",
|
||||||
|
"black_african",
|
||||||
|
"indigenous",
|
||||||
|
"mixed",
|
||||||
|
)
|
||||||
|
EUROPEAN_REGIONAL_LIST_KEYS = (
|
||||||
|
"western_european",
|
||||||
|
"french_european",
|
||||||
|
"germanic_european",
|
||||||
|
"nordic_european",
|
||||||
|
"celtic_european",
|
||||||
|
"slavic_european",
|
||||||
|
"baltic_european",
|
||||||
|
"alpine_european",
|
||||||
|
"balkan_european",
|
||||||
|
)
|
||||||
|
MEDITERRANEAN_REGIONAL_LIST_KEYS = (
|
||||||
|
"greek_mediterranean",
|
||||||
|
"italian_mediterranean",
|
||||||
|
"iberian_mediterranean",
|
||||||
|
)
|
||||||
|
|
||||||
CHARACTER_LABEL_CHOICES = [
|
CHARACTER_LABEL_CHOICES = [
|
||||||
"auto_chain",
|
"auto_chain",
|
||||||
@@ -1217,7 +1257,7 @@ def build_filter_config_json(
|
|||||||
enabled_ethnicities.extend(f"exclude_{key}" for key in disabled_ethnicities)
|
enabled_ethnicities.extend(f"exclude_{key}" for key in disabled_ethnicities)
|
||||||
if 0 < len(selected_ethnicities) < len(include_flags):
|
if 0 < len(selected_ethnicities) < len(include_flags):
|
||||||
ethnicity = "+".join(enabled_ethnicities)
|
ethnicity = "+".join(enabled_ethnicities)
|
||||||
elif ethnicity not in ETHNICITY_FILTER_CHOICES:
|
elif not _is_valid_ethnicity_filter(ethnicity):
|
||||||
ethnicity = "any"
|
ethnicity = "any"
|
||||||
return json.dumps(
|
return json.dumps(
|
||||||
{
|
{
|
||||||
@@ -1234,6 +1274,120 @@ def build_filter_config_json(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _ethnicity_text_from_value(value: Any) -> str:
|
||||||
|
if isinstance(value, dict):
|
||||||
|
return str(value.get("ethnicity") or "").strip()
|
||||||
|
text = str(value or "").strip()
|
||||||
|
if not text:
|
||||||
|
return ""
|
||||||
|
if text.startswith("{"):
|
||||||
|
try:
|
||||||
|
raw = json.loads(text)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
return text
|
||||||
|
if isinstance(raw, dict):
|
||||||
|
return str(raw.get("ethnicity") or "").strip()
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def _is_valid_ethnicity_filter(value: Any) -> bool:
|
||||||
|
text = _ethnicity_text_from_value(value)
|
||||||
|
return text == "any" or text in ETHNICITY_FILTER_CHOICES or "+" in text
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_ethnicity_filter(value: Any, default: str = "any", allow_random: bool = False) -> str:
|
||||||
|
text = _ethnicity_text_from_value(value)
|
||||||
|
if text.lower() in CHARACTER_RANDOM_TOKENS:
|
||||||
|
return "random" if allow_random else default
|
||||||
|
return text if _is_valid_ethnicity_filter(text) else default
|
||||||
|
|
||||||
|
|
||||||
|
def build_ethnicity_list_json(
|
||||||
|
include_european: bool = False,
|
||||||
|
include_mediterranean_mena: bool = False,
|
||||||
|
include_latina: bool = False,
|
||||||
|
include_east_asian: bool = False,
|
||||||
|
include_southeast_asian: bool = False,
|
||||||
|
include_south_asian: bool = False,
|
||||||
|
include_black_african: bool = False,
|
||||||
|
include_indigenous: bool = False,
|
||||||
|
include_mixed: bool = False,
|
||||||
|
include_asian: bool = False,
|
||||||
|
include_white_asian: bool = False,
|
||||||
|
include_western_european: bool = False,
|
||||||
|
include_french_european: bool = False,
|
||||||
|
include_germanic_european: bool = False,
|
||||||
|
include_nordic_european: bool = False,
|
||||||
|
include_celtic_european: bool = False,
|
||||||
|
include_slavic_european: bool = False,
|
||||||
|
include_baltic_european: bool = False,
|
||||||
|
include_alpine_european: bool = False,
|
||||||
|
include_balkan_european: bool = False,
|
||||||
|
include_greek_mediterranean: bool = False,
|
||||||
|
include_italian_mediterranean: bool = False,
|
||||||
|
include_iberian_mediterranean: bool = False,
|
||||||
|
strict_excludes: bool = True,
|
||||||
|
) -> dict[str, str]:
|
||||||
|
include_flags = {
|
||||||
|
"european": include_european,
|
||||||
|
"mediterranean_mena": include_mediterranean_mena,
|
||||||
|
"latina": include_latina,
|
||||||
|
"east_asian": include_east_asian,
|
||||||
|
"southeast_asian": include_southeast_asian,
|
||||||
|
"south_asian": include_south_asian,
|
||||||
|
"black_african": include_black_african,
|
||||||
|
"indigenous": include_indigenous,
|
||||||
|
"mixed": include_mixed,
|
||||||
|
"asian": include_asian,
|
||||||
|
"white_asian": include_white_asian,
|
||||||
|
"western_european": include_western_european,
|
||||||
|
"french_european": include_french_european,
|
||||||
|
"germanic_european": include_germanic_european,
|
||||||
|
"nordic_european": include_nordic_european,
|
||||||
|
"celtic_european": include_celtic_european,
|
||||||
|
"slavic_european": include_slavic_european,
|
||||||
|
"baltic_european": include_baltic_european,
|
||||||
|
"alpine_european": include_alpine_european,
|
||||||
|
"balkan_european": include_balkan_european,
|
||||||
|
"greek_mediterranean": include_greek_mediterranean,
|
||||||
|
"italian_mediterranean": include_italian_mediterranean,
|
||||||
|
"iberian_mediterranean": include_iberian_mediterranean,
|
||||||
|
}
|
||||||
|
selected = [key for key in ETHNICITY_LIST_KEYS if include_flags.get(key)]
|
||||||
|
if not selected or set(selected) == set(ETHNICITY_LIST_KEYS):
|
||||||
|
ethnicity = "any"
|
||||||
|
else:
|
||||||
|
tokens = list(selected)
|
||||||
|
if strict_excludes:
|
||||||
|
protected: set[str] = set()
|
||||||
|
if "asian" in selected:
|
||||||
|
protected.update(("east_asian", "southeast_asian", "south_asian"))
|
||||||
|
if "white_asian" in selected:
|
||||||
|
protected.update(("european", "east_asian", "southeast_asian", "south_asian", "mixed"))
|
||||||
|
if any(key in selected for key in EUROPEAN_REGIONAL_LIST_KEYS):
|
||||||
|
protected.add("european")
|
||||||
|
if any(key in selected for key in MEDITERRANEAN_REGIONAL_LIST_KEYS):
|
||||||
|
protected.add("mediterranean_mena")
|
||||||
|
if "mixed" in selected:
|
||||||
|
protected.update(ETHNICITY_BASE_LIST_KEYS)
|
||||||
|
tokens.extend(
|
||||||
|
f"exclude_{key}"
|
||||||
|
for key in ETHNICITY_BASE_LIST_KEYS
|
||||||
|
if key not in selected and key not in protected
|
||||||
|
)
|
||||||
|
ethnicity = "+".join(tokens)
|
||||||
|
filter_config = {
|
||||||
|
"ethnicity": ethnicity,
|
||||||
|
"ethnicity_includes": selected,
|
||||||
|
}
|
||||||
|
summary = "any ethnicity" if ethnicity == "any" else "ethnicity list: " + ", ".join(selected)
|
||||||
|
return {
|
||||||
|
"ethnicity": ethnicity,
|
||||||
|
"filter_config": json.dumps(filter_config, ensure_ascii=True, sort_keys=True),
|
||||||
|
"summary": summary,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def _parse_filter_config(filter_config: str | dict[str, Any] | None) -> dict[str, Any]:
|
def _parse_filter_config(filter_config: str | dict[str, Any] | None) -> dict[str, Any]:
|
||||||
defaults = {
|
defaults = {
|
||||||
"ethnicity": "any",
|
"ethnicity": "any",
|
||||||
@@ -1248,15 +1402,18 @@ def _parse_filter_config(filter_config: str | dict[str, Any] | None) -> dict[str
|
|||||||
if isinstance(filter_config, dict):
|
if isinstance(filter_config, dict):
|
||||||
raw = filter_config
|
raw = filter_config
|
||||||
else:
|
else:
|
||||||
try:
|
text = str(filter_config).strip()
|
||||||
raw = json.loads(str(filter_config))
|
if not text.startswith("{"):
|
||||||
except json.JSONDecodeError as exc:
|
raw = {"ethnicity": text}
|
||||||
raise ValueError(f"Invalid filter_config JSON: {exc}") from exc
|
else:
|
||||||
|
try:
|
||||||
|
raw = json.loads(text)
|
||||||
|
except json.JSONDecodeError as exc:
|
||||||
|
raise ValueError(f"Invalid filter_config JSON: {exc}") from exc
|
||||||
if not isinstance(raw, dict):
|
if not isinstance(raw, dict):
|
||||||
raise ValueError("filter_config must be a JSON object")
|
raise ValueError("filter_config must be a JSON object")
|
||||||
parsed = {**defaults, **raw}
|
parsed = {**defaults, **raw}
|
||||||
ethnicity = str(parsed.get("ethnicity") or "any")
|
parsed["ethnicity"] = normalize_ethnicity_filter(parsed.get("ethnicity"), "any")
|
||||||
parsed["ethnicity"] = ethnicity if ethnicity == "any" or ethnicity in ETHNICITY_FILTER_CHOICES or "+" in ethnicity else "any"
|
|
||||||
parsed["figure"] = parsed["figure"] if parsed.get("figure") in ("curvy", "balanced", "bombshell") else "curvy"
|
parsed["figure"] = parsed["figure"] if parsed.get("figure") in ("curvy", "balanced", "bombshell") else "curvy"
|
||||||
parsed["include_plus_size"] = bool(parsed.get("include_plus_size"))
|
parsed["include_plus_size"] = bool(parsed.get("include_plus_size"))
|
||||||
parsed["include_black_african"] = bool(parsed.get("include_black_african"))
|
parsed["include_black_african"] = bool(parsed.get("include_black_african"))
|
||||||
@@ -2481,12 +2638,7 @@ def _slot_manual_or_choice(choice: str, manual_value: str) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def _normalize_slot_ethnicity(value: Any) -> str:
|
def _normalize_slot_ethnicity(value: Any) -> str:
|
||||||
text = str(value or "").strip()
|
return normalize_ethnicity_filter(value, "random", allow_random=True)
|
||||||
if text.lower() in CHARACTER_RANDOM_TOKENS:
|
|
||||||
return "random"
|
|
||||||
if text == "any" or text in ETHNICITY_FILTER_CHOICES or "+" in text:
|
|
||||||
return text
|
|
||||||
return "random"
|
|
||||||
|
|
||||||
|
|
||||||
def _normalize_character_slot(slot: dict[str, Any]) -> dict[str, Any]:
|
def _normalize_character_slot(slot: dict[str, Any]) -> dict[str, Any]:
|
||||||
@@ -4308,7 +4460,7 @@ def build_prompt(
|
|||||||
start_index = max(1, int(start_index))
|
start_index = max(1, int(start_index))
|
||||||
seed = int(seed)
|
seed = int(seed)
|
||||||
clothing = clothing if clothing in ("full", "minimal") else "full"
|
clothing = clothing if clothing in ("full", "minimal") else "full"
|
||||||
ethnicity = ethnicity if ethnicity == "any" or ethnicity in ETHNICITY_FILTER_CHOICES or "+" in str(ethnicity) else "any"
|
ethnicity = normalize_ethnicity_filter(ethnicity, "any")
|
||||||
poses = poses if poses in ("standard", "evocative") else "standard"
|
poses = poses if poses in ("standard", "evocative") else "standard"
|
||||||
figure = figure if figure in ("curvy", "balanced", "bombshell") else "curvy"
|
figure = figure if figure in ("curvy", "balanced", "bombshell") else "curvy"
|
||||||
expression_enabled = not _is_false(expression_enabled)
|
expression_enabled = not _is_false(expression_enabled)
|
||||||
|
|||||||
Reference in New Issue
Block a user