Add configurable location pools
This commit is contained in:
@@ -61,6 +61,9 @@ node. For cleaner workflows, use the split nodes:
|
|||||||
weighted lists. With `women_start_count=1`, `women_weights=0.6,0.2` means
|
weighted lists. With `women_start_count=1`, `women_weights=0.6,0.2` means
|
||||||
60% one woman and 20% two women; with `men_start_count=0`,
|
60% one woman and 20% two women; with `men_start_count=0`,
|
||||||
`men_weights=0.5,0.3` means 50% no man and 30% one man.
|
`men_weights=0.5,0.3` means 50% no man and 30% one man.
|
||||||
|
- `SxCP Location Pool` outputs `location_config`. `replace` uses only the
|
||||||
|
selected/custom location pool; `add` keeps the category's own locations and
|
||||||
|
adds yours. Custom lines can be plain location text, or `slug: location text`.
|
||||||
- `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. Its clothing and pose overrides can be
|
Krea2-friendly, or Flux-original. Its clothing and pose overrides can be
|
||||||
@@ -82,7 +85,8 @@ The practical compact workflow is:
|
|||||||
|
|
||||||
`Category Preset` + `Cast Control` + `Generation Profile` + optional
|
`Category Preset` + `Cast Control` + `Generation Profile` + optional
|
||||||
`Advanced Filters`, `Seed Locker` or `Seed Control`, `Camera Control` or
|
`Advanced Filters`, `Seed Locker` or `Seed Control`, `Camera Control` or
|
||||||
`Camera Orbit Control`, `Woman Slot` / `Man Slot`, and `Character Profile`
|
`Camera Orbit Control`, `Location Pool`, `Woman Slot` / `Man Slot`, and
|
||||||
|
`Character Profile`
|
||||||
into `Prompt Builder From Configs`.
|
into `Prompt Builder From Configs`.
|
||||||
|
|
||||||
An importable default workflow is included at
|
An importable default workflow is included at
|
||||||
|
|||||||
+50
@@ -18,6 +18,7 @@ SXCP_ETHNICITY_LIST = "SXCP_ETHNICITY_LIST"
|
|||||||
SXCP_FILTER_CONFIG = "SXCP_FILTER_CONFIG"
|
SXCP_FILTER_CONFIG = "SXCP_FILTER_CONFIG"
|
||||||
SXCP_SEED_CONFIG = "SXCP_SEED_CONFIG"
|
SXCP_SEED_CONFIG = "SXCP_SEED_CONFIG"
|
||||||
SXCP_CAMERA_CONFIG = "SXCP_CAMERA_CONFIG"
|
SXCP_CAMERA_CONFIG = "SXCP_CAMERA_CONFIG"
|
||||||
|
SXCP_LOCATION_CONFIG = "SXCP_LOCATION_CONFIG"
|
||||||
SXCP_CATEGORY_CONFIG = "SXCP_CATEGORY_CONFIG"
|
SXCP_CATEGORY_CONFIG = "SXCP_CATEGORY_CONFIG"
|
||||||
SXCP_CAST_CONFIG = "SXCP_CAST_CONFIG"
|
SXCP_CAST_CONFIG = "SXCP_CAST_CONFIG"
|
||||||
SXCP_GENERATION_PROFILE = "SXCP_GENERATION_PROFILE"
|
SXCP_GENERATION_PROFILE = "SXCP_GENERATION_PROFILE"
|
||||||
@@ -58,12 +59,14 @@ COMMON_INPUT_TOOLTIPS = {
|
|||||||
"ethnicity_list": "Optional ethnicity pool. When connected, it overrides the slot or generator ethnicity picker.",
|
"ethnicity_list": "Optional ethnicity pool. When connected, it overrides the slot or generator ethnicity picker.",
|
||||||
"seed_config": "Per-axis seed config. Connect Global Seed, Seed Locker, or Seed Control here.",
|
"seed_config": "Per-axis seed config. Connect Global Seed, Seed Locker, or Seed Control here.",
|
||||||
"camera_config": "Camera config used by the prompt formatter when camera mode is from_camera_config.",
|
"camera_config": "Camera config used by the prompt formatter when camera mode is from_camera_config.",
|
||||||
|
"location_config": "Location config from SxCP Location Pool. It can replace or add to the category scene pool.",
|
||||||
"softcore_camera_config": "Camera config used only for the softcore Insta/OF prompt. Falls back to camera_config if empty.",
|
"softcore_camera_config": "Camera config used only for the softcore Insta/OF prompt. Falls back to camera_config if empty.",
|
||||||
"hardcore_camera_config": "Camera config used only for the hardcore Insta/OF prompt. Falls back to camera_config if empty.",
|
"hardcore_camera_config": "Camera config used only for the hardcore Insta/OF prompt. Falls back to camera_config if empty.",
|
||||||
"character_profile": "Saved or loaded single-character profile. Character slots override this for configured casts.",
|
"character_profile": "Saved or loaded single-character profile. Character slots override this for configured casts.",
|
||||||
"character_cast": "Chain character slots here. The node closest to the final generator becomes the next auto_chain label.",
|
"character_cast": "Chain character slots here. The node closest to the final generator becomes the next auto_chain label.",
|
||||||
"character_slot": "Single slot payload for saving/loading profiles or debugging one character.",
|
"character_slot": "Single slot payload for saving/loading profiles or debugging one character.",
|
||||||
"hardcore_position_config": "Hardcore action/position config. Chain Position Pool into Action Filter, then into the generator.",
|
"hardcore_position_config": "Hardcore action/position config. Chain Position Pool into Action Filter, then into the generator.",
|
||||||
|
"custom_locations": "One custom location per line. Use plain text, or slug: location text.",
|
||||||
"metadata_json": "Structured metadata from an SxCP generator. Prefer this over raw prompt text for formatters and profile save.",
|
"metadata_json": "Structured metadata from an SxCP generator. Prefer this over raw prompt text for formatters and profile save.",
|
||||||
"source_text": "Raw prompt, caption, or metadata JSON depending on input_hint.",
|
"source_text": "Raw prompt, caption, or metadata JSON depending on input_hint.",
|
||||||
"source_text_input": "Optional linked raw prompt/caption input. When connected, it overrides the source_text widget.",
|
"source_text_input": "Optional linked raw prompt/caption input. When connected, it overrides the source_text widget.",
|
||||||
@@ -413,6 +416,7 @@ try:
|
|||||||
build_hardcore_action_filter_json,
|
build_hardcore_action_filter_json,
|
||||||
build_hardcore_position_pool_json,
|
build_hardcore_position_pool_json,
|
||||||
build_insta_of_options_json,
|
build_insta_of_options_json,
|
||||||
|
build_location_pool_json,
|
||||||
build_insta_of_pair,
|
build_insta_of_pair,
|
||||||
build_prompt,
|
build_prompt,
|
||||||
build_prompt_from_configs,
|
build_prompt_from_configs,
|
||||||
@@ -457,6 +461,7 @@ try:
|
|||||||
hardcore_position_key_choices,
|
hardcore_position_key_choices,
|
||||||
hardcore_detail_density_choices,
|
hardcore_detail_density_choices,
|
||||||
load_character_profile_json,
|
load_character_profile_json,
|
||||||
|
location_pool_preset_choices,
|
||||||
save_character_profile_payload,
|
save_character_profile_payload,
|
||||||
seed_mode_choices,
|
seed_mode_choices,
|
||||||
subcategory_choices,
|
subcategory_choices,
|
||||||
@@ -491,6 +496,7 @@ except ImportError:
|
|||||||
build_hardcore_action_filter_json,
|
build_hardcore_action_filter_json,
|
||||||
build_hardcore_position_pool_json,
|
build_hardcore_position_pool_json,
|
||||||
build_insta_of_options_json,
|
build_insta_of_options_json,
|
||||||
|
build_location_pool_json,
|
||||||
build_insta_of_pair,
|
build_insta_of_pair,
|
||||||
build_prompt,
|
build_prompt,
|
||||||
build_prompt_from_configs,
|
build_prompt_from_configs,
|
||||||
@@ -535,6 +541,7 @@ except ImportError:
|
|||||||
hardcore_position_key_choices,
|
hardcore_position_key_choices,
|
||||||
hardcore_detail_density_choices,
|
hardcore_detail_density_choices,
|
||||||
load_character_profile_json,
|
load_character_profile_json,
|
||||||
|
location_pool_preset_choices,
|
||||||
save_character_profile_payload,
|
save_character_profile_payload,
|
||||||
seed_mode_choices,
|
seed_mode_choices,
|
||||||
subcategory_choices,
|
subcategory_choices,
|
||||||
@@ -646,6 +653,7 @@ class SxCPPromptBuilder:
|
|||||||
"ethnicity_list": (SXCP_ETHNICITY_LIST,),
|
"ethnicity_list": (SXCP_ETHNICITY_LIST,),
|
||||||
"seed_config": (SXCP_SEED_CONFIG,),
|
"seed_config": (SXCP_SEED_CONFIG,),
|
||||||
"camera_config": (SXCP_CAMERA_CONFIG,),
|
"camera_config": (SXCP_CAMERA_CONFIG,),
|
||||||
|
"location_config": (SXCP_LOCATION_CONFIG,),
|
||||||
"character_profile": (SXCP_CHARACTER_PROFILE,),
|
"character_profile": (SXCP_CHARACTER_PROFILE,),
|
||||||
"character_cast": (SXCP_CHARACTER_CAST,),
|
"character_cast": (SXCP_CHARACTER_CAST,),
|
||||||
"hardcore_position_config": (SXCP_HARDCORE_POSITION_CONFIG,),
|
"hardcore_position_config": (SXCP_HARDCORE_POSITION_CONFIG,),
|
||||||
@@ -681,6 +689,7 @@ class SxCPPromptBuilder:
|
|||||||
prepend_trigger_to_prompt,
|
prepend_trigger_to_prompt,
|
||||||
seed_config="",
|
seed_config="",
|
||||||
camera_config="",
|
camera_config="",
|
||||||
|
location_config="",
|
||||||
character_profile="",
|
character_profile="",
|
||||||
character_cast="",
|
character_cast="",
|
||||||
hardcore_position_config="",
|
hardcore_position_config="",
|
||||||
@@ -715,6 +724,7 @@ class SxCPPromptBuilder:
|
|||||||
extra_negative=extra_negative or "",
|
extra_negative=extra_negative or "",
|
||||||
seed_config=seed_config or "",
|
seed_config=seed_config or "",
|
||||||
camera_config=camera_config or "",
|
camera_config=camera_config or "",
|
||||||
|
location_config=location_config or "",
|
||||||
character_profile=character_profile or "",
|
character_profile=character_profile or "",
|
||||||
character_cast=character_cast or "",
|
character_cast=character_cast or "",
|
||||||
hardcore_position_config=hardcore_position_config or "",
|
hardcore_position_config=hardcore_position_config or "",
|
||||||
@@ -1162,6 +1172,38 @@ class SxCPCategoryPreset:
|
|||||||
return config, parsed["category"], parsed["subcategory"]
|
return config, parsed["category"], parsed["subcategory"]
|
||||||
|
|
||||||
|
|
||||||
|
class SxCPLocationPool:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"enabled": ("BOOLEAN", {"default": True}),
|
||||||
|
"combine_mode": (["replace", "add"], {"default": "replace"}),
|
||||||
|
"preset": (location_pool_preset_choices(), {"default": "custom_only"}),
|
||||||
|
"custom_locations": ("STRING", {"default": "", "multiline": True}),
|
||||||
|
},
|
||||||
|
"optional": {
|
||||||
|
"location_config": (SXCP_LOCATION_CONFIG,),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = (SXCP_LOCATION_CONFIG, "STRING")
|
||||||
|
RETURN_NAMES = ("location_config", "summary")
|
||||||
|
FUNCTION = "build"
|
||||||
|
CATEGORY = "prompt_builder"
|
||||||
|
|
||||||
|
def build(self, enabled, combine_mode, preset, custom_locations, location_config=""):
|
||||||
|
config = build_location_pool_json(
|
||||||
|
enabled=enabled,
|
||||||
|
combine_mode=combine_mode,
|
||||||
|
preset=preset,
|
||||||
|
custom_locations=custom_locations or "",
|
||||||
|
location_config=location_config or "",
|
||||||
|
)
|
||||||
|
parsed = json.loads(config)
|
||||||
|
return config, parsed.get("summary", "")
|
||||||
|
|
||||||
|
|
||||||
class SxCPCastControl:
|
class SxCPCastControl:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(cls):
|
def INPUT_TYPES(cls):
|
||||||
@@ -1913,6 +1955,7 @@ class SxCPPromptBuilderFromConfigs:
|
|||||||
"ethnicity_list": (SXCP_ETHNICITY_LIST,),
|
"ethnicity_list": (SXCP_ETHNICITY_LIST,),
|
||||||
"seed_config": (SXCP_SEED_CONFIG,),
|
"seed_config": (SXCP_SEED_CONFIG,),
|
||||||
"camera_config": (SXCP_CAMERA_CONFIG,),
|
"camera_config": (SXCP_CAMERA_CONFIG,),
|
||||||
|
"location_config": (SXCP_LOCATION_CONFIG,),
|
||||||
"character_profile": (SXCP_CHARACTER_PROFILE,),
|
"character_profile": (SXCP_CHARACTER_PROFILE,),
|
||||||
"character_cast": (SXCP_CHARACTER_CAST,),
|
"character_cast": (SXCP_CHARACTER_CAST,),
|
||||||
"hardcore_position_config": (SXCP_HARDCORE_POSITION_CONFIG,),
|
"hardcore_position_config": (SXCP_HARDCORE_POSITION_CONFIG,),
|
||||||
@@ -1938,6 +1981,7 @@ class SxCPPromptBuilderFromConfigs:
|
|||||||
ethnicity_list="",
|
ethnicity_list="",
|
||||||
seed_config="",
|
seed_config="",
|
||||||
camera_config="",
|
camera_config="",
|
||||||
|
location_config="",
|
||||||
character_profile="",
|
character_profile="",
|
||||||
character_cast="",
|
character_cast="",
|
||||||
hardcore_position_config="",
|
hardcore_position_config="",
|
||||||
@@ -1954,6 +1998,7 @@ class SxCPPromptBuilderFromConfigs:
|
|||||||
filter_config=ethnicity_list or 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 "",
|
||||||
|
location_config=location_config or "",
|
||||||
character_profile=character_profile or "",
|
character_profile=character_profile or "",
|
||||||
character_cast=character_cast or "",
|
character_cast=character_cast or "",
|
||||||
hardcore_position_config=hardcore_position_config or "",
|
hardcore_position_config=hardcore_position_config or "",
|
||||||
@@ -2674,6 +2719,7 @@ class SxCPInstaOFPromptPair:
|
|||||||
"camera_config": (SXCP_CAMERA_CONFIG,),
|
"camera_config": (SXCP_CAMERA_CONFIG,),
|
||||||
"softcore_camera_config": (SXCP_CAMERA_CONFIG,),
|
"softcore_camera_config": (SXCP_CAMERA_CONFIG,),
|
||||||
"hardcore_camera_config": (SXCP_CAMERA_CONFIG,),
|
"hardcore_camera_config": (SXCP_CAMERA_CONFIG,),
|
||||||
|
"location_config": (SXCP_LOCATION_CONFIG,),
|
||||||
"character_profile": (SXCP_CHARACTER_PROFILE,),
|
"character_profile": (SXCP_CHARACTER_PROFILE,),
|
||||||
"character_cast": (SXCP_CHARACTER_CAST,),
|
"character_cast": (SXCP_CHARACTER_CAST,),
|
||||||
"hardcore_position_config": (SXCP_HARDCORE_POSITION_CONFIG,),
|
"hardcore_position_config": (SXCP_HARDCORE_POSITION_CONFIG,),
|
||||||
@@ -2712,6 +2758,7 @@ class SxCPInstaOFPromptPair:
|
|||||||
camera_config="",
|
camera_config="",
|
||||||
softcore_camera_config="",
|
softcore_camera_config="",
|
||||||
hardcore_camera_config="",
|
hardcore_camera_config="",
|
||||||
|
location_config="",
|
||||||
character_profile="",
|
character_profile="",
|
||||||
character_cast="",
|
character_cast="",
|
||||||
hardcore_position_config="",
|
hardcore_position_config="",
|
||||||
@@ -2736,6 +2783,7 @@ class SxCPInstaOFPromptPair:
|
|||||||
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 "",
|
||||||
|
location_config=location_config or "",
|
||||||
character_profile=character_profile or "",
|
character_profile=character_profile or "",
|
||||||
character_cast=character_cast or "",
|
character_cast=character_cast or "",
|
||||||
hardcore_position_config=hardcore_position_config or "",
|
hardcore_position_config=hardcore_position_config or "",
|
||||||
@@ -2764,6 +2812,7 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
"SxCPCameraOrbitControl": SxCPCameraOrbitControl,
|
"SxCPCameraOrbitControl": SxCPCameraOrbitControl,
|
||||||
"SxCPQwenCameraTranslator": SxCPQwenCameraTranslator,
|
"SxCPQwenCameraTranslator": SxCPQwenCameraTranslator,
|
||||||
"SxCPCategoryPreset": SxCPCategoryPreset,
|
"SxCPCategoryPreset": SxCPCategoryPreset,
|
||||||
|
"SxCPLocationPool": SxCPLocationPool,
|
||||||
"SxCPCastControl": SxCPCastControl,
|
"SxCPCastControl": SxCPCastControl,
|
||||||
"SxCPCastBias": SxCPCastBias,
|
"SxCPCastBias": SxCPCastBias,
|
||||||
"SxCPGenerationProfile": SxCPGenerationProfile,
|
"SxCPGenerationProfile": SxCPGenerationProfile,
|
||||||
@@ -2806,6 +2855,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
|||||||
"SxCPCameraOrbitControl": "SxCP Camera Orbit Control",
|
"SxCPCameraOrbitControl": "SxCP Camera Orbit Control",
|
||||||
"SxCPQwenCameraTranslator": "SxCP Qwen Camera Translator",
|
"SxCPQwenCameraTranslator": "SxCP Qwen Camera Translator",
|
||||||
"SxCPCategoryPreset": "SxCP Category Preset",
|
"SxCPCategoryPreset": "SxCP Category Preset",
|
||||||
|
"SxCPLocationPool": "SxCP Location Pool",
|
||||||
"SxCPCastControl": "SxCP Cast Control",
|
"SxCPCastControl": "SxCP Cast Control",
|
||||||
"SxCPCastBias": "SxCP Cast Bias",
|
"SxCPCastBias": "SxCP Cast Bias",
|
||||||
"SxCPGenerationProfile": "SxCP Generation Profile",
|
"SxCPGenerationProfile": "SxCP Generation Profile",
|
||||||
|
|||||||
+240
-2
@@ -1310,6 +1310,36 @@ def load_scene_pool_library() -> dict[str, list[Any]]:
|
|||||||
return _load_named_pool_library("scene_pools")
|
return _load_named_pool_library("scene_pools")
|
||||||
|
|
||||||
|
|
||||||
|
LOCATION_POOL_PRESETS = {
|
||||||
|
"custom_only": (),
|
||||||
|
"all_json_locations": ("*",),
|
||||||
|
"casual_all": ("casual_",),
|
||||||
|
"casual_urban": ("casual_urban_scenes",),
|
||||||
|
"casual_summer": ("casual_summer_scenes",),
|
||||||
|
"casual_home": ("casual_lounge_scenes",),
|
||||||
|
"casual_smart": ("casual_smart_scenes",),
|
||||||
|
"creator_softcore": ("softcore_creator_scenes", "mirror_scenes", "boudoir_bedroom_scenes"),
|
||||||
|
"mirror_rooms": ("mirror_scenes", "hardcore_mirror_scenes"),
|
||||||
|
"boudoir_bedroom": ("boudoir_bedroom_scenes", "hardcore_bed_scenes"),
|
||||||
|
"fetish_studio": ("fetish_studio_scenes",),
|
||||||
|
"costume_backstage": ("costume_backstage_scenes",),
|
||||||
|
"hardcore_all": ("hardcore_",),
|
||||||
|
"hardcore_private": ("hardcore_private_scenes",),
|
||||||
|
"hardcore_bed": ("hardcore_bed_scenes",),
|
||||||
|
"hardcore_penetrative": ("hardcore_penetrative_scenes",),
|
||||||
|
"hardcore_oral": ("hardcore_oral_scenes",),
|
||||||
|
"hardcore_anal": ("hardcore_anal_scenes",),
|
||||||
|
"hardcore_threesome": ("hardcore_threesome_scenes",),
|
||||||
|
"hardcore_group": ("hardcore_group_scenes",),
|
||||||
|
"hardcore_climax": ("hardcore_climax_scenes",),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def location_pool_preset_choices() -> list[str]:
|
||||||
|
pool_choices = [f"pool:{key}" for key in sorted(load_scene_pool_library())]
|
||||||
|
return list(LOCATION_POOL_PRESETS) + pool_choices
|
||||||
|
|
||||||
|
|
||||||
def load_expression_pool_library() -> dict[str, list[Any]]:
|
def load_expression_pool_library() -> dict[str, list[Any]]:
|
||||||
return _load_named_pool_library("expression_pools")
|
return _load_named_pool_library("expression_pools")
|
||||||
|
|
||||||
@@ -1676,6 +1706,124 @@ def build_filter_config_json(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _location_pool_names_for_preset(preset: str) -> list[str]:
|
||||||
|
scene_pools = load_scene_pool_library()
|
||||||
|
preset = str(preset or "custom_only")
|
||||||
|
if preset.startswith("pool:"):
|
||||||
|
pool_name = preset.split(":", 1)[1].strip()
|
||||||
|
return [pool_name] if pool_name in scene_pools else []
|
||||||
|
selectors = LOCATION_POOL_PRESETS.get(preset, ())
|
||||||
|
names: list[str] = []
|
||||||
|
for selector in selectors:
|
||||||
|
if selector == "*":
|
||||||
|
_unique_extend(names, sorted(scene_pools))
|
||||||
|
elif selector.endswith("_"):
|
||||||
|
_unique_extend(names, sorted(name for name in scene_pools if name.startswith(selector)))
|
||||||
|
elif selector in scene_pools:
|
||||||
|
_unique_extend(names, [selector])
|
||||||
|
return names
|
||||||
|
|
||||||
|
|
||||||
|
def _custom_location_entries(custom_locations: str) -> list[dict[str, str]]:
|
||||||
|
entries: list[dict[str, str]] = []
|
||||||
|
for raw_line in str(custom_locations or "").splitlines():
|
||||||
|
line = raw_line.strip()
|
||||||
|
if not line or line.startswith("#"):
|
||||||
|
continue
|
||||||
|
slug = ""
|
||||||
|
prompt = line
|
||||||
|
if ":" in line:
|
||||||
|
maybe_slug, maybe_prompt = line.split(":", 1)
|
||||||
|
if maybe_slug.strip() and maybe_prompt.strip():
|
||||||
|
slug = _slug(maybe_slug)
|
||||||
|
prompt = maybe_prompt.strip()
|
||||||
|
prompt = prompt.strip()
|
||||||
|
if prompt:
|
||||||
|
entries.append({"slug": slug or _slug(prompt), "prompt": prompt})
|
||||||
|
return entries
|
||||||
|
|
||||||
|
|
||||||
|
def _scene_entries_for_pool_names(pool_names: list[str]) -> list[Any]:
|
||||||
|
scene_pools = load_scene_pool_library()
|
||||||
|
entries: list[Any] = []
|
||||||
|
for pool_name in pool_names:
|
||||||
|
if pool_name not in scene_pools:
|
||||||
|
continue
|
||||||
|
_unique_extend(entries, scene_pools[pool_name])
|
||||||
|
return entries
|
||||||
|
|
||||||
|
|
||||||
|
def build_location_pool_json(
|
||||||
|
enabled: bool = True,
|
||||||
|
combine_mode: str = "replace",
|
||||||
|
preset: str = "custom_only",
|
||||||
|
custom_locations: str = "",
|
||||||
|
location_config: str | dict[str, Any] | None = "",
|
||||||
|
) -> str:
|
||||||
|
incoming = _parse_location_config(location_config)
|
||||||
|
combine_mode = combine_mode if combine_mode in ("replace", "add") else "replace"
|
||||||
|
pool_names = _location_pool_names_for_preset(preset)
|
||||||
|
entries = _scene_entries_for_pool_names(pool_names)
|
||||||
|
_unique_extend(entries, _custom_location_entries(custom_locations))
|
||||||
|
|
||||||
|
if combine_mode == "add" and incoming.get("enabled"):
|
||||||
|
apply_mode = str(incoming.get("apply_mode") or "replace")
|
||||||
|
merged_pool_names = _list_from(incoming.get("pool_names"))
|
||||||
|
_unique_extend(merged_pool_names, pool_names)
|
||||||
|
merged_entries = _list_from(incoming.get("scene_entries"))
|
||||||
|
_unique_extend(merged_entries, entries)
|
||||||
|
else:
|
||||||
|
apply_mode = "replace" if combine_mode == "replace" else "add"
|
||||||
|
merged_pool_names = pool_names
|
||||||
|
merged_entries = entries
|
||||||
|
|
||||||
|
active = bool(enabled) and bool(merged_entries)
|
||||||
|
summary = (
|
||||||
|
f"{apply_mode}; pools={len(merged_pool_names)}; locations={len(merged_entries)}"
|
||||||
|
if active
|
||||||
|
else "disabled or empty"
|
||||||
|
)
|
||||||
|
return json.dumps(
|
||||||
|
{
|
||||||
|
"enabled": active,
|
||||||
|
"apply_mode": apply_mode,
|
||||||
|
"pool_names": merged_pool_names,
|
||||||
|
"scene_entries": merged_entries,
|
||||||
|
"summary": summary,
|
||||||
|
},
|
||||||
|
ensure_ascii=True,
|
||||||
|
sort_keys=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_location_config(location_config: str | dict[str, Any] | None) -> dict[str, Any]:
|
||||||
|
if not location_config:
|
||||||
|
return {"enabled": False, "apply_mode": "replace", "pool_names": [], "scene_entries": []}
|
||||||
|
if isinstance(location_config, dict):
|
||||||
|
raw = dict(location_config)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
raw = json.loads(str(location_config))
|
||||||
|
except json.JSONDecodeError as exc:
|
||||||
|
raise ValueError(f"Invalid location_config JSON: {exc}") from exc
|
||||||
|
if not isinstance(raw, dict):
|
||||||
|
raise ValueError("location_config must be a JSON object")
|
||||||
|
entries = _list_from(raw.get("scene_entries"))
|
||||||
|
if not entries and raw.get("pool_names"):
|
||||||
|
entries = _scene_entries_for_pool_names([str(name) for name in _list_from(raw.get("pool_names"))])
|
||||||
|
return {
|
||||||
|
"enabled": bool(raw.get("enabled")) and bool(entries),
|
||||||
|
"apply_mode": str(raw.get("apply_mode") or "replace") if str(raw.get("apply_mode") or "replace") in ("replace", "add") else "replace",
|
||||||
|
"pool_names": [str(name) for name in _list_from(raw.get("pool_names")) if str(name).strip()],
|
||||||
|
"scene_entries": entries,
|
||||||
|
"summary": str(raw.get("summary") or ""),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _location_config_active(location_config: dict[str, Any]) -> bool:
|
||||||
|
return bool(location_config.get("enabled")) and bool(location_config.get("scene_entries"))
|
||||||
|
|
||||||
|
|
||||||
def _ethnicity_text_from_value(value: Any) -> str:
|
def _ethnicity_text_from_value(value: Any) -> str:
|
||||||
if isinstance(value, dict):
|
if isinstance(value, dict):
|
||||||
return str(value.get("ethnicity") or "").strip()
|
return str(value.get("ethnicity") or "").strip()
|
||||||
@@ -5620,7 +5768,17 @@ def _subject_context(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _scene_pool(category: dict[str, Any], subcategory: dict[str, Any], item: Any, subject_type: str) -> list[Any]:
|
def _scene_pool(
|
||||||
|
category: dict[str, Any],
|
||||||
|
subcategory: dict[str, Any],
|
||||||
|
item: Any,
|
||||||
|
subject_type: str,
|
||||||
|
location_config: dict[str, Any] | None = None,
|
||||||
|
) -> list[Any]:
|
||||||
|
location_config = location_config or {}
|
||||||
|
location_entries = _list_from(location_config.get("scene_entries"))
|
||||||
|
if _location_config_active(location_config) and location_config.get("apply_mode") == "replace":
|
||||||
|
return location_entries
|
||||||
fallback = g.GROUP_SCENES if subject_type in ("group", "configured_cast") else g.SCENES
|
fallback = g.GROUP_SCENES if subject_type in ("group", "configured_cast") else g.SCENES
|
||||||
scene_entries: list[Any] = []
|
scene_entries: list[Any] = []
|
||||||
scene_pools = load_scene_pool_library()
|
scene_pools = load_scene_pool_library()
|
||||||
@@ -5642,9 +5800,63 @@ def _scene_pool(category: dict[str, Any], subcategory: dict[str, Any], item: Any
|
|||||||
if ref_name not in scene_pools:
|
if ref_name not in scene_pools:
|
||||||
raise ValueError(f"Unknown scene pool '{ref_name}'")
|
raise ValueError(f"Unknown scene pool '{ref_name}'")
|
||||||
_unique_extend(scene_entries, scene_pools[ref_name])
|
_unique_extend(scene_entries, scene_pools[ref_name])
|
||||||
|
if _location_config_active(location_config) and location_config.get("apply_mode") == "add":
|
||||||
|
_unique_extend(scene_entries, location_entries)
|
||||||
return scene_entries or fallback
|
return scene_entries or fallback
|
||||||
|
|
||||||
|
|
||||||
|
def _legacy_scene_entries_for_row(row: dict[str, Any]) -> list[Any]:
|
||||||
|
subject = str(row.get("primary_subject") or "").lower()
|
||||||
|
if "group" in subject or "layout" in subject:
|
||||||
|
return list(g.GROUP_SCENES)
|
||||||
|
return list(g.SCENES)
|
||||||
|
|
||||||
|
|
||||||
|
def _legacy_scene_text_for_slug(slug: str) -> str:
|
||||||
|
for entry in list(g.SCENES) + list(g.GROUP_SCENES):
|
||||||
|
entry_slug, entry_text = _pair_from(entry)
|
||||||
|
if entry_slug == slug:
|
||||||
|
return entry_text
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def _apply_location_config_to_legacy_row(
|
||||||
|
row: dict[str, Any],
|
||||||
|
location_config: dict[str, Any],
|
||||||
|
seed_config: dict[str, int],
|
||||||
|
seed: int,
|
||||||
|
row_number: int,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
if not _location_config_active(location_config):
|
||||||
|
return row
|
||||||
|
location_entries = _list_from(location_config.get("scene_entries"))
|
||||||
|
if location_config.get("apply_mode") == "add":
|
||||||
|
choices = _legacy_scene_entries_for_row(row)
|
||||||
|
_unique_extend(choices, location_entries)
|
||||||
|
else:
|
||||||
|
choices = location_entries
|
||||||
|
scene_rng = _axis_rng(seed_config, "scene", seed, row_number)
|
||||||
|
scene_slug, scene_text = _choose_pair(scene_rng, choices)
|
||||||
|
old_slug = str(row.get("scene") or "")
|
||||||
|
old_text = _legacy_scene_text_for_slug(old_slug)
|
||||||
|
row["source_scene"] = old_slug
|
||||||
|
row["source_scene_text"] = old_text
|
||||||
|
row["scene"] = scene_slug
|
||||||
|
row["scene_text"] = scene_text
|
||||||
|
row["location_config"] = location_config
|
||||||
|
if old_text:
|
||||||
|
row["prompt"] = str(row.get("prompt") or "").replace(f"Scene: {old_text}.", f"Scene: {scene_text}.")
|
||||||
|
row["caption"] = str(row.get("caption") or "").replace(f", {old_text},", f", {scene_text},")
|
||||||
|
else:
|
||||||
|
row["prompt"] = re.sub(
|
||||||
|
r"Scene:\s*.*?\.\s*Pose:",
|
||||||
|
f"Scene: {scene_text}. Pose:",
|
||||||
|
str(row.get("prompt") or ""),
|
||||||
|
count=1,
|
||||||
|
)
|
||||||
|
return row
|
||||||
|
|
||||||
|
|
||||||
def _sources_with_inheritance(
|
def _sources_with_inheritance(
|
||||||
category: dict[str, Any],
|
category: dict[str, Any],
|
||||||
subcategory: dict[str, Any],
|
subcategory: dict[str, Any],
|
||||||
@@ -5870,6 +6082,7 @@ def _build_custom_row(
|
|||||||
character_cast: str | dict[str, Any] | list[Any] | None = None,
|
character_cast: str | dict[str, Any] | list[Any] | None = None,
|
||||||
expression_phase: str = "",
|
expression_phase: str = "",
|
||||||
hardcore_position_config: str | dict[str, Any] | None = None,
|
hardcore_position_config: str | dict[str, Any] | None = None,
|
||||||
|
location_config: str | dict[str, Any] | None = None,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
categories = load_category_library()
|
categories = load_category_library()
|
||||||
category_rng = _axis_rng(seed_config, "category", seed, row_number)
|
category_rng = _axis_rng(seed_config, "category", seed, row_number)
|
||||||
@@ -5881,6 +6094,7 @@ def _build_custom_row(
|
|||||||
expression_rng = _axis_rng(seed_config, "expression", seed, row_number)
|
expression_rng = _axis_rng(seed_config, "expression", seed, row_number)
|
||||||
composition_rng = _axis_rng(seed_config, "composition", seed, row_number)
|
composition_rng = _axis_rng(seed_config, "composition", seed, row_number)
|
||||||
parsed_hardcore_position_config = _parse_hardcore_position_config(hardcore_position_config)
|
parsed_hardcore_position_config = _parse_hardcore_position_config(hardcore_position_config)
|
||||||
|
parsed_location_config = _parse_location_config(location_config)
|
||||||
|
|
||||||
requested_women_count = women_count
|
requested_women_count = women_count
|
||||||
requested_men_count = men_count
|
requested_men_count = men_count
|
||||||
@@ -5993,7 +6207,14 @@ def _build_custom_row(
|
|||||||
)
|
)
|
||||||
cast_descriptor_text = _insta_of_prompt_cast_descriptors("; ".join(cast_descriptors))
|
cast_descriptor_text = _insta_of_prompt_cast_descriptors("; ".join(cast_descriptors))
|
||||||
|
|
||||||
scene_slug, scene = _choose_pair(scene_rng, _compatible_entries(_scene_pool(category, subcategory, item, subject_type), women_count, men_count))
|
scene_slug, scene = _choose_pair(
|
||||||
|
scene_rng,
|
||||||
|
_compatible_entries(
|
||||||
|
_scene_pool(category, subcategory, item, subject_type, parsed_location_config),
|
||||||
|
women_count,
|
||||||
|
men_count,
|
||||||
|
),
|
||||||
|
)
|
||||||
pose = str(_merged_field(category, subcategory, item, "pose", "") or context.get("fallback_pose") or _choose_text(
|
pose = str(_merged_field(category, subcategory, item, "pose", "") or context.get("fallback_pose") or _choose_text(
|
||||||
pose_rng, _compatible_entries(_pose_pool(category, subcategory, item, subject_type, poses), women_count, men_count)
|
pose_rng, _compatible_entries(_pose_pool(category, subcategory, item, subject_type, poses), women_count, men_count)
|
||||||
))
|
))
|
||||||
@@ -6146,6 +6367,7 @@ def _build_custom_row(
|
|||||||
"custom_item": item_name,
|
"custom_item": item_name,
|
||||||
"item_axis_values": item_axis_values,
|
"item_axis_values": item_axis_values,
|
||||||
"scene_text": scene,
|
"scene_text": scene,
|
||||||
|
"location_config": parsed_location_config if _location_config_active(parsed_location_config) else {},
|
||||||
"pose": pose,
|
"pose": pose,
|
||||||
"seed_config": seed_config,
|
"seed_config": seed_config,
|
||||||
"hardcore_position_config": (
|
"hardcore_position_config": (
|
||||||
@@ -6218,6 +6440,7 @@ def build_prompt(
|
|||||||
expression_enabled: bool = True,
|
expression_enabled: bool = True,
|
||||||
expression_phase: str = "",
|
expression_phase: str = "",
|
||||||
hardcore_position_config: str | dict[str, Any] | None = None,
|
hardcore_position_config: str | dict[str, Any] | None = None,
|
||||||
|
location_config: str | dict[str, Any] | None = None,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
apply_pool_extensions()
|
apply_pool_extensions()
|
||||||
row_number = max(1, int(row_number))
|
row_number = max(1, int(row_number))
|
||||||
@@ -6228,6 +6451,7 @@ def build_prompt(
|
|||||||
minimal_ratio = _ratio_or_none(minimal_clothing_ratio)
|
minimal_ratio = _ratio_or_none(minimal_clothing_ratio)
|
||||||
pose_ratio = _ratio_or_none(standard_pose_ratio)
|
pose_ratio = _ratio_or_none(standard_pose_ratio)
|
||||||
parsed_seed_config = _parse_seed_config(seed_config)
|
parsed_seed_config = _parse_seed_config(seed_config)
|
||||||
|
parsed_location_config = _parse_location_config(location_config)
|
||||||
content_rng = _axis_rng(parsed_seed_config, "content", seed, row_number)
|
content_rng = _axis_rng(parsed_seed_config, "content", seed, row_number)
|
||||||
pose_axis_rng = _axis_rng(parsed_seed_config, "pose", seed, row_number)
|
pose_axis_rng = _axis_rng(parsed_seed_config, "pose", seed, row_number)
|
||||||
person_rng = _axis_rng(parsed_seed_config, "person", seed, row_number)
|
person_rng = _axis_rng(parsed_seed_config, "person", seed, row_number)
|
||||||
@@ -6300,8 +6524,17 @@ def build_prompt(
|
|||||||
character_cast,
|
character_cast,
|
||||||
expression_phase,
|
expression_phase,
|
||||||
hardcore_position_config,
|
hardcore_position_config,
|
||||||
|
parsed_location_config,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if row.get("source") == "built_in_generator":
|
||||||
|
row = _apply_location_config_to_legacy_row(
|
||||||
|
row,
|
||||||
|
parsed_location_config,
|
||||||
|
parsed_seed_config,
|
||||||
|
seed,
|
||||||
|
row_number,
|
||||||
|
)
|
||||||
if not expression_enabled:
|
if not expression_enabled:
|
||||||
row = _disable_row_expression(row, "disabled")
|
row = _disable_row_expression(row, "disabled")
|
||||||
if extra_positive.strip():
|
if extra_positive.strip():
|
||||||
@@ -6329,6 +6562,7 @@ def build_prompt_from_configs(
|
|||||||
character_profile: str | dict[str, Any] | None = "",
|
character_profile: str | dict[str, Any] | None = "",
|
||||||
character_cast: str | dict[str, Any] | list[Any] | None = "",
|
character_cast: str | dict[str, Any] | list[Any] | None = "",
|
||||||
hardcore_position_config: str | dict[str, Any] | None = "",
|
hardcore_position_config: str | dict[str, Any] | None = "",
|
||||||
|
location_config: str | dict[str, Any] | None = "",
|
||||||
extra_positive: str = "",
|
extra_positive: str = "",
|
||||||
extra_negative: str = "",
|
extra_negative: str = "",
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
@@ -6364,6 +6598,7 @@ def build_prompt_from_configs(
|
|||||||
character_profile=character_profile or "",
|
character_profile=character_profile or "",
|
||||||
character_cast=character_cast or "",
|
character_cast=character_cast or "",
|
||||||
hardcore_position_config=hardcore_position_config or "",
|
hardcore_position_config=hardcore_position_config or "",
|
||||||
|
location_config=location_config or "",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -7146,6 +7381,7 @@ def build_insta_of_pair(
|
|||||||
character_profile: str | dict[str, Any] | None = "",
|
character_profile: str | dict[str, Any] | None = "",
|
||||||
character_cast: str | dict[str, Any] | list[Any] | None = "",
|
character_cast: str | dict[str, Any] | list[Any] | None = "",
|
||||||
hardcore_position_config: str | dict[str, Any] | None = "",
|
hardcore_position_config: str | dict[str, Any] | None = "",
|
||||||
|
location_config: str | dict[str, Any] | None = "",
|
||||||
extra_positive: str = "",
|
extra_positive: str = "",
|
||||||
extra_negative: str = "",
|
extra_negative: str = "",
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
@@ -7223,6 +7459,7 @@ def build_insta_of_pair(
|
|||||||
expression_intensity=soft_expression_intensity,
|
expression_intensity=soft_expression_intensity,
|
||||||
character_profile="" if primary_slot else character_profile or "",
|
character_profile="" if primary_slot else character_profile or "",
|
||||||
character_cast="",
|
character_cast="",
|
||||||
|
location_config=location_config or "",
|
||||||
)
|
)
|
||||||
soft_row["expression_intensity_source"] = soft_expression_intensity_source
|
soft_row["expression_intensity_source"] = soft_expression_intensity_source
|
||||||
if primary_slot_context:
|
if primary_slot_context:
|
||||||
@@ -7280,6 +7517,7 @@ def build_insta_of_pair(
|
|||||||
character_cast=character_cast or "",
|
character_cast=character_cast or "",
|
||||||
expression_phase="hardcore",
|
expression_phase="hardcore",
|
||||||
hardcore_position_config=hardcore_position_config or "",
|
hardcore_position_config=hardcore_position_config or "",
|
||||||
|
location_config=location_config or "",
|
||||||
)
|
)
|
||||||
hard_row["hardcore_detail_density"] = options["hardcore_detail_density"]
|
hard_row["hardcore_detail_density"] = options["hardcore_detail_density"]
|
||||||
hard_row["pov_character_labels"] = pov_character_labels
|
hard_row["pov_character_labels"] = pov_character_labels
|
||||||
|
|||||||
Reference in New Issue
Block a user