Vishal Patil commited on
Commit
debc1ce
·
verified ·
1 Parent(s): b4b8d36

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +124 -20
app.py CHANGED
@@ -8,21 +8,107 @@ UPLOAD_DIR = Path(tempfile.gettempdir()) / "speech_annotation"
8
  UPLOAD_DIR.mkdir(parents=True, exist_ok=True)
9
  _last_segments = []
10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  def process_audio(audio_path, num_speakers):
12
  global _last_segments
13
  if audio_path is None:
14
- return "⚠️ Please upload an audio file first.", ""
15
  try:
16
  pipeline = get_pipeline()
17
  n = int(num_speakers) if num_speakers and int(num_speakers) > 0 else 0
18
  segments = pipeline.process(audio_path, num_speakers=n)
19
  except Exception as e:
20
- return f"❌ Error: {e}\n{traceback.format_exc()}", ""
21
  if not segments:
22
- return "⚠️ No speech detected.", ""
23
  _last_segments = segments
24
  unique = len(set(s.speaker for s in segments))
25
- return f"✅ Done — {len(segments)} segments, {unique} speaker(s) detected", to_abab_text(segments)
 
26
 
27
  def export_json():
28
  if not _last_segments: return None
@@ -36,25 +122,43 @@ def export_csv():
36
  to_csv(_last_segments, out)
37
  return out
38
 
39
- with gr.Blocks(title="Speech Annotation Pipeline") as demo:
40
- gr.Markdown("# 🎙️ Speech Annotation Pipeline")
41
- audio_input = gr.Audio(label="Upload Audio (.wav/.mp3/.flac)", type="filepath")
42
- num_speakers = gr.Slider(minimum=0, maximum=10, step=1, value=0,
43
- label="Number of speakers (0 = auto-detect)")
44
- run_btn = gr.Button("▶ Run Annotation", variant="primary")
45
- status_box = gr.Textbox(label="Status", value="Ready.", interactive=False)
46
- abab_box = gr.Textbox(label="Transcript (ABAB)", lines=15, value="",
47
- placeholder="Annotations will appear here...")
48
  with gr.Row():
49
- json_btn = gr.Button("Export JSON")
50
- csv_btn = gr.Button("Export CSV")
51
- json_file = gr.File(label="JSON")
52
- csv_file = gr.File(label="CSV")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
- run_btn.click(fn=process_audio, inputs=[audio_input, num_speakers],
55
- outputs=[status_box, abab_box])
 
56
  json_btn.click(fn=export_json, inputs=[], outputs=[json_file])
57
- csv_btn.click(fn=export_csv, inputs=[], outputs=[csv_file])
58
 
59
  if __name__ == "__main__":
60
  demo.launch(server_name="0.0.0.0", server_port=7860, show_error=True)
 
8
  UPLOAD_DIR.mkdir(parents=True, exist_ok=True)
9
  _last_segments = []
10
 
11
+ SPEAKER_COLORS = [
12
+ ("#4F46E5", "#EEF2FF"), # A - indigo
13
+ ("#059669", "#ECFDF5"), # B - green
14
+ ("#DC2626", "#FEF2F2"), # C - red
15
+ ("#D97706", "#FFFBEB"), # D - amber
16
+ ("#7C3AED", "#F5F3FF"), # E - purple
17
+ ("#0891B2", "#ECFEFF"), # F - cyan
18
+ ]
19
+
20
+ def make_conversation_html(segments):
21
+ if not segments:
22
+ return ""
23
+ speaker_list = list(dict.fromkeys(s.speaker for s in segments))
24
+ color_map = {spk: SPEAKER_COLORS[i % len(SPEAKER_COLORS)] for i, spk in enumerate(speaker_list)}
25
+
26
+ # Legend
27
+ legend_items = "".join(
28
+ f"<span style='display:inline-flex;align-items:center;gap:6px;margin-right:16px'>"
29
+ f"<span style='width:12px;height:12px;border-radius:50%;background:{color_map[spk][0]}'></span>"
30
+ f"<span style='font-weight:600;color:{color_map[spk][0]}'>Speaker {spk}</span></span>"
31
+ for spk in speaker_list
32
+ )
33
+ legend = f"<div style='padding:12px 16px;border-bottom:1px solid #e5e7eb;display:flex;flex-wrap:wrap;gap:4px'>{legend_items}</div>"
34
+
35
+ # Conversation bubbles
36
+ bubbles = ""
37
+ for seg in segments:
38
+ fg, bg = color_map[seg.speaker]
39
+ align = "flex-end" if speaker_list.index(seg.speaker) % 2 == 1 else "flex-start"
40
+ bubbles += f"""
41
+ <div style='display:flex;justify-content:{align};margin:6px 12px'>
42
+ <div style='max-width:75%'>
43
+ <div style='font-size:11px;color:#6b7280;margin-bottom:3px;
44
+ {"text-align:right" if align=="flex-end" else ""}'>
45
+ <span style='font-weight:600;color:{fg}'>Speaker {seg.speaker}</span>
46
+ &nbsp;·&nbsp;{seg.start_fmt} → {seg.end_fmt}
47
+ </div>
48
+ <div style='background:{bg};border:1px solid {fg}30;color:#111827;
49
+ padding:10px 14px;border-radius:{"4px 16px 16px 16px" if align=="flex-start" else "16px 4px 16px 16px"};
50
+ font-size:14px;line-height:1.5'>
51
+ {seg.text}
52
+ </div>
53
+ </div>
54
+ </div>"""
55
+
56
+ return f"""
57
+ <div style='border:1px solid #e5e7eb;border-radius:12px;overflow:hidden;
58
+ font-family:system-ui,sans-serif;background:white'>
59
+ {legend}
60
+ <div style='height:480px;overflow-y:auto;padding:8px 0;background:#f9fafb'>
61
+ {bubbles}
62
+ </div>
63
+ </div>"""
64
+
65
+ def make_table_html(segments):
66
+ if not segments:
67
+ return ""
68
+ speaker_list = list(dict.fromkeys(s.speaker for s in segments))
69
+ color_map = {spk: SPEAKER_COLORS[i % len(SPEAKER_COLORS)] for i, spk in enumerate(speaker_list)}
70
+ rows = "".join(
71
+ f"<tr style='border-bottom:1px solid #f3f4f6'>"
72
+ f"<td style='padding:8px 12px'>"
73
+ f" <span style='background:{color_map[s.speaker][1]};color:{color_map[s.speaker][0]};"
74
+ f" padding:3px 10px;border-radius:99px;font-weight:700;font-size:13px'> {s.speaker}</span></td>"
75
+ f"<td style='padding:8px 12px;color:#6b7280;font-size:13px;white-space:nowrap'>{s.start_fmt}</td>"
76
+ f"<td style='padding:8px 12px;color:#6b7280;font-size:13px;white-space:nowrap'>{s.end_fmt}</td>"
77
+ f"<td style='padding:8px 12px;font-size:14px;color:#111827'>{s.text}</td>"
78
+ f"</tr>"
79
+ for s in segments
80
+ )
81
+ return f"""
82
+ <div style='border:1px solid #e5e7eb;border-radius:12px;overflow:hidden;font-family:system-ui,sans-serif'>
83
+ <table style='width:100%;border-collapse:collapse'>
84
+ <thead>
85
+ <tr style='background:#1e3a5f;color:white'>
86
+ <th style='padding:10px 12px;text-align:left;font-size:13px'>Speaker</th>
87
+ <th style='padding:10px 12px;text-align:left;font-size:13px'>Start</th>
88
+ <th style='padding:10px 12px;text-align:left;font-size:13px'>End</th>
89
+ <th style='padding:10px 12px;text-align:left;font-size:13px'>Transcript</th>
90
+ </tr>
91
+ </thead>
92
+ <tbody>{rows}</tbody>
93
+ </table>
94
+ </div>"""
95
+
96
  def process_audio(audio_path, num_speakers):
