Monstermango commited on
Commit
0e55d28
·
verified ·
1 Parent(s): 82e7aa3

Full-audio transcription, language detection, save with speaker names

Browse files
Files changed (1) hide show
  1. app.py +67 -17
app.py CHANGED
@@ -71,15 +71,41 @@ def _analyse_gpu(audio_path, num_speakers):
71
  output = diarizer(audio_path, **kwargs)
72
  dia = getattr(output, "speaker_diarization", output)
73
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  segmente = []
75
- for turn, _, lb in dia.itertracks(yield_label=True):
76
- text = None
77
- if turn.duration >= 0.3:
78
- wellenform, sr = loader.crop(audio_path, Segment(turn.start, turn.end))
79
- text = asr({"array": wellenform.squeeze(0).numpy(),
80
- "sampling_rate": sr})["text"].strip()
81
  segmente.append({"start": round(turn.start, 2),
82
- "ende": round(turn.end, 2), "label": lb, "text": text})
 
83
 
84
  stats = {lb: {"dauer": round(dia.label_duration(lb), 1),
85
  "turns": len(dia.label_timeline(lb))}
@@ -92,18 +118,21 @@ def _analyse_gpu(audio_path, num_speakers):
92
  "ende": round(seg.end, 2), "wer": wer})
93
 
94
  return {"dauer": round(loader.get_duration(audio_path), 1),
95
- "segmente": segmente, "stats": stats, "overlaps": overlaps}
 
96
 
97
 
98
  # ---------- Markdown-Ablage ----------
99
 
100
- def _markdown(daten, quelle, zeitpunkt):
101
- namen, _ = _namen_farben(daten["stats"])
 
102
  frontmatter = {
103
  "titel": f"Aufnahme {zeitpunkt:%Y-%m-%d %H:%M}",
104
  "datum": zeitpunkt.isoformat(timespec="seconds"),
105
  "dauer_s": daten["dauer"],
106
  "sprecher": len(daten["stats"]),
 
107
  "quelle": quelle,
108
  "redeanteile_s": {namen[lb]: st["dauer"]
109
  for lb, st in daten["stats"].items()},
@@ -185,15 +214,30 @@ def analysieren_api(key, audio, num_speakers):
185
  except Exception as e:
186
  return {"ok": False, "fehler": f"Analyse fehlgeschlagen: {e}"}
187
  zeitpunkt = datetime.now(ZEITZONE)
188
- md_text = _markdown(daten, quelle, zeitpunkt)
189
- try:
190
- pfad = _speichern(md_text, zeitpunkt)
191
- except Exception:
192
- pfad = None
193
  namen, farben = _namen_farben(daten["stats"])
194
  return {"ok": True, "daten": daten, "namen": namen, "farben": farben,
195
- "zeitpunkt": zeitpunkt.isoformat(timespec="seconds"), "pfad": pfad,
196
- "markdown": md_text}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
 
198
 
199
  def verlauf_api(key):
@@ -242,6 +286,12 @@ with gr.Blocks(title="Sprecher-Analyse API") as demo:
242
  out_analyse = gr.JSON()
243
  b_analyse.click(analysieren_api, [key, audio, num_speakers],
244
  out_analyse, api_name="analysieren")
 
 
 
 
 
 
245
  with gr.Tab("Verlauf"):
246
  b_verlauf = gr.Button("Verlauf laden")
247
  out_verlauf = gr.JSON()
 
71
  output = diarizer(audio_path, **kwargs)
72
  dia = getattr(output, "speaker_diarization", output)
73
 
74
+ # Die ganze Aufnahme in einem Stück transkribieren: voller Kontext liefert
75
+ # deutlich bessere Texte als Einzelsegmente; die Sprache erkennt Whisper
76
+ # dabei automatisch.
77
+ transkript = asr(audio_path, return_timestamps=True, return_language=True)
78
+ chunks = []
79
+ for c in transkript.get("chunks", []):
80
+ start, ende = c.get("timestamp") or (None, None)
81
+ if start is None or not c.get("text", "").strip():
82
+ continue
83
+ chunks.append({"start": float(start),
84
+ "ende": float(ende if ende is not None else start + 30.0),
85
+ "text": c["text"].strip(),
86
+ "sprache": c.get("language")})
87
+ sprache = next((c["sprache"] for c in chunks if c.get("sprache")), None)
88
+
89
+ turns = [(turn, lb) for turn, _, lb in dia.itertracks(yield_label=True)]
90
+ texte = [[] for _ in turns]
91
+ for c in chunks:
92
+ mitte = (c["start"] + c["ende"]) / 2
93
+ best, best_wert = None, None
94
+ for i, (turn, _) in enumerate(turns):
95
+ ueberlappung = max(0.0, min(c["ende"], turn.end)
96
+ - max(c["start"], turn.start))
97
+ abstand = abs(mitte - (turn.start + turn.end) / 2)
98
+ wert = (-ueberlappung, abstand)
99
+ if best_wert is None or wert < best_wert:
100
+ best, best_wert = i, wert
101
+ if best is not None:
102
+ texte[best].append(c["text"])
103
+
104
  segmente = []
105
+ for i, (turn, lb) in enumerate(turns):
 
 
 
 
 
106
  segmente.append({"start": round(turn.start, 2),
107
+ "ende": round(turn.end, 2), "label": lb,
108
+ "text": " ".join(texte[i]) or None})
109
 
110
  stats = {lb: {"dauer": round(dia.label_duration(lb), 1),
111
  "turns": len(dia.label_timeline(lb))}
 
118
  "ende": round(seg.end, 2), "wer": wer})
