Honor metadata in hardcore filters
This commit is contained in:
@@ -240,6 +240,29 @@ def _entry_text(item: Any) -> str:
|
||||
return str(item).strip()
|
||||
|
||||
|
||||
def _metadata_tokens(item: Any, keys: tuple[str, ...]) -> set[str]:
|
||||
if not isinstance(item, dict):
|
||||
return set()
|
||||
tokens: set[str] = set()
|
||||
for key in keys:
|
||||
for value in _list_from(item.get(key)):
|
||||
token = re.sub(r"[^a-z0-9]+", "_", str(value or "").strip().lower()).strip("_")
|
||||
if token and token != "any":
|
||||
tokens.add(token)
|
||||
return tokens
|
||||
|
||||
|
||||
def _entry_position_keys(item: Any) -> list[str]:
|
||||
if not isinstance(item, dict):
|
||||
return []
|
||||
values: list[Any] = []
|
||||
if item.get("position_keys") is not None:
|
||||
values.extend(_list_from(item.get("position_keys")))
|
||||
if item.get("position_key") is not None:
|
||||
values.append(item.get("position_key"))
|
||||
return normalize_hardcore_position_values(values)
|
||||
|
||||
|
||||
def hardcore_position_family_choices() -> list[str]:
|
||||
return list(HARDCORE_POSITION_FAMILY_CHOICES)
|
||||
|
||||
@@ -633,10 +656,42 @@ def hardcore_text_blocked_by_action(text: str, axis_name: str, config: dict[str,
|
||||
return False
|
||||
|
||||
|
||||
def hardcore_entry_blocked_by_action(entry: Any, axis_name: str, config: dict[str, Any]) -> bool:
|
||||
action_tokens = _metadata_tokens(entry, ("action_family", "action_type"))
|
||||
family_tokens = _metadata_tokens(entry, ("position_family", "family"))
|
||||
position_keys = set(_entry_position_keys(entry))
|
||||
route_tokens = action_tokens | family_tokens
|
||||
|
||||
if not config.get("allow_toys", True) and action_tokens & {"toy", "toy_double"}:
|
||||
return True
|
||||
if not config.get("allow_double", True) and (action_tokens & {"double", "toy_double"} or "front_back" in position_keys):
|
||||
return True
|
||||
if not config.get("allow_anal", True) and "anal" in route_tokens:
|
||||
return True
|
||||
if not config.get("allow_oral", True) and "oral" in route_tokens:
|
||||
return True
|
||||
if not config.get("allow_outercourse", True) and "outercourse" in route_tokens:
|
||||
return True
|
||||
if not config.get("allow_penetration", True) and route_tokens & {"penetration", "penetrative", "toy_double", "anal"}:
|
||||
return True
|
||||
if not config.get("allow_foreplay", True) and "foreplay" in route_tokens:
|
||||
return True
|
||||
if not config.get("allow_interaction", True) and "interaction" in route_tokens:
|
||||
return True
|
||||
if not config.get("allow_manual", True) and "manual" in route_tokens:
|
||||
return True
|
||||
if not config.get("allow_climax", True) and "climax" in route_tokens:
|
||||
return True
|
||||
return hardcore_text_blocked_by_action(_entry_text(entry), axis_name, config)
|
||||
|
||||
|
||||
def hardcore_position_entry_matches(entry: Any, config: dict[str, Any]) -> bool:
|
||||
positions = config.get("positions") or []
|
||||
if not positions:
|
||||
return True
|
||||
metadata_keys = _entry_position_keys(entry)
|
||||
if metadata_keys:
|
||||
return bool(set(metadata_keys) & set(positions))
|
||||
text = _entry_text(entry).lower()
|
||||
for position in positions:
|
||||
if any(term in text for term in HARDCORE_POSITION_KEY_MATCHES.get(position, ())):
|
||||
@@ -648,12 +703,16 @@ def hardcore_position_entry_conflicts(entry: Any, config: dict[str, Any]) -> boo
|
||||
selected = set(config.get("positions") or [])
|
||||
if not selected:
|
||||
return False
|
||||
text = _entry_text(entry).lower()
|
||||
matched = {
|
||||
position
|
||||
for position, terms in HARDCORE_POSITION_KEY_MATCHES.items()
|
||||
if any(term in text for term in terms)
|
||||
}
|
||||
metadata_keys = _entry_position_keys(entry)
|
||||
if metadata_keys:
|
||||
matched = set(metadata_keys)
|
||||
else:
|
||||
text = _entry_text(entry).lower()
|
||||
matched = {
|
||||
position
|
||||
for position, terms in HARDCORE_POSITION_KEY_MATCHES.items()
|
||||
if any(term in text for term in terms)
|
||||
}
|
||||
return bool(matched) and not bool(matched & selected)
|
||||
|
||||
|
||||
@@ -678,7 +737,7 @@ def filter_hardcore_axis(axis_name: str, values: list[Any], config: dict[str, An
|
||||
filtered = [
|
||||
value
|
||||
for value in values
|
||||
if not hardcore_text_blocked_by_action(_entry_text(value), axis_name, config)
|
||||
if not hardcore_entry_blocked_by_action(value, axis_name, config)
|
||||
and not (axis_name not in HARDCORE_POSITION_AXIS_KEYS and hardcore_position_entry_conflicts(value, config))
|
||||
and (axis_name not in HARDCORE_POSITION_AXIS_KEYS or hardcore_position_entry_matches(value, config))
|
||||
]
|
||||
@@ -692,8 +751,10 @@ def filter_hardcore_templates(templates: list[Any], config: dict[str, Any]) -> l
|
||||
for template in templates:
|
||||
text = _entry_text(template)
|
||||
fields = {key for _, key, _, _ in Formatter().parse(text) if key}
|
||||
blocked = hardcore_position_template_required(config) and not bool(fields & HARDCORE_POSITION_AXIS_KEYS)
|
||||
blocked = blocked or any(hardcore_text_blocked_by_action(text, field, config) for field in fields | {""})
|
||||
has_position_route = bool(fields & HARDCORE_POSITION_AXIS_KEYS) or bool(_entry_position_keys(template))
|
||||
blocked = hardcore_position_template_required(config) and not has_position_route
|
||||
blocked = blocked or hardcore_entry_blocked_by_action(template, "", config)
|
||||
blocked = blocked or any(hardcore_text_blocked_by_action(text, field, config) for field in fields)
|
||||
if not blocked:
|
||||
filtered.append(template)
|
||||
return filtered or templates
|
||||
|
||||
Reference in New Issue
Block a user