From 7ad3715c520b8fbe8b0a57711fb36c48d87cfa84 Mon Sep 17 00:00:00 2001 From: David Kifer Date: Mon, 12 Jan 2026 10:53:06 -0500 Subject: [PATCH] added diag script --- .../__pycache__/extractor.cpython-313.pyc | Bin 3905 -> 6471 bytes .../ai_transcriber_v2/diagnostic_check.py | 95 ++++++++++++++++++ .../ai_transcriber_v2/extractor.py | 68 ++++++++++++- video_transcription/ai_transcriber_v2/main.py | 27 +++-- 4 files changed, 172 insertions(+), 18 deletions(-) create mode 100755 video_transcription/ai_transcriber_v2/diagnostic_check.py diff --git a/video_transcription/ai_transcriber_v2/__pycache__/extractor.cpython-313.pyc b/video_transcription/ai_transcriber_v2/__pycache__/extractor.cpython-313.pyc index 4899a45c24ebe1f27f14e3eb9bf32bf12a9af2f9..ff2d352fe275f428efda02311dbe2f896fbc828f 100644 GIT binary patch delta 3306 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 R$Z20MZu|QX58+|A#(#$}%L@Pi diff --git a/video_transcription/ai_transcriber_v2/diagnostic_check.py b/video_transcription/ai_transcriber_v2/diagnostic_check.py new file mode 100755 index 0000000..737dc0b --- /dev/null +++ b/video_transcription/ai_transcriber_v2/diagnostic_check.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +import sys +import shutil +import subprocess +import torch +import os + +def check_flatpak(): + return os.path.exists("/.flatpak-info") + +def run_cmd(cmd_list): + in_flatpak = check_flatpak() + if in_flatpak: + full_cmd = ["flatpak-spawn", "--host"] + cmd_list + else: + full_cmd = cmd_list + + try: + result = subprocess.run(full_cmd, capture_output=True, text=True) + return result.returncode == 0, result.stdout.strip(), result.stderr.strip() + except FileNotFoundError: + return False, "", "Command not found" + except Exception as e: + return False, "", str(e) + +def main(): + print("========================================") + print(" AI Transcriber Diagnostic Tool") + print("========================================") + + in_flatpak = check_flatpak() + print(f"Environment: {'Flatpak Sandbox' if in_flatpak else 'Native Host'}") + print("-" * 40) + + # 1. GPU Check (PyTorch) + print("\n[1] GPU Availability (Internal PyTorch)") + if torch.cuda.is_available(): + print(f"✅ GPU Detected: {torch.cuda.get_device_name(0)}") + print(f" VRAM: {torch.cuda.get_device_properties(0).total_memory / 1024**3:.2f} GB") + print(f" CUDA Version: {torch.version.cuda}") + else: + print("❌ GPU NOT Detected by PyTorch.") + print(" Whisper will run on CPU (Slow).") + + # 2. Nvidia Driver Check (System) + print("\n[2] Nvidia Driver Check (System)") + ok, out, err = run_cmd(["nvidia-smi", "--query-gpu=name,driver_version", "--format=csv,noheader"]) + if ok: + print(f"✅ Driver Active: {out}") + else: + print("❌ 'nvidia-smi' failed. Drivers might be missing or inaccessible.") + if err: print(f" Error: {err}") + + # 3. FFmpeg Check + print("\n[3] FFmpeg Check") + ok, out, err = run_cmd(["ffmpeg", "-version"]) + if ok: + version_line = out.split('\n')[0] + print(f"✅ FFmpeg Ready: {version_line}") + else: + print("❌ FFmpeg NOT found.") + print(" On Bazzite, install it with: 'brew install ffmpeg'") + + # 4. FFprobe Check + print("\n[4] FFprobe Check (Required for duration checks)") + ok, out, err = run_cmd(["ffprobe", "-version"]) + if ok: + version_line = out.split('\n')[0] + print(f"✅ FFprobe Ready: {version_line}") + else: + print("❌ FFprobe NOT found.") + + # 5. Ollama Check + print("\n[5] Local LLM (Ollama)") + ok, out, err = run_cmd(["ollama", "--version"]) + if ok: + print(f"✅ Ollama Installed: {out}") + + # Check server + import socket + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(1) + result = sock.connect_ex(('127.0.0.1', 11434)) + if result == 0: + print("✅ Ollama Server is RUNNING.") + else: + print("⚠️ Ollama Server is NOT running (Scripts will try to auto-start it).") + else: + print("❌ Ollama NOT found.") + print(" Run: ./ai_transcriber_v2/install_local_llm.sh") + + print("\n========================================") + +if __name__ == "__main__": + main() diff --git a/video_transcription/ai_transcriber_v2/extractor.py b/video_transcription/ai_transcriber_v2/extractor.py index d39f7e2..202541c 100644 --- a/video_transcription/ai_transcriber_v2/extractor.py +++ b/video_transcription/ai_transcriber_v2/extractor.py @@ -1,6 +1,7 @@ import os import subprocess import sys +import json from utils import verify_file_not_empty def run_ffmpeg(args): @@ -20,6 +21,27 @@ def run_ffmpeg(args): print("Error: 'ffmpeg' command not found. Please ensure it is installed on your host system.") return False +def get_video_duration(file_path): + """ + Gets video duration in seconds using ffprobe. + """ + in_flatpak = os.path.exists("/.flatpak-info") + cmd_base = ["flatpak-spawn", "--host"] if in_flatpak else [] + + cmd = cmd_base + [ + "ffprobe", + "-v", "error", + "-show_entries", "format=duration", + "-of", "default=noprint_wrappers=1:nokey=1", + file_path + ] + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return float(result.stdout.strip()) + except Exception: + return 0.0 + def extract_audio(video_path, output_path=None): """ Extracts audio from a video file using ffmpeg. @@ -58,10 +80,19 @@ def extract_audio(video_path, output_path=None): def embed_subtitles(video_path, srt_path, output_path=None): """ Embeds subtitles into the video file (Soft Subs) and sets them as primary. + Includes strict safety checks (Size & Duration) to prevent replacing videos with corrupted files. """ if not os.path.exists(video_path) or not os.path.exists(srt_path): print("Error: Video or SRT file not found for embedding.") - return + return False + + # Capture original stats + original_size = os.path.getsize(video_path) + original_duration = get_video_duration(video_path) + + if original_size == 0: + print("Error: Source video is 0 bytes.") + return False if output_path is None: base, ext = os.path.splitext(video_path) @@ -91,8 +122,39 @@ def embed_subtitles(video_path, srt_path, output_path=None): ] if run_ffmpeg(args): - if not verify_file_not_empty(output_path): - raise Exception("FFmpeg succeeded but output video is empty.") + # --- Safety Checks --- + if not os.path.exists(output_path): + print("Error: Output file was not created.") + return False + + new_size = os.path.getsize(output_path) + new_duration = get_video_duration(output_path) + + # 1. Zero Byte Check + if new_size == 0: + print("❌ CRITICAL: Output file is 0 bytes. Deleting corrupted output.") + os.remove(output_path) + raise Exception("Embedding failed: Output is empty.") + + # 2. Significant Size Drop Check + if new_size < (original_size * 0.8): + print(f"❌ CRITICAL: Output file is significantly smaller than source!") + print(f" Original: {original_size/1024/1024:.2f} MB") + print(f" New: {new_size/1024/1024:.2f} MB") + os.remove(output_path) + raise Exception("Embedding failed: Suspicious file size reduction.") + + # 3. Duration Mismatch Check (New) + if abs(original_duration - new_duration) > 1.0: + print(f"❌ CRITICAL: Duration mismatch detected!") + print(f" Original: {original_duration:.2f}s") + print(f" New: {new_duration:.2f}s") + print(" This indicates truncated video stream. Aborting.") + os.remove(output_path) + raise Exception("Embedding failed: Duration mismatch > 1s.") + print(f"Subtitles embedded successfully: {output_path}") + return True else: print("Error: Embedding failed.") + return False diff --git a/video_transcription/ai_transcriber_v2/main.py b/video_transcription/ai_transcriber_v2/main.py index 64b65bc..3555b8f 100644 --- a/video_transcription/ai_transcriber_v2/main.py +++ b/video_transcription/ai_transcriber_v2/main.py @@ -195,23 +195,20 @@ def process_file(file_path, args, source_lang=None, loaded_model=None, service_s should_embed = False if should_embed: - embed_subtitles(file_path, final_srt_path) + success_embed = embed_subtitles(file_path, final_srt_path) - if args.delete_source: - if args.embed: - base, ext = os.path.splitext(file_path) - expected_output = f"{base}.subbed{ext}" - - if os.path.exists(expected_output): - try: - os.remove(file_path) - tracker.logger.info(f"SOURCE DELETED: {file_path}") - except OSError as e: - tracker.logger.error(f"Error deleting source: {e}") - else: - tracker.logger.error(f"SAFETY ABORT: Output '{expected_output}' not found.") + if success_embed and args.delete_source: + base, ext = os.path.splitext(file_path) + expected_output = f"{base}.subbed{ext}" + + if os.path.exists(expected_output): + try: + os.remove(file_path) + tracker.logger.info(f"SOURCE DELETED: {file_path}") + except OSError as e: + tracker.logger.error(f"Error deleting source: {e}") else: - tracker.logger.warning("SAFETY ABORT: Enable --embed to delete source.") + tracker.logger.error(f"SAFETY ABORT: Output '{expected_output}' not found.") tracker.update_step(file_path, "step_embed", "done") # 5. Cleanup