neuralworm commited on
Commit
eb37fc2
·
verified ·
1 Parent(s): 6a3101c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +99 -224
app.py CHANGED
@@ -2,23 +2,8 @@
2
  # coding: utf-8
3
 
4
  """ Hugging Face Space (Gradio) App: Video -> Audio -> Whisper Transkript (+ Downloads SRT/TXT/VTT/JSON)
5
-
6
- Tab 1: Transkription
7
-
8
- - Video per URL (yt-dlp) oder Upload
9
- - Audio-Extraktion via ffmpeg
10
- - Transkription mit Whisper (lokal)
11
- - Downloads: SRT, VTT, TXT, JSON
12
-
13
-
14
- Tab 2: Netzwerk / DNS Diagnose
15
-
16
- - Testet DNS-Auflösung für mehrere Hosts
17
- - Testet HTTP-Requests auf Basis-URLs
18
- - Zeigt Version/Verfügbarkeit von yt-dlp und ffmpeg
19
-
20
-
21
- Hinweis: Verwende diese App nur für eigene oder freigegebene Inhalte. """
22
  import os
23
  import subprocess
24
  import tempfile
@@ -28,6 +13,8 @@ from datetime import timedelta
28
  import socket
29
  import urllib.request
30
  from urllib.parse import urlparse
 
 
31
 
32
  import gradio as gr
33
 
@@ -41,6 +28,39 @@ try:
41
  except ImportError:
42
  dns_resolver = None
43
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  # ---------------------------------------------------------------------------
45
  # Helper: Shell
46
  # ---------------------------------------------------------------------------
@@ -51,53 +71,42 @@ def run_capture(cmd):
51
  if result.returncode != 0:
52
  stderr_text = result.stderr or ""
53
  tail = stderr_text[-2000:]
54
- raise RuntimeError("Command failed: " + " ".join(cmd) + " " + tail)
55
  return result.stdout
56
 
57
- # ---------------------------------------------------------------------------
58
- # NEUE FUNKTION: DNS-Auflösung via dnspython
59
- # ---------------------------------------------------------------------------
60
  def resolve_hostname_with_dns_python(hostname):
61
- """Resolves a hostname using a public DNS server to bypass local DNS blocks."""
62
  if not dns_resolver:
63
- # Fallback auf System-DNS, wenn dnspython nicht installiert ist
64
  print("Warning: dnspython not found. Falling back to system DNS.")
65
  return socket.gethostbyname(hostname)
66
-
67
  try:
68
  resolver = dns_resolver.Resolver()
69
- resolver.nameservers = ['8.8.8.8', '1.1.1.1'] # Google & Cloudflare DNS
70
  answers = resolver.resolve(hostname, 'A')
71
- if answers:
72
- return answers[0].to_text()
73
  except Exception as e:
74
  print(f"DNS resolution with dnspython failed for {hostname}: {e}")
75
- # Als letzten Ausweg versuchen wir es mit dem System-Resolver
76
  try:
77
  return socket.gethostbyname(hostname)
78
  except Exception as se:
79
- raise se # Den ursprünglichen Systemfehler auslösen
80
  return None
81
 
82
  # ---------------------------------------------------------------------------
83
- # MODIFIZIERTE FUNKTION: Download & Audio
84
  # ---------------------------------------------------------------------------
85
 
86
  def download_video_with_ytdlp(url, out_dir, cookies_path=None, format_selector=None):
87
- """Download a video with yt-dlp, using custom DNS resolution if necessary."""
88
  out_template = str(Path(out_dir) / "%(title)s.%(ext)s")
89
- cmd = ["yt-dlp", "-o", out_template]
 
90
 
91
- # DNS-Umgehung implementieren
92
  try:
93
  parsed_url = urlparse(url)
94
  hostname = parsed_url.hostname
95
  if hostname:
96
- print(f"Resolving hostname: {hostname}")
97
  ip_address = resolve_hostname_with_dns_python(hostname)
98
  if ip_address:
99
- print(f"Resolved {hostname} to {ip_address}. Using --resolve.")
100
- # --resolve weist yt-dlp an, diese IP für den Hostnamen auf Port 443 zu verwenden
101
  resolve_arg = f"{hostname}:443:{ip_address}"
