Ctrl K
ComunidadGuías…

backend/health_fixes.py

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""Subtitle auto-fix functions.

Extracted from health_checker.py. Each fix_* function mutates a
pysubs2.SSAFile in-place and returns the count of repairs made.
FIX_MAP wires detection-rule names to their fixer — apply_fixes() in
health_checker iterates it by name.
"""


def fix_duplicate_lines(subs) -> int:
    """Remove exact duplicate events. Returns count removed."""
    seen = set()
    to_remove = []
    for idx, event in enumerate(subs.events):
        if event.is_comment:
            continue
        key = (event.start, event.end, event.text, event.style)
        if key in seen:
            to_remove.append(idx)
        else:
            seen.add(key)
    # Remove in reverse order to preserve indices
    for idx in reversed(to_remove):
        del subs.events[idx]
    return len(to_remove)


def fix_timing_overlaps(subs) -> int:
    """Trim previous event end to current event start for same-style overlaps.

    Returns count of overlaps fixed.
    """
    groups = {}
    for idx, event in enumerate(subs.events):
        if event.is_comment:
            continue
        key = (event.style, getattr(event, "layer", 0))
        if key not in groups:
            groups[key] = []
        groups[key].append((idx, event))

    fixed = 0
    for group_key, events in groups.items():
        sorted_events = sorted(events, key=lambda x: x[1].start)
        for i in range(1, len(sorted_events)):
            prev_idx, prev = sorted_events[i - 1]
            curr_idx, curr = sorted_events[i]
            if curr.start < prev.end:
                prev.end = curr.start
                fixed += 1
    return fixed


def fix_missing_styles(subs) -> int:
    """Change undefined style references to 'Default'. Returns count fixed."""
    if not hasattr(subs, "styles") or not subs.styles:
        return 0

    defined_styles = set(subs.styles.keys())
    fixed = 0
    for event in subs.events:
        if event.is_comment:
            continue
        if event.style not in defined_styles:
            event.style = "Default"
            fixed += 1
    return fixed


def fix_empty_events(subs) -> int:
    """Remove events with empty plaintext. Returns count removed."""
    to_remove = []
    for idx, event in enumerate(subs.events):
        if event.is_comment:
            continue
        if not event.plaintext.strip():
            to_remove.append(idx)
    for idx in reversed(to_remove):
        del subs.events[idx]
    return len(to_remove)


def fix_negative_timing(subs) -> int:
    """Swap start and end when end < start. Returns count fixed."""
    fixed = 0
    for event in subs.events:
        if event.is_comment:
            continue
        if event.end < event.start:
            event.start, event.end = event.end, event.start
            fixed += 1
    return fixed


def fix_zero_duration(subs) -> int:
    """Remove zero-duration events. Returns count removed."""
    to_remove = []
    for idx, event in enumerate(subs.events):
        if event.is_comment:
            continue
        if event.start == event.end:
            to_remove.append(idx)
    for idx in reversed(to_remove):
        del subs.events[idx]
    return len(to_remove)


# Map check names to fix functions
FIX_MAP = {
    "duplicate_lines": fix_duplicate_lines,
    "timing_overlaps": fix_timing_overlaps,
    "missing_styles": fix_missing_styles,
    "empty_events": fix_empty_events,
    "negative_timing": fix_negative_timing,
    "zero_duration": fix_zero_duration,
}