97
  global _last_segments
98
  if audio_path is None:
99
+ return "⚠️ Please upload an audio file first.", "", ""
100
  try:
101
  pipeline = get_pipeline()
102
  n = int(num_speakers) if num_speakers and int(num_speakers) > 0 else 0
103
  segments = pipeline.process(audio_path, num_speakers=n)
104
  except Exception as e:
105
+ return f"❌ Error: {e}\n{traceback.format_exc()}", "", ""
106
  if not segments:
107
+ return "⚠️ No speech detected.", "", ""
108
  _last_segments = segments
109
  unique = len(set(s.speaker for s in segments))
110
+ status = f"✅ Done — {len(segments)} segments · {unique} speaker(s) detected"
111
+ return status, make_conversation_html(segments), make_table_html(segments)
112
 
113
  def export_json():
114
  if not _last_segments: return None
 
122
  to_csv(_last_segments, out)
123
  return out
124
 
125
+ css = """
126
+ .gradio-container { max-width: 1100px !important; margin: auto !important; }
127
+ footer { display: none !important; }
128
+ """
129
+
130
+ with gr.Blocks(title="Speech Annotation Pipeline", css=css) as demo:
131
+ gr.Markdown("""# 🎙️ Speech Annotation Pipeline
132
+ *Upload audio · Detect speakers · Export transcript*""")
133
+
134
  with gr.Row():
135
+ with gr.Column(scale=1):
136
+ audio_input = gr.Audio(label="Upload Audio (.wav / .mp3 / .flac)", type="filepath")
137
+ num_speakers = gr.Slider(minimum=0, maximum=10, step=1, value=0,
138
+ label="Number of speakers (0 = auto-detect)")
139
+ run_btn = gr.Button("▶ Run Annotation", variant="primary", size="lg")
140
+ status_box = gr.Textbox(label="Status", value="Ready.", interactive=False)
141
+ gr.Markdown("### 📥 Export")
142
+ with gr.Row():
143
+ json_btn = gr.Button("⬇ JSON", size="sm")
144
+ csv_btn = gr.Button("⬇ CSV", size="sm")
145
+ json_file = gr.File(label="JSON Download", visible=True)
146
+ csv_file = gr.File(label="CSV Download", visible=True)
147
+
148
+ with gr.Column(scale=2):
149
+ gr.Markdown("### 💬 Conversation View")
150
+ conversation_html = gr.HTML(value="<div style='height:480px;border:1px solid #e5e7eb;"
151
+ "border-radius:12px;display:flex;align-items:center;"
152
+ "justify-content:center;color:#9ca3af;font-family:system-ui'>"
153
+ "Transcript will appear here after processing…</div>")
154
+ gr.Markdown("### 📋 Segment Table")
155
+ table_html = gr.HTML(value="")
156
 
157
+ run_btn.click(fn=process_audio,
158
+ inputs=[audio_input, num_speakers],
159
+ outputs=[status_box, conversation_html, table_html])
160
  json_btn.click(fn=export_json, inputs=[], outputs=[json_file])
161
+ csv_btn.click(fn=export_csv, inputs=[], outputs=[csv_file])
162
 
163
  if __name__ == "__main__":
164
  demo.launch(server_name="0.0.0.0", server_port=7860, show_error=True)