102
  cmd.extend(["--resolve", resolve_arg])
103
  except Exception as e:
@@ -110,17 +119,7 @@ def download_video_with_ytdlp(url, out_dir, cookies_path=None, format_selector=N
110
  cmd.append(url)
111
 
112
  print(f"Running command: {' '.join(cmd)}")
113
-
114
- try:
115
- run_capture(cmd)
116
- except RuntimeError as e:
117
- msg = str(e)
118
- if "Failed to resolve" in msg or "Name or service not known" in msg:
119
- raise RuntimeError(
120
- "DNS/Internet-Problem: Der Host konnte nicht aufgelöst werden. "
121
- "Selbst die DNS-Umgehung ist fehlgeschlagen. Möglicherweise blockiert eine Firewall auch die IP-Adressen."
122
- )
123
- raise
124
 
125
  files = sorted(Path(out_dir).glob("*"), key=lambda p: p.stat().st_mtime, reverse=True)
126
  if not files:
@@ -129,211 +128,87 @@ def download_video_with_ytdlp(url, out_dir, cookies_path=None, format_selector=N
129
 
130
 
131
  def extract_audio_ffmpeg(video_path, out_wav):
132
- cmd = ["ffmpeg", "-y", "-i", video_path, "-vn", "-ac", "1", "-ar", "16000", "-f", "wav", out_wav]
 
133
  subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
134
  return out_wav
135
 
136
- # ... (der Rest des Codes von "Zeit- und Format-Helfer" bis zum Ende bleibt identisch)
137
- # Ich füge ihn hier zur Vollständigkeit ein.
138
-
139
- # ---------------------------------------------------------------------------
140
- # Zeit- und Format-Helfer
141
- # ---------------------------------------------------------------------------
142
-
143
- def seconds_to_timestamp(s):
144
- hours = int(s // 3600)
145
- minutes = int((s % 3600) // 60)
146
- seconds = int(s % 60)
147
- ms = int(round((s - int(s)) * 1000))
148
- return f"{hours:02d}:{minutes:02d}:{seconds:02d},{ms:03d}"
149
-
150
- def format_timestamp_vtt(s):
151
- hours = int(s // 3600)
152
- minutes = int((s % 3600) // 60)
153
- seconds = int(s % 60)
154
- ms = int(round((s - int(s)) * 1000))
155
- return f"{hours:02d}:{minutes:02d}:{seconds:02d}.{ms:03d}"
156
-
157
- def segments_to_srt(segments):
158
- parts = []
159
- for i, seg in enumerate(segments, start=1):
160
- start = seconds_to_timestamp(seg['start'])
161
- end = seconds_to_timestamp(seg['end'])
162
- text = seg['text'].strip()
163
- parts.append(f"{i}\n{start} --> {end}\n{text}")
164
- return "\n\n".join(parts) + "\n\n"
165
-
166
- def segments_to_vtt(segments):
167
- parts = ["WEBVTT\n"]
168
- for seg in segments:
169
- start = format_timestamp_vtt(seg['start'])
170
- end = format_timestamp_vtt(seg['end'])
171
- text = seg['text'].strip()
172
- parts.append(f"{start} --> {end}\n{text}")
173
- return "\n\n".join(parts)
174
-
175
- def segments_to_txt(segments):
176
- return "\n".join([f"[{seconds_to_timestamp(seg['start'])}] {seg['text'].strip()}" for seg in segments])
177
-
178
- def segments_to_json(segments, language=None, metadata=None):
179
- data = {"language": language, "segments": segments}
180
- if metadata:
181
- data["metadata"] = metadata
182
- return json.dumps(data, ensure_ascii=False, indent=2)
183
 
184
  # ---------------------------------------------------------------------------
185
- # Kern-Pipeline: Transkription
186
  # ---------------------------------------------------------------------------
187
-
188
  def transcribe_pipeline(file_obj, url, model_size, keep_video=False, cookies_file=None, format_selector=None):
189
- if whisper is None:
190
- return "Fehler: whisper ist nicht installiert.", None, None, None, None, None
191
-
192
- tmpdir = tempfile.mkdtemp(prefix="whisper_space_")
193
  try:
194
- if url:
195
- video_path = download_video_with_ytdlp(url, tmpdir, cookies_path=cookies_file, format_selector=format_selector)
196
- elif file_obj:
197
- video_path = file_obj.name
198
- else:
199
- return "Kein Video angegeben.", None, None, None, None, None
200
-
201
- audio_wav = str(Path(tmpdir) / "audio.wav")
202
- extract_audio_ffmpeg(video_path, audio_wav)
203
-
204
- model = whisper.load_model(model_size)
205
- result = model.transcribe(audio_wav, verbose=False)
206
- segments = result.get("segments", [])
207
- language = result.get("language", "unknown")
208
-
209
- txt_text = segments_to_txt(segments)
210
- srt_text = segments_to_srt(segments)
211
- vtt_text = segments_to_vtt(segments)
212
- json_text = segments_to_json(segments, language, {"model": model_size})
213
-
214
- base = Path(video_path).stem
215
- files = {}
216
- for ext, content in {"srt": srt_text, "vtt": vtt_text, "txt": txt_text, "json": json_text}.items():
217
- p = Path(tmpdir) / f"{base}.{ext}"
218
- p.write_text(content, encoding="utf-8")
219
- files[ext] = str(p)
220
-
221
- if not keep_video and url:
222
- try:
223
- os.remove(video_path)
224
- except Exception:
225
- pass
226
-
227
- meta = f"Model: {model_size}, Sprache: {language}"
228
- return txt_text, files["srt"], files["vtt"], files["txt"], files["json"], meta
229
- except Exception as e:
230
- return f"Fehler: {e}", None, None, None, None, None
231
 
232
  # ---------------------------------------------------------------------------
233
- # Netzwerk / DNS Diagnose
234
  # ---------------------------------------------------------------------------
235
-
236
  def dns_internet_diag():
237
  lines = []
 
 
 
238
 
239
- lines.append("=== DNS-Auflösung (System) ===")
240
- for host in ["huggingface.co", "www.google.com", "www.instagram.com", "youtube.com"]:
241
  try:
242
- ip = socket.gethostbyname(host)
243
- lines.append(f"{host} -> {ip} (OK)")
 
 
 
 
 
 
 
 
244
  except Exception as e:
245
- lines.append(f"{host} -> ERROR: {e}")
246
-
 
 
 
 
247
  if dns_resolver:
248
  lines.append("\n\n=== DNS-Auflösung (via dnspython mit 8.8.8.8) ===")
249
  for host in ["huggingface.co", "www.google.com", "www.instagram.com", "youtube.com"]:
250
- try:
251
- ip = resolve_hostname_with_dns_python(host)
252
- lines.append(f"{host} -> {ip} (OK)")
253
- except Exception as e:
254
- lines.append(f"{host} -> ERROR: {e}")
255
-
256
- lines.append("\n\n=== HTTP-Requests (GET) ===")
257
- for url in ["https://huggingface.co", "https://www.google.com"]:
258
- try:
259
- with urllib.request.urlopen(url, timeout=5) as resp:
260
- code = getattr(resp, "status", None) or resp.getcode()
261
- lines.append(f"{url} -> OK (Status {code})")
262
- except Exception as e:
263
- lines.append(f"{url} -> ERROR: {e}")
264
-
265
- lines.append("\n\n=== yt-dlp ===")
266
- try:
267
- out = run_capture(["yt-dlp", "--version"])
268
- lines.append(f"yt-dlp Version: {out.strip()}")
269
- except Exception as e:
270
- lines.append(f"yt-dlp Fehler: {e}")
271
-
272
- lines.append("\n\n=== ffmpeg ===")
273
- try:
274
- out = run_capture(["ffmpeg", "-version"])
275
- first = out.splitlines()[0] if out else "(keine Ausgabe)"
276
- lines.append(first)
277
- except Exception as e:
278
- lines.append(f"ffmpeg Fehler: {e}")
279
-
280
  return "\n".join(lines)
281
 
282
  # ---------------------------------------------------------------------------
283
- # Gradio UI mit zwei Tabs
284
  # ---------------------------------------------------------------------------
285
-
286
  with gr.Blocks() as demo:
287
  gr.Markdown("# Video → Whisper Transkript (SRT/TXT/VTT/JSON)")
288
-
289
  with gr.Tab("Transkription"):
290
  with gr.Row():
291
- with gr.Column():
292
- url_in = gr.Textbox(label="Video URL", placeholder="https://...")
293
- file_in = gr.File(label="Oder Videodatei hochladen")
294
- cookies_in = gr.File(label="Cookies.txt (optional, für yt-dlp)")
295
- fmt_in = gr.Textbox(label="Format (optional, yt-dlp -f)", placeholder="z.B. bestvideo+bestaudio/best")
296
- model_sel = gr.Radio(["tiny", "base", "small", "medium", "large"], value="small", label="Whisper-Modell")
297
- keep_chk = gr.Checkbox(label="Video behalten (bei URL-Download)", value=False)
298
- btn = gr.Button("Transkribieren")
299
- status = gr.Textbox(label="Status / Meta", interactive=False)
300
- with gr.Column():
301
- transcript = gr.Textbox(label="Transkript", lines=20)
302
- srt_dl = gr.File(label="SRT")
303
- vtt_dl = gr.File(label="VTT")
304
- txt_dl = gr.File(label="TXT")
305
- json_dl = gr.File(label="JSON")
306
-
307
- def run_transcribe(f, u, m, k, c, fmt):
308
- cookies_path = c.name if c else None
309
- display, srtf, vttf, txtf, jsonf, meta = transcribe_pipeline(
310
- f, u, m, k, cookies_file=cookies_path, format_selector=(fmt or None)
311
- )
312
- return (
313
- display,
314
- gr.update(value=srtf, visible=bool(srtf)),
315
- gr.update(value=vttf, visible=bool(vttf)),
316
- gr.update(value=txtf, visible=bool(txtf)),
317
- gr.update(value=jsonf, visible=bool(jsonf)),
318
- meta,
319
- )
320
-
321
- btn.click(
322
- run_transcribe,
323
- [file_in, url_in, model_sel, keep_chk, cookies_in, fmt_in],
324
- [transcript, srt_dl, vtt_dl, txt_dl, json_dl, status],
325
- )
326
-
327
  with gr.Tab("Netzwerk / DNS Diagnose"):
328
- gr.Markdown(
329
- """Führt Tests für den System-DNS und einen externen DNS (via dnspython) durch.
330
-
331
- Wenn der System-DNS fehlschlägt, der externe aber funktioniert, ist die DNS-Umgehung aktiv.
332
- Wenn beides fehlschlägt, blockiert eine Firewall wahrscheinlich auch die IP-Adressen."""
333
- )
334
- diag_btn = gr.Button("Diagnose starten")
335
- diag_out = gr.Textbox(label="Diagnose-Ausgabe", lines=25)
336
-
337
  diag_btn.click(dns_internet_diag, inputs=[], outputs=[diag_out])
338
 
339
  if __name__ == "__main__":
 
2
  # coding: utf-8
3
 
4
  """ Hugging Face Space (Gradio) App: Video -> Audio -> Whisper Transkript (+ Downloads SRT/TXT/VTT/JSON)
5
+ LÖSUNG FÜR PATH-KONFLIKT IMPLEMENTIERT
6
+ """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  import os
8
  import subprocess
9
  import tempfile
 
13
  import socket
14
  import urllib.request
15
  from urllib.parse import urlparse
16
+ import sys
17
+ import shutil
18
 
19
  import gradio as gr
20
 
 
28
  except ImportError:
29
  dns_resolver = None
30
 
31
+ # ---------------------------------------------------------------------------
32
+ # NEUE HELFER-FUNKTION: Finde den korrekten Pfad zur ausführbaren Datei
33
+ # ---------------------------------------------------------------------------
34
+ def get_executable_path(name):
35
+ """Findet den vollen Pfad zu einer ausführbaren Datei, die mit pip installiert wurde."""
36
+ # shutil.which ist der robusteste Weg, um den Pfad zu einer ausführbaren Datei zu finden
37
+ path = shutil.which(name)
38
+ if path:
39
+ # Führen wir eine Versionsprüfung durch, um sicherzustellen, dass es nicht die alte Systemversion ist
40
+ try:
41
+ version_output = subprocess.check_output([path, '--version'], text=True)
42
+ # Neuere Versionen haben oft ein Datum im Format YYYY.MM.DD
43
+ if '.' in version_output and len(version_output.split('.')[0]) == 4:
44
+ print(f"Found modern executable for '{name}' at: {path}")
45
+ return path
46
+ except Exception:
47
+ pass # Konnte die Version nicht prüfen, fahre mit der Suche fort
48
+
49
+ # Fallback für Umgebungen, in denen `which` nicht das Richtige findet
50
+ # Versuche im Verzeichnis des Python-Interpreters
51
+ py_executable_dir = Path(sys.executable).parent
52
+ candidate = py_executable_dir / name
53
+ if candidate.exists():
54
+ print(f"Found executable '{name}' next to python interpreter: {candidate}")
55
+ return str(candidate)
56
+
57
+ print(f"Warning: Could not find a reliable path for '{name}'. Falling back to system PATH.")
58
+ return name # Fallback auf den einfachen Namen, wenn alles andere fehlschlägt
59
+
60
+ # Global die Pfade einmal beim Start ermitteln
61
+ YT_DLP_PATH = get_executable_path("yt-dlp")
62
+ FFMPEG_PATH = get_executable_path("ffmpeg")
63
+
64
  # ---------------------------------------------------------------------------
65
  # Helper: Shell
66
  # ---------------------------------------------------------------------------
 
71
  if result.returncode != 0:
72
  stderr_text = result.stderr or ""
73
  tail = stderr_text[-2000:]
74
+ raise RuntimeError("Command failed: " + " ".join(map(str, cmd)) + " " + tail)
75
  return result.stdout
76
 
77
+ # ... (resolve_hostname_with_dns_python bleibt gleich)
 
 
78
  def resolve_hostname_with_dns_python(hostname):
 
79
  if not dns_resolver:
 
80
  print("Warning: dnspython not found. Falling back to system DNS.")
81
  return socket.gethostbyname(hostname)
 
82
  try:
83
  resolver = dns_resolver.Resolver()
84
+ resolver.nameservers = ['8.8.8.8', '1.1.1.1']
85
  answers = resolver.resolve(hostname, 'A')
86
+ return answers[0].to_text() if answers else None
 
87
  except Exception as e:
88
  print(f"DNS resolution with dnspython failed for {hostname}: {e}")
 
89
  try:
90
  return socket.gethostbyname(hostname)
91
  except Exception as se:
92
+ raise se
93
  return None
94
 
95
  # ---------------------------------------------------------------------------
96
+ # MODIFIZIERTE FUNKTION: Download & Audio (nutzt jetzt expliziten Pfad)
97
  # ---------------------------------------------------------------------------
98
 
99
  def download_video_with_ytdlp(url, out_dir, cookies_path=None, format_selector=None):
 
100
  out_template = str(Path(out_dir) / "%(title)s.%(ext)s")
101
+ # KORREKTUR: Verwende den expliziten, vollen Pfad zu yt-dlp
102
+ cmd = [YT_DLP_PATH, "-o", out_template]
103
 
 
104
  try:
105
  parsed_url = urlparse(url)
106
  hostname = parsed_url.hostname
107
  if hostname:
 
108
  ip_address = resolve_hostname_with_dns_python(hostname)
109
  if ip_address:
 
 
110
  resolve_arg = f"{hostname}:443:{ip_address}"
111
  cmd.extend(["--resolve", resolve_arg])
112
  except Exception as e:
 
119
  cmd.append(url)
120
 
121
  print(f"Running command: {' '.join(cmd)}")
122
+ run_capture(cmd)
 
 
 
 
 
 
 
 
 
 
123
 
124
  files = sorted(Path(out_dir).glob("*"), key=lambda p: p.stat().st_mtime, reverse=True)
125
  if not files:
 
128
 
129
 
130
  def extract_audio_ffmpeg(video_path, out_wav):
131
+ # KORREKTUR: Verwende den expliziten, vollen Pfad zu ffmpeg
132
+ cmd = [FFMPEG_PATH, "-y", "-i", video_path, "-vn", "-ac", "1", "-ar", "16000", "-f", "wav", out_wav]
133
  subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
134
  return out_wav
135
 
136
+ # ... (Zeit- und Format-Helfer bleiben identisch)
137
+ def seconds_to_timestamp(s): hours = int(s // 3600); minutes = int((s % 3600) // 60); seconds = int(s % 60); ms = int(round((s - int(s)) * 1000)); return f"{hours:02d}:{minutes:02d}:{seconds:02d},{ms:03d}"
138
+ def format_timestamp_vtt(s): hours = int(s // 3600); minutes = int((s % 3600) // 60); seconds = int(s % 60); ms = int(round((s - int(s)) * 1000)); return f"{hours:02d}:{minutes:02d}:{seconds:02d}.{ms:03d}"
139
+ def segments_to_srt(segments): parts = []; [parts.append(f"{i}\n{seconds_to_timestamp(s['start'])} --> {seconds_to_timestamp(s['end'])}\n{s['text'].strip()}") for i, s in enumerate(segments, 1)]; return "\n\n".join(parts) + "\n\n"
140
+ def segments_to_vtt(segments): parts = ["WEBVTT\n"]; [parts.append(f"{format_timestamp_vtt(s['start'])} --> {format_timestamp_vtt(s['end'])}\n{s['text'].strip()}") for s in segments]; return "\n\n".join(parts)
141
+ def segments_to_txt(segments): return "\n".join([f"[{seconds_to_timestamp(s['start'])}] {s['text'].strip()}" for s in segments])
142
+ def segments_to_json(segments, language=None, metadata=None): data = {"language": language, "segments": segments}; [data.update({"metadata": metadata}) if metadata else None]; return json.dumps(data, ensure_ascii=False, indent=2)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
 
144
  # ---------------------------------------------------------------------------
145
+ # Kern-Pipeline: Transkription (bleibt logisch identisch)
146
  # ---------------------------------------------------------------------------
 
147
  def transcribe_pipeline(file_obj, url, model_size, keep_video=False, cookies_file=None, format_selector=None):
148
+ if whisper is None: return "Fehler: whisper ist nicht installiert.", None, None, None, None, None
149
+ tmpdir = tempfile.mkdtemp(prefix="whisper_space_");
 
 
150
  try:
151
+ video_path = download_video_with_ytdlp(url, tmpdir, cookies_path=cookies_file, format_selector=format_selector) if url else file_obj.name
152
+ if not video_path: return "Kein Video angegeben.", None, None, None, None, None
153
+ audio_wav = str(Path(tmpdir) / "audio.wav"); extract_audio_ffmpeg(video_path, audio_wav)
154
+ model = whisper.load_model(model_size); result = model.transcribe(audio_wav, verbose=False)
155
+ segments = result.get("segments", []); language = result.get("language", "unknown")
156
+ txt_text = segments_to_txt(segments); srt_text = segments_to_srt(segments); vtt_text = segments_to_vtt(segments); json_text = segments_to_json(segments, language, {"model": model_size})
157
+ base = Path(video_path).stem; files = {}
158
+ for ext, content in {"srt": srt_text, "vtt": vtt_text, "txt": txt_text, "json": json_text}.items(): p = Path(tmpdir) / f"{base}.{ext}"; p.write_text(content, encoding="utf-8"); files[ext] = str(p)
159
+ if not keep_video and url: [os.remove(video_path) for _ in [1] if os.path.exists(video_path)]
160
+ meta = f"Model: {model_size}, Sprache: {language}"; return txt_text, files["srt"], files["vtt"], files["txt"], files["json"], meta
161
+ except Exception as e: return f"Fehler: {e}", None, None, None, None, None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
 
163
  # ---------------------------------------------------------------------------
164
+ # MODIFIZIERTE DIAGNOSE: Zeigt uns den Beweis!
165
  # ---------------------------------------------------------------------------
 
166
  def dns_internet_diag():
167
  lines = []
168
+ lines.append("=== Python & PATH Info ===")
169
+ lines.append(f"Python Executable: {sys.executable}")
170
+ lines.append(f"System PATH: {os.environ.get('PATH', 'Nicht gefunden')}")
171
 
172
+ lines.append("\n\n=== Executable Location & Version (Proof) ===")
173
+ for name in ["yt-dlp", "ffmpeg"]:
174
  try:
175
+ # Finde den Pfad, den unser Skript verwenden WÜRDE
176
+ path_in_use = get_executable_path(name)
177
+ lines.append(f"Path für '{name}' via get_executable_path(): {path_in_use}")
178
+ # Finde den Pfad, den das SYSTEM verwenden würde
179
+ system_path = shutil.which(name)
180
+ lines.append(f"Path für '{name}' via shutil.which() (System PATH): {system_path}")
181
+ # Prüfe die Version der Datei, die wir tatsächlich verwenden
182
+ out = run_capture([path_in_use, "-version" if name == "ffmpeg" else "--version"])
183
+ version_line = out.splitlines()[0] if out else "(keine Ausgabe)"
184
+ lines.append(f"VERSION VON '{path_in_use}': {version_line.strip()}")
185
  except Exception as e:
186
+ lines.append(f"Fehler bei der Diagnose von '{name}': {e}")
187
+ # ... Rest der Diagnose bleibt gleich
188
+ lines.append("\n\n=== DNS-Auflösung (System) ===")
189
+ for host in ["huggingface.co", "www.google.com", "www.instagram.com", "youtube.com"]:
190
+ try: ip = socket.gethostbyname(host); lines.append(f"{host} -> {ip} (OK)")
191
+ except Exception as e: lines.append(f"{host} -> ERROR: {e}")
192
  if dns_resolver:
193
  lines.append("\n\n=== DNS-Auflösung (via dnspython mit 8.8.8.8) ===")
194
  for host in ["huggingface.co", "www.google.com", "www.instagram.com", "youtube.com"]:
195
+ try: ip = resolve_hostname_with_dns_python(host); lines.append(f"{host} -> {ip} (OK)")
196
+ except Exception as e: lines.append(f"{host} -> ERROR: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
  return "\n".join(lines)
198
 
199
  # ---------------------------------------------------------------------------
200
+ # Gradio UI (bleibt identisch)
201
  # ---------------------------------------------------------------------------
 
202
  with gr.Blocks() as demo:
203
  gr.Markdown("# Video → Whisper Transkript (SRT/TXT/VTT/JSON)")
 
204
  with gr.Tab("Transkription"):
205
  with gr.Row():
206
+ with gr.Column(): url_in = gr.Textbox(label="Video URL", placeholder="https://..."); file_in = gr.File(label="Oder Videodatei hochladen"); cookies_in = gr.File(label="Cookies.txt (optional, für yt-dlp)"); fmt_in = gr.Textbox(label="Format (optional, yt-dlp -f)", placeholder="z.B. bestvideo+bestaudio/best"); model_sel = gr.Radio(["tiny", "base", "small", "medium", "large"], value="small", label="Whisper-Modell"); keep_chk = gr.Checkbox(label="Video behalten (bei URL-Download)", value=False); btn = gr.Button("Transkribieren"); status = gr.Textbox(label="Status / Meta", interactive=False)
207
+ with gr.Column(): transcript = gr.Textbox(label="Transkript", lines=20); srt_dl = gr.File(label="SRT"); vtt_dl = gr.File(label="VTT"); txt_dl = gr.File(label="TXT"); json_dl = gr.File(label="JSON")
208
+ def run_transcribe(f, u, m, k, c, fmt): cookies_path = c.name if c else None; display, srtf, vttf, txtf, jsonf, meta = transcribe_pipeline(f, u, m, k, cookies_file=cookies_path, format_selector=(fmt or None)); return (display, gr.update(value=srtf, visible=bool(srtf)), gr.update(value=vttf, visible=bool(vttf)), gr.update(value=txtf, visible=bool(txtf)), gr.update(value=jsonf, visible=bool(jsonf)), meta,)
209
+ btn.click(run_transcribe, [file_in, url_in, model_sel, keep_chk, cookies_in, fmt_in], [transcript, srt_dl, vtt_dl, txt_dl, json_dl, status])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
  with gr.Tab("Netzwerk / DNS Diagnose"):
211
+ gr.Markdown("""Führt Tests durch, um Pfad-Konflikte und DNS-Probleme zu identifizieren."""); diag_btn = gr.Button("Diagnose starten"); diag_out = gr.Textbox(label="Diagnose-Ausgabe", lines=25)
 
 
 
 
 
 
 
 
212
  diag_btn.click(dns_internet_diag, inputs=[], outputs=[diag_out])
213
 
214
  if __name__ == "__main__":