Update pipeline/postprocess.py
Browse files- pipeline/postprocess.py +10 -28
pipeline/postprocess.py
CHANGED
|
@@ -1,10 +1,10 @@
|
|
| 1 |
-
# ─── Post-Processing Engine
|
| 2 |
|
| 3 |
-
MIN_DISPLAY
|
| 4 |
-
MAX_DISPLAY
|
| 5 |
-
MERGE_MAX_CHARS = 72
|
| 6 |
-
SPLIT_MAX_CHARS = 60
|
| 7 |
-
MAX_GAP
|
| 8 |
|
| 9 |
|
| 10 |
def format_timestamp(seconds: float) -> str:
|
|
@@ -16,7 +16,6 @@ def format_timestamp(seconds: float) -> str:
|
|
| 16 |
|
| 17 |
|
| 18 |
def merge_segments(segments: list) -> list:
|
| 19 |
-
"""ادغام segmentهای کوتاه به جملات کامل"""
|
| 20 |
if not segments:
|
| 21 |
return []
|
| 22 |
|
|
@@ -38,7 +37,7 @@ def merge_segments(segments: list) -> list:
|
|
| 38 |
combined_duration = seg["end"] - current["start"]
|
| 39 |
ends_sentence = current["text"].rstrip().endswith((".", "!", "?"))
|
| 40 |
|
| 41 |
-
|
| 42 |
current_words = current.get("words", [])
|
| 43 |
is_short = len(current_words) <= 2 or (current["end"] - current["start"]) < 0.8
|
| 44 |
|
|
@@ -46,10 +45,10 @@ def merge_segments(segments: list) -> list:
|
|
| 46 |
not ends_sentence
|
| 47 |
and combined_duration <= MAX_DISPLAY
|
| 48 |
and (
|
| 49 |
-
is_short
|
| 50 |
or (len(combined_text) <= MERGE_MAX_CHARS and gap <= MAX_GAP)
|
| 51 |
)
|
| 52 |
-
)
|
| 53 |
|
| 54 |
if should_merge:
|
| 55 |
current["text"] = combined_text
|
|
@@ -71,7 +70,6 @@ def merge_segments(segments: list) -> list:
|
|
| 71 |
|
| 72 |
|
| 73 |
def remove_duplicates(segments: list) -> list:
|
| 74 |
-
"""حذف subtitleهای تکراری متوالی"""
|
| 75 |
if not segments:
|
| 76 |
return []
|
| 77 |
result = [segments[0]]
|
|
@@ -82,7 +80,6 @@ def remove_duplicates(segments: list) -> list:
|
|
| 82 |
|
| 83 |
|
| 84 |
def fix_overlaps(segments: list) -> list:
|
| 85 |
-
"""رفع timestampهای overlapping"""
|
| 86 |
fixed = []
|
| 87 |
for seg in segments:
|
| 88 |
seg = dict(seg)
|
|
@@ -95,35 +92,29 @@ def fix_overlaps(segments: list) -> list:
|
|
| 95 |
|
| 96 |
|
| 97 |
def apply_min_display(segments: list) -> list:
|
| 98 |
-
"""اعمال حداقل زمان نمایش"""
|
| 99 |
result = []
|
| 100 |
for i, seg in enumerate(segments):
|
| 101 |
seg = dict(seg)
|
| 102 |
duration = seg["end"] - seg["start"]
|
| 103 |
-
|
| 104 |
if duration < MIN_DISPLAY:
|
| 105 |
if i + 1 < len(segments):
|
| 106 |
next_start = segments[i + 1]["start"]
|
| 107 |
max_end = next_start - 0.080
|
| 108 |
else:
|
| 109 |
max_end = seg["end"] + MAX_DISPLAY
|
| 110 |
-
|
| 111 |
new_end = seg["start"] + MIN_DISPLAY
|
| 112 |
seg["end"] = min(new_end, max(max_end, seg["end"]))
|
| 113 |
-
|
| 114 |
result.append(seg)
|
| 115 |
return result
|
| 116 |
|
| 117 |
|
| 118 |
def split_long_segment(seg: dict) -> list:
|
| 119 |
-
"""شکستن segmentهای خیلی طولانی"""
|
| 120 |
words = seg.get("words", [])
|
| 121 |
text = seg["text"].strip()
|
| 122 |
|
| 123 |
if len(text) <= SPLIT_MAX_CHARS or len(words) < 3:
|
| 124 |
return [seg]
|
| 125 |
|
| 126 |
-
# پیدا کردن بهترین نقطه split (بزرگترین gap)
|
| 127 |
best_idx = len(words) // 2
|
| 128 |
best_gap = -1
|
| 129 |
for i in range(1, len(words)):
|
|
@@ -175,24 +166,15 @@ def process(segments: list) -> str:
|
|
| 175 |
if not segments:
|
| 176 |
return ""
|
| 177 |
|
| 178 |
-
# ۱ — ادغام segmentهای کوتاه
|
| 179 |
merged = merge_segments(segments)
|
| 180 |
-
|
| 181 |
-
# ۲ — حذف duplicate
|
| 182 |
merged = remove_duplicates(merged)
|
|
|
|
| 183 |
|
| 184 |
-
# ۳ — رفع overlap
|
| 185 |
-
fixed = fix_overlaps(merged)
|
| 186 |
-
|
| 187 |
-
# ۴ — شکستن segmentهای خیلی طولانی
|
| 188 |
split = []
|
| 189 |
for seg in fixed:
|
| 190 |
split.extend(split_long_segment(seg))
|
| 191 |
|
| 192 |
-
# ۵ — رفع overlap بعد از split
|
| 193 |
split = fix_overlaps(split)
|
| 194 |
-
|
| 195 |
-
# ۶ — اعمال حداقل زمان نمایش
|
| 196 |
final = apply_min_display(split)
|
| 197 |
|
| 198 |
return build_srt(final)
|
|
|
|
| 1 |
+
# ─── Post-Processing Engine v6 ─────────────────────────────────────────────────
|
| 2 |
|
| 3 |
+
MIN_DISPLAY = 2.5
|
| 4 |
+
MAX_DISPLAY = 7.0
|
| 5 |
+
MERGE_MAX_CHARS = 72
|
| 6 |
+
SPLIT_MAX_CHARS = 60
|
| 7 |
+
MAX_GAP = 1.5
|
| 8 |
|
| 9 |
|
| 10 |
def format_timestamp(seconds: float) -> str:
|
|
|
|
| 16 |
|
| 17 |
|
| 18 |
def merge_segments(segments: list) -> list:
|
|
|
|
| 19 |
if not segments:
|
| 20 |
return []
|
| 21 |
|
|
|
|
| 37 |
combined_duration = seg["end"] - current["start"]
|
| 38 |
ends_sentence = current["text"].rstrip().endswith((".", "!", "?"))
|
| 39 |
|
| 40 |
+
# segment کوتاه (کمتر از ۳ کلمه یا کمتر از ۰.۸ ثانیه) → همیشه merge کن
|
| 41 |
current_words = current.get("words", [])
|
| 42 |
is_short = len(current_words) <= 2 or (current["end"] - current["start"]) < 0.8
|
| 43 |
|
|
|
|
| 45 |
not ends_sentence
|
| 46 |
and combined_duration <= MAX_DISPLAY
|
| 47 |
and (
|
| 48 |
+
is_short
|
| 49 |
or (len(combined_text) <= MERGE_MAX_CHARS and gap <= MAX_GAP)
|
| 50 |
)
|
| 51 |
+
)
|
| 52 |
|
| 53 |
if should_merge:
|
| 54 |
current["text"] = combined_text
|
|
|
|
| 70 |
|
| 71 |
|
| 72 |
def remove_duplicates(segments: list) -> list:
|
|
|
|
| 73 |
if not segments:
|
| 74 |
return []
|
| 75 |
result = [segments[0]]
|
|
|
|
| 80 |
|
| 81 |
|
| 82 |
def fix_overlaps(segments: list) -> list:
|
|
|
|
| 83 |
fixed = []
|
| 84 |
for seg in segments:
|
| 85 |
seg = dict(seg)
|
|
|
|
| 92 |
|
| 93 |
|
| 94 |
def apply_min_display(segments: list) -> list:
|
|
|
|
| 95 |
result = []
|
| 96 |
for i, seg in enumerate(segments):
|
| 97 |
seg = dict(seg)
|
| 98 |
duration = seg["end"] - seg["start"]
|
|
|
|
| 99 |
if duration < MIN_DISPLAY:
|
| 100 |
if i + 1 < len(segments):
|
| 101 |
next_start = segments[i + 1]["start"]
|
| 102 |
max_end = next_start - 0.080
|
| 103 |
else:
|
| 104 |
max_end = seg["end"] + MAX_DISPLAY
|
|
|
|
| 105 |
new_end = seg["start"] + MIN_DISPLAY
|
| 106 |
seg["end"] = min(new_end, max(max_end, seg["end"]))
|
|
|
|
| 107 |
result.append(seg)
|
| 108 |
return result
|
| 109 |
|
| 110 |
|
| 111 |
def split_long_segment(seg: dict) -> list:
|
|
|
|
| 112 |
words = seg.get("words", [])
|
| 113 |
text = seg["text"].strip()
|
| 114 |
|
| 115 |
if len(text) <= SPLIT_MAX_CHARS or len(words) < 3:
|
| 116 |
return [seg]
|
| 117 |
|
|
|
|
| 118 |
best_idx = len(words) // 2
|
| 119 |
best_gap = -1
|
| 120 |
for i in range(1, len(words)):
|
|
|
|
| 166 |
if not segments:
|
| 167 |
return ""
|
| 168 |
|
|
|
|
| 169 |
merged = merge_segments(segments)
|
|
|
|
|
|
|
| 170 |
merged = remove_duplicates(merged)
|
| 171 |
+
fixed = fix_overlaps(merged)
|
| 172 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 173 |
split = []
|
| 174 |
for seg in fixed:
|
| 175 |
split.extend(split_long_segment(seg))
|
| 176 |
|
|
|
|
| 177 |
split = fix_overlaps(split)
|
|
|
|
|
|
|
| 178 |
final = apply_min_display(split)
|
| 179 |
|
| 180 |
return build_srt(final)
|