begin repair mp4 attempts
This commit is contained in:
238
repair_mp4.py
Normal file
238
repair_mp4.py
Normal file
@@ -0,0 +1,238 @@
|
||||
import os
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
import json
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
# ANSI color codes
|
||||
class Colors:
|
||||
PURPLE = '\033[95m'
|
||||
BLUE = '\033[94m'
|
||||
GREEN = '\033[92m'
|
||||
YELLOW = '\033[93m'
|
||||
RED = '\033[91m'
|
||||
ENDC = '\033[0m'
|
||||
|
||||
# Set up logging
|
||||
log_dir = "logs"
|
||||
os.makedirs(log_dir, exist_ok=True)
|
||||
log_file = os.path.join(log_dir, f"repair_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log")
|
||||
logging.basicConfig(filename=log_file, level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
|
||||
def check_file_health(input_file):
|
||||
"""Check if MP4 file has issues using ffmpeg"""
|
||||
print(f"\n{Colors.BLUE}Analyzing: {input_file}{Colors.ENDC}")
|
||||
logging.info(f"Analyzing file: {input_file}")
|
||||
|
||||
cmd = [
|
||||
'ffmpeg',
|
||||
'-v', 'error',
|
||||
'-i', str(input_file),
|
||||
'-f', 'null',
|
||||
'-'
|
||||
]
|
||||
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
|
||||
if result.stderr:
|
||||
print(f"{Colors.RED}Issues found:{Colors.ENDC}")
|
||||
for line in result.stderr.split('\n')[:10]: # Show first 10 errors
|
||||
if line.strip():
|
||||
print(f" {Colors.YELLOW}{line}{Colors.ENDC}")
|
||||
logging.warning(f"File has errors: {result.stderr[:500]}")
|
||||
return False
|
||||
else:
|
||||
print(f"{Colors.GREEN}No errors detected{Colors.ENDC}")
|
||||
logging.info("File appears healthy")
|
||||
return True
|
||||
|
||||
def get_file_info(input_file):
|
||||
"""Get basic file info"""
|
||||
cmd = [
|
||||
'ffprobe',
|
||||
'-v', 'error',
|
||||
'-show_entries', 'format=duration,size:stream=codec_name,width,height',
|
||||
'-of', 'json',
|
||||
input_file
|
||||
]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
try:
|
||||
return json.loads(result.stdout)
|
||||
except:
|
||||
return None
|
||||
|
||||
def repair_method_1_remux(input_file, output_file):
|
||||
"""Method 1: Simple remux (copy streams to new container)"""
|
||||
print(f"\n{Colors.BLUE}Trying Method 1: Remux (copy streams){Colors.ENDC}")
|
||||
logging.info("Attempting repair method 1: Remux")
|
||||
|
||||
cmd = [
|
||||
'ffmpeg',
|
||||
'-v', 'warning',
|
||||
'-i', str(input_file),
|
||||
'-c', 'copy',
|
||||
'-map', '0',
|
||||
'-y',
|
||||
str(output_file)
|
||||
]
|
||||
|
||||
try:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
|
||||
if result.returncode == 0 and Path(output_file).exists():
|
||||
print(f"{Colors.GREEN}Method 1 successful!{Colors.ENDC}")
|
||||
logging.info("Method 1 successful")
|
||||
return True
|
||||
else:
|
||||
print(f"{Colors.RED}Method 1 failed{Colors.ENDC}")
|
||||
logging.warning(f"Method 1 failed: {result.stderr[:200]}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Colors.RED}Method 1 error: {e}{Colors.ENDC}")
|
||||
logging.error(f"Method 1 error: {e}")
|
||||
return False
|
||||
|
||||
def repair_method_2_error_concealment(input_file, output_file):
|
||||
"""Method 2: Remux with error concealment"""
|
||||
print(f"\n{Colors.BLUE}Trying Method 2: Error concealment{Colors.ENDC}")
|
||||
logging.info("Attempting repair method 2: Error concealment")
|
||||
|
||||
cmd = [
|
||||
'ffmpeg',
|
||||
'-v', 'warning',
|
||||
'-err_detect', 'ignore_err',
|
||||
'-i', str(input_file),
|
||||
'-c', 'copy',
|
||||
'-map', '0',
|
||||
'-fflags', '+genpts+igndts',
|
||||
'-y',
|
||||
str(output_file)
|
||||
]
|
||||
|
||||
try:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
|
||||
if result.returncode == 0 and Path(output_file).exists():
|
||||
print(f"{Colors.GREEN}Method 2 successful!{Colors.ENDC}")
|
||||
logging.info("Method 2 successful")
|
||||
return True
|
||||
else:
|
||||
print(f"{Colors.RED}Method 2 failed{Colors.ENDC}")
|
||||
logging.warning(f"Method 2 failed: {result.stderr[:200]}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Colors.RED}Method 2 error: {e}{Colors.ENDC}")
|
||||
logging.error(f"Method 2 error: {e}")
|
||||
return False
|
||||
|
||||
def repair_method_3_force_keyframes(input_file, output_file):
|
||||
"""Method 3: Force keyframe processing"""
|
||||
print(f"\n{Colors.BLUE}Trying Method 3: Force keyframe processing{Colors.ENDC}")
|
||||
logging.info("Attempting repair method 3: Force keyframe processing")
|
||||
|
||||
cmd = [
|
||||
'ffmpeg',
|
||||
'-v', 'warning',
|
||||
'-fflags', '+discardcorrupt+genpts',
|
||||
'-i', str(input_file),
|
||||
'-c', 'copy',
|
||||
'-map', '0',
|
||||
'-avoid_negative_ts', 'make_zero',
|
||||
'-y',
|
||||
str(output_file)
|
||||
]
|
||||
|
||||
try:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
|
||||
if result.returncode == 0 and Path(output_file).exists():
|
||||
print(f"{Colors.GREEN}Method 3 successful!{Colors.ENDC}")
|
||||
logging.info("Method 3 successful")
|
||||
return True
|
||||
else:
|
||||
print(f"{Colors.RED}Method 3 failed{Colors.ENDC}")
|
||||
logging.warning(f"Method 3 failed: {result.stderr[:200]}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Colors.RED}Method 3 error: {e}{Colors.ENDC}")
|
||||
logging.error(f"Method 3 error: {e}")
|
||||
return False
|
||||
|
||||
def repair_file(input_file, output_dir):
|
||||
"""Attempt to repair a damaged MP4 file"""
|
||||
input_path = Path(input_file)
|
||||
output_path = Path(output_dir) / f"{input_path.stem}_repaired{input_path.suffix}"
|
||||
|
||||
print(f"\n{Colors.PURPLE}{'='*60}{Colors.ENDC}")
|
||||
print(f"{Colors.PURPLE}Processing: {input_path.name}{Colors.ENDC}")
|
||||
print(f"{Colors.PURPLE}{'='*60}{Colors.ENDC}")
|
||||
|
||||
# Skip if already repaired
|
||||
if output_path.exists():
|
||||
print(f"{Colors.YELLOW}Already repaired: {output_path.name}{Colors.ENDC}")
|
||||
logging.info(f"Skipping {input_path} - already repaired")
|
||||
return
|
||||
|
||||
# Check file health first
|
||||
is_healthy = check_file_health(str(input_path))
|
||||
|
||||
if is_healthy:
|
||||
print(f"{Colors.GREEN}File appears healthy, no repair needed{Colors.ENDC}")
|
||||
logging.info("File is healthy, skipping")
|
||||
return
|
||||
|
||||
# Try repair methods in sequence
|
||||
methods = [
|
||||
repair_method_1_remux,
|
||||
repair_method_2_error_concealment,
|
||||
repair_method_3_force_keyframes
|
||||
]
|
||||
|
||||
for method in methods:
|
||||
if method(str(input_path), str(output_path)):
|
||||
# Verify the repaired file
|
||||
print(f"\n{Colors.BLUE}Verifying repaired file...{Colors.ENDC}")
|
||||
if check_file_health(str(output_path)):
|
||||
print(f"{Colors.GREEN}✓ Repair successful and verified!{Colors.ENDC}")
|
||||
logging.info(f"Successfully repaired: {output_path}")
|
||||
|
||||
# Show size comparison
|
||||
original_size = input_path.stat().st_size / (1024*1024)
|
||||
repaired_size = output_path.stat().st_size / (1024*1024)
|
||||
print(f"Original: {original_size:.2f} MB → Repaired: {repaired_size:.2f} MB")
|
||||
logging.info(f"Size: {original_size:.2f} MB → {repaired_size:.2f} MB")
|
||||
return
|
||||
else:
|
||||
print(f"{Colors.YELLOW}Repair completed but file still has issues{Colors.ENDC}")
|
||||
logging.warning("Repaired file still has issues")
|
||||
return
|
||||
|
||||
print(f"{Colors.RED}All repair methods failed{Colors.ENDC}")
|
||||
logging.error(f"Failed to repair: {input_path}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
input_dir = "input"
|
||||
output_dir = "output"
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
# Get list of MP4 files
|
||||
files = [f for f in os.listdir(input_dir) if f.endswith(('.mp4', '.DVR.mp4'))]
|
||||
total_files = len(files)
|
||||
|
||||
if total_files == 0:
|
||||
print(f"{Colors.YELLOW}No MP4 files found in input directory{Colors.ENDC}")
|
||||
logging.info("No files to process")
|
||||
else:
|
||||
print(f"\n{Colors.BLUE}Found {total_files} MP4 files to check{Colors.ENDC}")
|
||||
logging.info(f"Found {total_files} files to process")
|
||||
|
||||
for i, file in enumerate(files, 1):
|
||||
input_file = os.path.join(input_dir, file)
|
||||
print(f"\n{Colors.BLUE}[{i}/{total_files}]{Colors.ENDC}")
|
||||
repair_file(input_file, output_dir)
|
||||
|
||||
print(f"\n{Colors.GREEN}{'='*60}{Colors.ENDC}")
|
||||
print(f"{Colors.GREEN}Repair process complete!{Colors.ENDC}")
|
||||
print(f"{Colors.GREEN}Check the output directory for repaired files{Colors.ENDC}")
|
||||
print(f"{Colors.GREEN}Log file: {log_file}{Colors.ENDC}")
|
||||
print(f"{Colors.GREEN}{'='*60}{Colors.ENDC}")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user