added diag script
This commit is contained in:
Binary file not shown.
@@ -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()
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import json
|
||||||
from utils import verify_file_not_empty
|
from utils import verify_file_not_empty
|
||||||
|
|
||||||
def run_ffmpeg(args):
|
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.")
|
print("Error: 'ffmpeg' command not found. Please ensure it is installed on your host system.")
|
||||||
return False
|
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):
|
def extract_audio(video_path, output_path=None):
|
||||||
"""
|
"""
|
||||||
Extracts audio from a video file using ffmpeg.
|
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):
|
def embed_subtitles(video_path, srt_path, output_path=None):
|
||||||
"""
|
"""
|
||||||
Embeds subtitles into the video file (Soft Subs) and sets them as primary.
|
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):
|
if not os.path.exists(video_path) or not os.path.exists(srt_path):
|
||||||
print("Error: Video or SRT file not found for embedding.")
|
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:
|
if output_path is None:
|
||||||
base, ext = os.path.splitext(video_path)
|
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 run_ffmpeg(args):
|
||||||
if not verify_file_not_empty(output_path):
|
# --- Safety Checks ---
|
||||||
raise Exception("FFmpeg succeeded but output video is empty.")
|
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}")
|
print(f"Subtitles embedded successfully: {output_path}")
|
||||||
|
return True
|
||||||
else:
|
else:
|
||||||
print("Error: Embedding failed.")
|
print("Error: Embedding failed.")
|
||||||
|
return False
|
||||||
|
|||||||
@@ -195,10 +195,9 @@ def process_file(file_path, args, source_lang=None, loaded_model=None, service_s
|
|||||||
should_embed = False
|
should_embed = False
|
||||||
|
|
||||||
if should_embed:
|
if should_embed:
|
||||||
embed_subtitles(file_path, final_srt_path)
|
success_embed = embed_subtitles(file_path, final_srt_path)
|
||||||
|
|
||||||
if args.delete_source:
|
if success_embed and args.delete_source:
|
||||||
if args.embed:
|
|
||||||
base, ext = os.path.splitext(file_path)
|
base, ext = os.path.splitext(file_path)
|
||||||
expected_output = f"{base}.subbed{ext}"
|
expected_output = f"{base}.subbed{ext}"
|
||||||
|
|
||||||
@@ -210,8 +209,6 @@ def process_file(file_path, args, source_lang=None, loaded_model=None, service_s
|
|||||||
tracker.logger.error(f"Error deleting source: {e}")
|
tracker.logger.error(f"Error deleting source: {e}")
|
||||||
else:
|
else:
|
||||||
tracker.logger.error(f"SAFETY ABORT: Output '{expected_output}' not found.")
|
tracker.logger.error(f"SAFETY ABORT: Output '{expected_output}' not found.")
|
||||||
else:
|
|
||||||
tracker.logger.warning("SAFETY ABORT: Enable --embed to delete source.")
|
|
||||||
tracker.update_step(file_path, "step_embed", "done")
|
tracker.update_step(file_path, "step_embed", "done")
|
||||||
|
|
||||||
# 5. Cleanup
|
# 5. Cleanup
|
||||||
|
|||||||
Reference in New Issue
Block a user