basyx commited on
Commit
43393d9
·
verified ·
1 Parent(s): 5bb419d

Update utils/srt.py

Browse files
Files changed (1) hide show
  1. utils/srt.py +188 -5
utils/srt.py CHANGED
@@ -1,5 +1,188 @@
1
- class Subtitle:
2
- def __init__(self, start, end, text):
3
- self.start = start
4
- self.end = end
5
- self.text = text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List, Dict, Any, Union
2
+
3
+
4
+ # =====================================================
5
+ # PUBLIC API (USED BY MAIN.PY)
6
+ # =====================================================
7
+
8
+ def generate_srt(data: List[Dict[str, Any]]) -> str:
9
+ """
10
+ Universal SRT generator for:
11
+ - Whisper word output (V1–V7)
12
+ - Highlight segments (start/end grouped words)
13
+ - Mixed/partial structures
14
+
15
+ Expected input formats:
16
+ 1. Word-level:
17
+ {"text": "...", "start": float, "end": float}
18
+
19
+ 2. Segment-level:
20
+ [{"start": float, "end": float, "text": "..."}]
21
+
22
+ Returns:
23
+ SRT formatted string
24
+ """
25
+
26
+ if not data:
27
+ return ""
28
+
29
+ normalized = _normalize_input(data)
30
+ return _build_srt(normalized)
31
+
32
+
33
+ # =====================================================
34
+ # NORMALIZATION LAYER (CRITICAL FOR V1–V7 COMPATIBILITY)
35
+ # =====================================================
36
+
37
+ def _normalize_input(data: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
38
+ """
39
+ Converts any supported structure into unified subtitle blocks
40
+ """
41
+
42
+ normalized = []
43
+
44
+ # CASE 1: Already segment-based
45
+ if isinstance(data[0], dict) and "start" in data[0] and "end" in data[0] and "text" in data[0]:
46
+ for item in data:
47
+ normalized.append({
48
+ "start": float(item.get("start", 0)),
49
+ "end": float(item.get("end", 0)),
50
+ "text": str(item.get("text", "")).strip()
51
+ })
52
+ return normalized
53
+
54
+ # CASE 2: Whisper word-level output
55
+ buffer = []
56
+ current_start = None
57
+
58
+ for w in data:
59
+
60
+ if not isinstance(w, dict):
61
+ continue
62
+
63
+ text = str(w.get("text", "")).strip()
64
+ start = w.get("start", None)
65
+ end = w.get("end", None)
66
+
67
+ if start is None or end is None:
68
+ continue
69
+
70
+ if current_start is None:
71
+ current_start = start
72
+
73
+ buffer.append(text)
74
+
75
+ # Chunking strategy: group every ~8–12 words
76
+ if len(buffer) >= 10:
77
+
78
+ normalized.append({
79
+ "start": current_start,
80
+ "end": end,
81
+ "text": " ".join(buffer)
82
+ })
83
+
84
+ buffer = []
85
+ current_start = None
86
+
87
+ # flush remaining buffer
88
+ if buffer:
89
+ normalized.append({
90
+ "start": current_start or 0,
91
+ "end": data[-1].get("end", 0),
92
+ "text": " ".join(buffer)
93
+ })
94
+
95
+ return normalized
96
+
97
+
98
+ # =====================================================
99
+ # SRT BUILDER
100
+ # =====================================================
101
+
102
+ def _build_srt(items: List[Dict[str, Any]]) -> str:
103
+ """
104
+ Converts normalized subtitle blocks → SRT format
105
+ """
106
+
107
+ output = []
108
+ index = 1
109
+
110
+ for item in items:
111
+
112
+ start = _format_time(item["start"])
113
+ end = _format_time(item["end"])
114
+ text = _clean_text(item["text"])
115
+
116
+ if not text:
117
+ continue
118
+
119
+ output.append(f"{index}")
120
+ output.append(f"{start} --> {end}")
121
+ output.append(f"{text}")
122
+ output.append("") # blank line separator
123
+
124
+ index += 1
125
+
126
+ return "\n".join(output).strip()
127
+
128
+
129
+ # =====================================================
130
+ # TIME FORMATTER
131
+ # =====================================================
132
+
133
+ def _format_time(seconds: Union[int, float]) -> str:
134
+ """
135
+ Converts seconds → SRT timestamp format
136
+ HH:MM:SS,mmm
137
+ """
138
+
139
+ try:
140
+ seconds = float(seconds)
141
+ except:
142
+ seconds = 0.0
143
+
144
+ hrs = int(seconds // 3600)
145
+ mins = int((seconds % 3600) // 60)
146
+ secs = int(seconds % 60)
147
+ ms = int((seconds - int(seconds)) * 1000)
148
+
149
+ return f"{hrs:02}:{mins:02}:{secs:02},{ms:03}"
150
+
151
+
152
+ # =====================================================
153
+ # TEXT CLEANER (IMPORTANT FOR VIDEO RENDERING STABILITY)
154
+ # =====================================================
155
+
156
+ def _clean_text(text: str) -> str:
157
+ """
158
+ Sanitizes subtitle text for rendering engines
159
+ """
160
+
161
+ if not text:
162
+ return ""
163
+
164
+ text = text.replace("\n", " ")
165
+ text = text.replace("\r", " ")
166
+
167
+ # remove excessive spacing
168
+ text = " ".join(text.split())
169
+
170
+ return text.strip()
171
+
172
+
173
+ # =====================================================
174
+ # OPTIONAL DEBUG HELPER (SAFE IN PRODUCTION)
175
+ # =====================================================
176
+
177
+ def debug_srt(data: List[Dict[str, Any]]) -> dict:
178
+ """
179
+ Returns structured preview for debugging pipelines
180
+ """
181
+
182
+ normalized = _normalize_input(data)
183
+
184
+ return {
185
+ "blocks": len(normalized),
186
+ "sample": normalized[:3],
187
+ "duration": normalized[-1]["end"] if normalized else 0
188
+ }