AliJabbarpour commited on
Commit
650264a
·
verified ·
1 Parent(s): 54d6dd3

Update pipeline/postprocess.py

Browse files
Files changed (1) hide show
  1. pipeline/postprocess.py +35 -37
pipeline/postprocess.py CHANGED
@@ -1,10 +1,11 @@
1
- # ─── Post-Processing Engine v7 ─────────────────────────────────────────────────
2
 
3
- MIN_DISPLAY = 1.5 # حداقل زمان نمایش (ثانیه)
4
- MAX_DISPLAY = 7.0 # حداکثر زمان نمایش
5
- MERGE_MAX_CHARS = 72 # حداکثر کاراکتر برای merge
6
- MAX_GAP = 1.5 # حداکثر فاصله بین دو segment برای merge
7
- MAX_LINE_CHARS = 70 # threshold برای split کردن به دو subtitle جداگانه
 
8
 
9
 
10
  def format_timestamp(seconds: float) -> str:
@@ -16,7 +17,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
 
@@ -106,44 +106,44 @@ def apply_min_display(segments: list) -> list:
106
  result.append(seg)
107
  return result
108
 
 
109
  def split_long_segment(seg: dict) -> list:
110
  """
111
- اگه segment بیشتر از MAX_LINE_CHARS کاراکتر داشت،
112
- تبدیل میشه به دو subtitle جداگانه با timestamp مجزا.
113
  """
114
  words = seg.get("words", [])
115
  text = seg["text"].strip()
116
 
117
- # اگه کوتاهه یا کلمه کافی نداره، نشکن
118
  if len(text) <= MAX_LINE_CHARS or len(words) < 4:
119
  return [seg]
120
 
121
- # پیدا کردن بهترین نقطه split
122
- mid = len(words) // 2
123
- best_idx = mid
124
- best_gap = -1
125
-
126
- # اول دنبال بزرگ‌ترین gap بگرد
127
- for i in range(1, len(words)):
128
- gap = words[i]["start"] - words[i - 1]["end"]
129
- if gap > best_gap:
130
- best_gap = gap
131
- best_idx = i
132
-
133
- # اگه gap خیلی کوچیکه (زیر ۵۰ms)، از وسط بشکن
134
- if best_gap < 0.050:
135
- best_idx = mid
 
136
 
137
- words1 = words[:best_idx]
138
- words2 = words[best_idx:]
139
 
140
- if not words1 or not words2:
141
  return [seg]
142
 
143
  start2 = words2[0]["start"]
144
  end1 = max(start2 - 0.080, words1[-1]["end"])
145
 
146
- # safety: حداقل MIN_DISPLAY برای هر بخش
147
  if end1 - words1[0]["start"] < MIN_DISPLAY:
148
  end1 = words1[0]["start"] + MIN_DISPLAY
149
  if end1 >= start2:
@@ -162,20 +162,19 @@ def split_long_segment(seg: dict) -> list:
162
  "words": words2,
163
  }
164
 
165
- # recursive — اگه هنوز طولانیه، دوباره بشکن
166
  return split_long_segment(seg1) + split_long_segment(seg2)
167
 
168
 
169
- def wrap_text(text: str, max_chars: int = 42) -> str:
170
- """اگه متن بیشتر از max_chars بود، به دو خط تقسیم کن"""
171
- if len(text) <= max_chars:
172
  return text
173
 
174
- words = text.split()
175
  best_break = -1
176
  best_diff = float('inf')
177
-
178
  current_len = 0
 
179
  for i, word in enumerate(words):
180
  current_len += len(word) + (1 if i > 0 else 0)
181
  diff = abs(current_len - len(text) / 2)
@@ -221,5 +220,4 @@ def process(segments: list) -> str:
221
  split = fix_overlaps(split)
222
  final = apply_min_display(split)
223
 
224
- return build_srt(final)
225
-
 
1
+ # ─── Post-Processing Engine v8 ─────────────────────────────────────────────────
2
 
3
+ MIN_DISPLAY = 1.5
4
+ MAX_DISPLAY = 7.0
5
+ MERGE_MAX_CHARS = 72
6
+ MAX_GAP = 1.5
7
+ MAX_LINE_CHARS = 70 # threshold برای split به دو subtitle جداگانه
8
+ WRAP_CHARS = 42 # استاندارد Netflix برای wrap به دو خط
9
 
10
 
11
  def format_timestamp(seconds: float) -> str:
 
17
 
18
 
19
  def merge_segments(segments: list) -> list:
 
20
  if not segments:
21
  return []
22
 
 
106
  result.append(seg)
107
  return result
108
 
109
+
110
  def split_long_segment(seg: dict) -> list:
111
  """
112
+ فقط اگه punctuation نزدیک وسط جمله بود، به دو subtitle جداگانه بشکن.
113
+ اگه نقطه مناسبی نبود، نشکن — wrap_text در build_srt دو خطش می‌کنه.
114
  """
115
  words = seg.get("words", [])
116
  text = seg["text"].strip()
117
 
 
118
  if len(text) <= MAX_LINE_CHARS or len(words) < 4:
119
  return [seg]
120
 
121
+ mid = len(words) // 2
122
+ punct_chars = {'.', ',', '!', '?', ';', ':'}
123
+ best_punct = -1
124
+ best_dist = float('inf')
125
+
126
+ for i in range(1, len(words) - 1):
127
+ word = words[i]["word"].strip()
128
+ if word and word[-1] in punct_chars:
129
+ dist = abs(i - mid)
130
+ if dist < best_dist:
131
+ best_dist = dist
132
+ best_punct = i + 1
133
+
134
+ # اگه punctuation نزدیک وسط نبود → نشکن، wrap_text کافیه
135
+ if best_punct == -1 or best_dist > len(words) // 3:
136
+ return [seg]
137
 
138
+ words1 = words[:best_punct]
139
+ words2 = words[best_punct:]
140
 
141
+ if len(words1) < 3 or len(words2) < 3:
142
  return [seg]
143
 
144
  start2 = words2[0]["start"]
145
  end1 = max(start2 - 0.080, words1[-1]["end"])
146
 
 
147
  if end1 - words1[0]["start"] < MIN_DISPLAY:
148
  end1 = words1[0]["start"] + MIN_DISPLAY
149
  if end1 >= start2:
 
162
  "words": words2,
163
  }
164
 
 
165
  return split_long_segment(seg1) + split_long_segment(seg2)
166
 
167
 
168
+ def wrap_text(text: str) -> str:
169
+ """اگه متن بیشتر از WRAP_CHARS بود، به دو خط تقسیم کن"""
170
+ if len(text) <= WRAP_CHARS:
171
  return text
172
 
173
+ words = text.split()
174
  best_break = -1
175
  best_diff = float('inf')
 
176
  current_len = 0
177
+
178
  for i, word in enumerate(words):
179
  current_len += len(word) + (1 if i > 0 else 0)
180
  diff = abs(current_len - len(text) / 2)
 
220
  split = fix_overlaps(split)
221
  final = apply_min_display(split)
222
 
223
+ return build_srt(final)