119
 
120
  return {"dauer": round(loader.get_duration(audio_path), 1),
121
+ "segmente": segmente, "stats": stats, "overlaps": overlaps,
122
+ "sprache": sprache}
123
 
124
 
125
  # ---------- Markdown-Ablage ----------
126
 
127
+ def _markdown(daten, quelle, zeitpunkt, namen=None):
128
+ std_namen, _ = _namen_farben(daten["stats"])
129
+ namen = {**std_namen, **(namen or {})}
130
  frontmatter = {
131
  "titel": f"Aufnahme {zeitpunkt:%Y-%m-%d %H:%M}",
132
  "datum": zeitpunkt.isoformat(timespec="seconds"),
133
  "dauer_s": daten["dauer"],
134
  "sprecher": len(daten["stats"]),
135
+ "sprache": daten.get("sprache"),
136
  "quelle": quelle,
137
  "redeanteile_s": {namen[lb]: st["dauer"]
138
  for lb, st in daten["stats"].items()},
 
214
  except Exception as e:
215
  return {"ok": False, "fehler": f"Analyse fehlgeschlagen: {e}"}
216
  zeitpunkt = datetime.now(ZEITZONE)
 
 
 
 
 
217
  namen, farben = _namen_farben(daten["stats"])
218
  return {"ok": True, "daten": daten, "namen": namen, "farben": farben,
219
+ "zeitpunkt": zeitpunkt.isoformat(timespec="seconds"),
220
+ "quelle": quelle, "sprache": daten.get("sprache")}
221
+
222
+
223
+ def speichern_api(key, analyse, namen):
224
+ if not _pruefe(key):
225
+ return {"ok": False, "fehler": "Ungültiger Zugangsschlüssel."}
226
+ try:
227
+ daten = analyse["daten"]
228
+ zeitpunkt = datetime.fromisoformat(analyse["zeitpunkt"])
229
+ quelle = analyse.get("quelle") or "aufnahme"
230
+ daten["stats"]
231
+ except (KeyError, TypeError, ValueError):
232
+ return {"ok": False, "fehler": "Ungültige Analysedaten."}
233
+ eigene = {k: v.strip() for k, v in (namen or {}).items()
234
+ if isinstance(v, str) and v.strip()}
235
+ md_text = _markdown(daten, quelle, zeitpunkt, eigene)
236
+ try:
237
+ pfad = _speichern(md_text, zeitpunkt)
238
+ except Exception as e:
239
+ return {"ok": False, "fehler": f"Speichern fehlgeschlagen: {e}"}
240
+ return {"ok": True, "pfad": pfad, "markdown": md_text}
241
 
242
 
243
  def verlauf_api(key):
 
286
  out_analyse = gr.JSON()
287
  b_analyse.click(analysieren_api, [key, audio, num_speakers],
288
  out_analyse, api_name="analysieren")
289
+ analyse_json = gr.JSON(label="Analyse-Objekt (aus /analysieren)")
290
+ namen_json = gr.JSON(label='Sprechernamen, z. B. {"SPEAKER_00": "Nils"}')
291
+ b_speichern = gr.Button("Als Markdown speichern")
292
+ out_speichern = gr.JSON()
293
+ b_speichern.click(speichern_api, [key, analyse_json, namen_json],
294
+ out_speichern, api_name="speichern")
295
  with gr.Tab("Verlauf"):
296
  b_verlauf = gr.Button("Verlauf laden")
297
  out_verlauf = gr.JSON()