ffmpeg error handling
This commit is contained in:
3974
.specstory/history/2026-01-17_02-53Z-python-error-logging-and-i-o.md
Normal file
3974
.specstory/history/2026-01-17_02-53Z-python-error-logging-and-i-o.md
Normal file
File diff suppressed because it is too large
Load Diff
163
encode_VOD.py
163
encode_VOD.py
@@ -27,18 +27,52 @@ def get_gpu_selection():
|
|||||||
return gpu
|
return gpu
|
||||||
print(f"{Colors.RED}Invalid selection. Please try again.{Colors.ENDC}")
|
print(f"{Colors.RED}Invalid selection. Please try again.{Colors.ENDC}")
|
||||||
|
|
||||||
|
# Custom file handler that silently handles I/O errors (for network shares)
|
||||||
|
class SafeFileHandler(logging.FileHandler):
|
||||||
|
"""File handler that silently handles I/O errors during flush"""
|
||||||
|
def flush(self):
|
||||||
|
"""Override flush to silently handle I/O errors"""
|
||||||
|
try:
|
||||||
|
super().flush()
|
||||||
|
except (OSError, IOError):
|
||||||
|
# Silently ignore I/O errors (network share issues)
|
||||||
|
pass
|
||||||
|
except Exception:
|
||||||
|
# Silently ignore all other errors during flush
|
||||||
|
pass
|
||||||
|
|
||||||
|
def emit(self, record):
|
||||||
|
"""Override emit to handle errors gracefully"""
|
||||||
|
try:
|
||||||
|
super().emit(record)
|
||||||
|
except (OSError, IOError):
|
||||||
|
# Silently ignore I/O errors - we'll fall back to console output
|
||||||
|
self.handleError(record)
|
||||||
|
except Exception:
|
||||||
|
# Handle other errors
|
||||||
|
self.handleError(record)
|
||||||
|
|
||||||
|
def handleError(self, record):
|
||||||
|
"""Override to prevent error messages from being printed"""
|
||||||
|
# Don't print "--- Logging error ---" messages
|
||||||
|
pass
|
||||||
|
|
||||||
# Set up logging
|
# Set up logging
|
||||||
log_dir = "logs"
|
log_dir = "logs"
|
||||||
os.makedirs(log_dir, exist_ok=True)
|
os.makedirs(log_dir, exist_ok=True)
|
||||||
log_file = os.path.join(log_dir, f"encode_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log")
|
log_file = os.path.join(log_dir, f"encode_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log")
|
||||||
|
|
||||||
# Configure logging with Windows-safe settings
|
# Configure logging with custom handler that handles network share errors
|
||||||
logging.basicConfig(
|
handler = SafeFileHandler(log_file, mode='w', encoding='utf-8')
|
||||||
filename=log_file,
|
handler.setLevel(logging.INFO)
|
||||||
level=logging.INFO,
|
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
|
||||||
format='%(asctime)s - %(levelname)s - %(message)s',
|
handler.setFormatter(formatter)
|
||||||
filemode='w' # Explicitly set write mode
|
|
||||||
)
|
logger = logging.getLogger()
|
||||||
|
logger.setLevel(logging.INFO)
|
||||||
|
logger.addHandler(handler)
|
||||||
|
# Remove default handlers to avoid duplicate output
|
||||||
|
logger.handlers = [handler]
|
||||||
|
|
||||||
def get_file_info(input_file):
|
def get_file_info(input_file):
|
||||||
cmd = [
|
cmd = [
|
||||||
@@ -75,6 +109,58 @@ def format_size(size_bytes):
|
|||||||
size_bytes /= 1024
|
size_bytes /= 1024
|
||||||
return f"{size_bytes:.2f} TB"
|
return f"{size_bytes:.2f} TB"
|
||||||
|
|
||||||
|
def safe_log_info(message, print_msg=None):
|
||||||
|
"""Safely log info message, ensuring console output even if logging fails"""
|
||||||
|
try:
|
||||||
|
logging.info(message)
|
||||||
|
except (OSError, IOError) as e:
|
||||||
|
# Logging failed (likely network share issue) - print to console
|
||||||
|
if print_msg is None:
|
||||||
|
print(f"{Colors.YELLOW}[Logging failed: {e}] {message}{Colors.ENDC}")
|
||||||
|
else:
|
||||||
|
print(print_msg)
|
||||||
|
print(f"{Colors.YELLOW}[Logging failed: {e}]{Colors.ENDC}")
|
||||||
|
return
|
||||||
|
except Exception as e:
|
||||||
|
# Other logging errors
|
||||||
|
if print_msg is None:
|
||||||
|
print(f"{Colors.YELLOW}[Logging error: {e}] {message}{Colors.ENDC}")
|
||||||
|
else:
|
||||||
|
print(print_msg)
|
||||||
|
print(f"{Colors.YELLOW}[Logging error: {e}]{Colors.ENDC}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Always print to console if message provided
|
||||||
|
if print_msg is not None:
|
||||||
|
print(print_msg)
|
||||||
|
|
||||||
|
def safe_log_error(message, print_msg=None):
|
||||||
|
"""Safely log error message, ensuring console output even if logging fails"""
|
||||||
|
try:
|
||||||
|
logging.error(message)
|
||||||
|
except (OSError, IOError) as e:
|
||||||
|
# Logging failed (likely network share issue) - print to console
|
||||||
|
if print_msg is None:
|
||||||
|
print(f"{Colors.RED}[Logging failed: {e}] {message}{Colors.ENDC}")
|
||||||
|
else:
|
||||||
|
print(print_msg)
|
||||||
|
print(f"{Colors.RED}[Logging failed: {e}]{Colors.ENDC}")
|
||||||
|
return
|
||||||
|
except Exception as e:
|
||||||
|
# Other logging errors
|
||||||
|
if print_msg is None:
|
||||||
|
print(f"{Colors.RED}[Logging error: {e}] {message}{Colors.ENDC}")
|
||||||
|
else:
|
||||||
|
print(print_msg)
|
||||||
|
print(f"{Colors.RED}[Logging error: {e}]{Colors.ENDC}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Always print to console if message provided
|
||||||
|
if print_msg is not None:
|
||||||
|
print(print_msg)
|
||||||
|
else:
|
||||||
|
print(f"{Colors.RED}{message}{Colors.ENDC}")
|
||||||
|
|
||||||
def encode_dvr(input_file, output_dir, gpu):
|
def encode_dvr(input_file, output_dir, gpu):
|
||||||
input_path = Path(input_file)
|
input_path = Path(input_file)
|
||||||
output_path = Path(output_dir) / f"{input_path.stem}{input_path.suffix}"
|
output_path = Path(output_dir) / f"{input_path.stem}{input_path.suffix}"
|
||||||
@@ -84,9 +170,9 @@ def encode_dvr(input_file, output_dir, gpu):
|
|||||||
input_size = int(file_info['format']['size'])
|
input_size = int(file_info['format']['size'])
|
||||||
duration = float(file_info['format']['duration'])
|
duration = float(file_info['format']['duration'])
|
||||||
|
|
||||||
logging.info(f"Processing file: {input_path}")
|
safe_log_info(f"Processing file: {input_path}")
|
||||||
logging.info(f"Input size: {format_size(input_size)}")
|
safe_log_info(f"Input size: {format_size(input_size)}")
|
||||||
logging.info(f"Duration: {duration:.2f} seconds")
|
safe_log_info(f"Duration: {duration:.2f} seconds")
|
||||||
|
|
||||||
print(f"\n{Colors.BLUE}Processing file: {input_path}{Colors.ENDC}")
|
print(f"\n{Colors.BLUE}Processing file: {input_path}{Colors.ENDC}")
|
||||||
print(f"Input size: {format_size(input_size)}")
|
print(f"Input size: {format_size(input_size)}")
|
||||||
@@ -95,21 +181,21 @@ def encode_dvr(input_file, output_dir, gpu):
|
|||||||
# Log stream information
|
# Log stream information
|
||||||
for i, stream in enumerate(file_info.get('streams', [])):
|
for i, stream in enumerate(file_info.get('streams', [])):
|
||||||
stream_type = 'Video' if stream.get('codec_name', '').startswith('h') else 'Audio'
|
stream_type = 'Video' if stream.get('codec_name', '').startswith('h') else 'Audio'
|
||||||
logging.info(f"Stream {i} ({stream_type}):")
|
safe_log_info(f"Stream {i} ({stream_type}):")
|
||||||
for key, value in stream.items():
|
for key, value in stream.items():
|
||||||
logging.info(f" {key}: {value}")
|
safe_log_info(f" {key}: {value}")
|
||||||
|
|
||||||
# Skip if output file already exists
|
# Skip if output file already exists
|
||||||
if output_path.exists():
|
if output_path.exists():
|
||||||
output_size = output_path.stat().st_size
|
output_size = output_path.stat().st_size
|
||||||
logging.info(f"Skipping {input_path} - output already exists: {output_path}")
|
safe_log_info(f"Skipping {input_path} - output already exists: {output_path}")
|
||||||
logging.info(f"Output size: {format_size(output_size)}")
|
safe_log_info(f"Output size: {format_size(output_size)}")
|
||||||
print(f"{Colors.YELLOW}Skipping {input_path} - output already exists{Colors.ENDC}")
|
print(f"{Colors.YELLOW}Skipping {input_path} - output already exists{Colors.ENDC}")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Get audio labels
|
# Get audio labels
|
||||||
audio_labels = get_audio_labels(str(input_path))
|
audio_labels = get_audio_labels(str(input_path))
|
||||||
logging.info(f"Audio labels: {audio_labels}")
|
safe_log_info(f"Audio labels: {audio_labels}")
|
||||||
|
|
||||||
# FFmpeg command with NVIDIA HEVC encoder and maximum quality
|
# FFmpeg command with NVIDIA HEVC encoder and maximum quality
|
||||||
cmd = [
|
cmd = [
|
||||||
@@ -156,13 +242,16 @@ def encode_dvr(input_file, output_dir, gpu):
|
|||||||
text = line.strip()
|
text = line.strip()
|
||||||
try:
|
try:
|
||||||
if text.startswith('frame=') or ' fps=' in text:
|
if text.startswith('frame=') or ' fps=' in text:
|
||||||
logging.info(f"Progress: {text}")
|
safe_log_info(f"Progress: {text}", f"{Colors.PURPLE}Progress: {text}{Colors.ENDC}")
|
||||||
print(f"{Colors.PURPLE}Progress: {text}{Colors.ENDC}")
|
|
||||||
else:
|
else:
|
||||||
logging.info(f"FFmpeg: {text}")
|
safe_log_info(f"FFmpeg: {text}", f"{Colors.GREEN}FFmpeg: {text}{Colors.ENDC}")
|
||||||
print(f"{Colors.GREEN}FFmpeg: {text}{Colors.ENDC}")
|
except (OSError, IOError) as e:
|
||||||
except (OSError, IOError):
|
# I/O error reading from pipe - log it
|
||||||
pass
|
safe_log_error(f"I/O error reading FFmpeg output: {e}")
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
# Unexpected error
|
||||||
|
safe_log_error(f"Unexpected error processing FFmpeg output: {e}")
|
||||||
|
|
||||||
process.wait()
|
process.wait()
|
||||||
|
|
||||||
@@ -172,23 +261,27 @@ def encode_dvr(input_file, output_dir, gpu):
|
|||||||
output_size = int(output_info['format']['size'])
|
output_size = int(output_info['format']['size'])
|
||||||
compression_ratio = input_size / output_size if output_size > 0 else 0
|
compression_ratio = input_size / output_size if output_size > 0 else 0
|
||||||
|
|
||||||
logging.info(f"Successfully encoded: {output_path}")
|
safe_log_info(f"Successfully encoded: {output_path}", f"{Colors.GREEN}Successfully encoded: {output_path}{Colors.ENDC}")
|
||||||
logging.info(f"Output size: {format_size(output_size)}")
|
safe_log_info(f"Output size: {format_size(output_size)}")
|
||||||
logging.info(f"Compression ratio: {compression_ratio:.2f}x")
|
safe_log_info(f"Compression ratio: {compression_ratio:.2f}x", f"Compression ratio: {compression_ratio:.2f}x")
|
||||||
print(f"{Colors.GREEN}Successfully encoded: {output_path}{Colors.ENDC}")
|
|
||||||
print(f"Compression ratio: {compression_ratio:.2f}x")
|
|
||||||
else:
|
else:
|
||||||
logging.error(f"FFmpeg process failed with return code {process.returncode}")
|
# Convert Windows error code to signed integer if needed
|
||||||
print(f"{Colors.RED}FFmpeg process failed with return code {process.returncode}{Colors.ENDC}")
|
return_code = process.returncode
|
||||||
|
if return_code > 2147483647: # If it's a large unsigned int, convert to signed
|
||||||
|
return_code = return_code - 4294967296
|
||||||
|
safe_log_error(f"FFmpeg process failed with return code {return_code}",
|
||||||
|
f"{Colors.RED}FFmpeg process failed with return code {return_code}{Colors.ENDC}")
|
||||||
|
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
logging.error(f"Error encoding {input_path}: {e}")
|
safe_log_error(f"Error encoding {input_path}: {e}", f"{Colors.RED}Error encoding {input_path}: {e}{Colors.ENDC}")
|
||||||
print(f"{Colors.RED}Error encoding {input_path}: {e}{Colors.ENDC}")
|
except Exception as e:
|
||||||
|
safe_log_error(f"Unexpected error encoding {input_path}: {type(e).__name__}: {e}",
|
||||||
|
f"{Colors.RED}Unexpected error encoding {input_path}: {e}{Colors.ENDC}")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# Get GPU selection
|
# Get GPU selection
|
||||||
gpu = get_gpu_selection()
|
gpu = get_gpu_selection()
|
||||||
logging.info(f"Selected GPU: {gpu}")
|
safe_log_info(f"Selected GPU: {gpu}")
|
||||||
|
|
||||||
input_dir = "input"
|
input_dir = "input"
|
||||||
output_dir = "output"
|
output_dir = "output"
|
||||||
@@ -199,14 +292,12 @@ if __name__ == "__main__":
|
|||||||
total_files = len(files)
|
total_files = len(files)
|
||||||
|
|
||||||
if total_files == 0:
|
if total_files == 0:
|
||||||
logging.info("No files to process in input directory")
|
safe_log_info("No files to process in input directory", f"{Colors.YELLOW}No files to process in input directory{Colors.ENDC}")
|
||||||
print(f"{Colors.YELLOW}No files to process in input directory{Colors.ENDC}")
|
|
||||||
else:
|
else:
|
||||||
logging.info(f"Found {total_files} files to process")
|
safe_log_info(f"Found {total_files} files to process", f"{Colors.BLUE}Found {total_files} files to process{Colors.ENDC}")
|
||||||
print(f"{Colors.BLUE}Found {total_files} files to process{Colors.ENDC}")
|
|
||||||
|
|
||||||
for i, file in enumerate(files, 1):
|
for i, file in enumerate(files, 1):
|
||||||
input_file = os.path.join(input_dir, file)
|
input_file = os.path.join(input_dir, file)
|
||||||
logging.info(f"Processing file {i}/{total_files}: {file}")
|
safe_log_info(f"Processing file {i}/{total_files}: {file}")
|
||||||
print(f"\n{Colors.BLUE}Processing file {i}/{total_files}: {file}{Colors.ENDC}")
|
print(f"\n{Colors.BLUE}Processing file {i}/{total_files}: {file}{Colors.ENDC}")
|
||||||
encode_dvr(input_file, output_dir, gpu)
|
encode_dvr(input_file, output_dir, gpu)
|
||||||
@@ -26,11 +26,52 @@ def get_gpu_selection():
|
|||||||
return gpu
|
return gpu
|
||||||
print(f"{Colors.RED}Invalid selection. Please try again.{Colors.ENDC}")
|
print(f"{Colors.RED}Invalid selection. Please try again.{Colors.ENDC}")
|
||||||
|
|
||||||
|
# Custom file handler that silently handles I/O errors (for network shares)
|
||||||
|
class SafeFileHandler(logging.FileHandler):
|
||||||
|
"""File handler that silently handles I/O errors during flush"""
|
||||||
|
def flush(self):
|
||||||
|
"""Override flush to silently handle I/O errors"""
|
||||||
|
try:
|
||||||
|
super().flush()
|
||||||
|
except (OSError, IOError):
|
||||||
|
# Silently ignore I/O errors (network share issues)
|
||||||
|
pass
|
||||||
|
except Exception:
|
||||||
|
# Silently ignore all other errors during flush
|
||||||
|
pass
|
||||||
|
|
||||||
|
def emit(self, record):
|
||||||
|
"""Override emit to handle errors gracefully"""
|
||||||
|
try:
|
||||||
|
super().emit(record)
|
||||||
|
except (OSError, IOError):
|
||||||
|
# Silently ignore I/O errors - we'll fall back to console output
|
||||||
|
self.handleError(record)
|
||||||
|
except Exception:
|
||||||
|
# Handle other errors
|
||||||
|
self.handleError(record)
|
||||||
|
|
||||||
|
def handleError(self, record):
|
||||||
|
"""Override to prevent error messages from being printed"""
|
||||||
|
# Don't print "--- Logging error ---" messages
|
||||||
|
pass
|
||||||
|
|
||||||
# Set up logging
|
# Set up logging
|
||||||
log_dir = "logs"
|
log_dir = "logs"
|
||||||
os.makedirs(log_dir, exist_ok=True)
|
os.makedirs(log_dir, exist_ok=True)
|
||||||
log_file = os.path.join(log_dir, f"encode_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log")
|
log_file = os.path.join(log_dir, f"encode_{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')
|
|
||||||
|
# Configure logging with custom handler that handles network share errors
|
||||||
|
handler = SafeFileHandler(log_file, mode='w', encoding='utf-8')
|
||||||
|
handler.setLevel(logging.INFO)
|
||||||
|
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
|
||||||
|
handler.setFormatter(formatter)
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
logger.setLevel(logging.INFO)
|
||||||
|
logger.addHandler(handler)
|
||||||
|
# Remove default handlers to avoid duplicate output
|
||||||
|
logger.handlers = [handler]
|
||||||
|
|
||||||
def get_file_info(input_file):
|
def get_file_info(input_file):
|
||||||
cmd = [
|
cmd = [
|
||||||
@@ -67,6 +108,58 @@ def format_size(size_bytes):
|
|||||||
size_bytes /= 1024
|
size_bytes /= 1024
|
||||||
return f"{size_bytes:.2f} TB"
|
return f"{size_bytes:.2f} TB"
|
||||||
|
|
||||||
|
def safe_log_info(message, print_msg=None):
|
||||||
|
"""Safely log info message, ensuring console output even if logging fails"""
|
||||||
|
try:
|
||||||
|
logging.info(message)
|
||||||
|
except (OSError, IOError) as e:
|
||||||
|
# Logging failed (likely network share issue) - print to console
|
||||||
|
if print_msg is None:
|
||||||
|
print(f"{Colors.YELLOW}[Logging failed: {e}] {message}{Colors.ENDC}")
|
||||||
|
else:
|
||||||
|
print(print_msg)
|
||||||
|
print(f"{Colors.YELLOW}[Logging failed: {e}]{Colors.ENDC}")
|
||||||
|
return
|
||||||
|
except Exception as e:
|
||||||
|
# Other logging errors
|
||||||
|
if print_msg is None:
|
||||||
|
print(f"{Colors.YELLOW}[Logging error: {e}] {message}{Colors.ENDC}")
|
||||||
|
else:
|
||||||
|
print(print_msg)
|
||||||
|
print(f"{Colors.YELLOW}[Logging error: {e}]{Colors.ENDC}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Always print to console if message provided
|
||||||
|
if print_msg is not None:
|
||||||
|
print(print_msg)
|
||||||
|
|
||||||
|
def safe_log_error(message, print_msg=None):
|
||||||
|
"""Safely log error message, ensuring console output even if logging fails"""
|
||||||
|
try:
|
||||||
|
logging.error(message)
|
||||||
|
except (OSError, IOError) as e:
|
||||||
|
# Logging failed (likely network share issue) - print to console
|
||||||
|
if print_msg is None:
|
||||||
|
print(f"{Colors.RED}[Logging failed: {e}] {message}{Colors.ENDC}")
|
||||||
|
else:
|
||||||
|
print(print_msg)
|
||||||
|
print(f"{Colors.RED}[Logging failed: {e}]{Colors.ENDC}")
|
||||||
|
return
|
||||||
|
except Exception as e:
|
||||||
|
# Other logging errors
|
||||||
|
if print_msg is None:
|
||||||
|
print(f"{Colors.RED}[Logging error: {e}] {message}{Colors.ENDC}")
|
||||||
|
else:
|
||||||
|
print(print_msg)
|
||||||
|
print(f"{Colors.RED}[Logging error: {e}]{Colors.ENDC}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Always print to console if message provided
|
||||||
|
if print_msg is not None:
|
||||||
|
print(print_msg)
|
||||||
|
else:
|
||||||
|
print(f"{Colors.RED}{message}{Colors.ENDC}")
|
||||||
|
|
||||||
def encode_dvr(input_file, output_dir, gpu):
|
def encode_dvr(input_file, output_dir, gpu):
|
||||||
input_path = Path(input_file)
|
input_path = Path(input_file)
|
||||||
output_path = Path(output_dir) / f"{input_path.stem}{input_path.suffix}"
|
output_path = Path(output_dir) / f"{input_path.stem}{input_path.suffix}"
|
||||||
@@ -76,9 +169,9 @@ def encode_dvr(input_file, output_dir, gpu):
|
|||||||
input_size = int(file_info['format']['size'])
|
input_size = int(file_info['format']['size'])
|
||||||
duration = float(file_info['format']['duration'])
|
duration = float(file_info['format']['duration'])
|
||||||
|
|
||||||
logging.info(f"Processing file: {input_path}")
|
safe_log_info(f"Processing file: {input_path}")
|
||||||
logging.info(f"Input size: {format_size(input_size)}")
|
safe_log_info(f"Input size: {format_size(input_size)}")
|
||||||
logging.info(f"Duration: {duration:.2f} seconds")
|
safe_log_info(f"Duration: {duration:.2f} seconds")
|
||||||
|
|
||||||
print(f"\n{Colors.BLUE}Processing file: {input_path}{Colors.ENDC}")
|
print(f"\n{Colors.BLUE}Processing file: {input_path}{Colors.ENDC}")
|
||||||
print(f"Input size: {format_size(input_size)}")
|
print(f"Input size: {format_size(input_size)}")
|
||||||
@@ -87,21 +180,21 @@ def encode_dvr(input_file, output_dir, gpu):
|
|||||||
# Log stream information
|
# Log stream information
|
||||||
for i, stream in enumerate(file_info.get('streams', [])):
|
for i, stream in enumerate(file_info.get('streams', [])):
|
||||||
stream_type = 'Video' if stream.get('codec_name', '').startswith('h') else 'Audio'
|
stream_type = 'Video' if stream.get('codec_name', '').startswith('h') else 'Audio'
|
||||||
logging.info(f"Stream {i} ({stream_type}):")
|
safe_log_info(f"Stream {i} ({stream_type}):")
|
||||||
for key, value in stream.items():
|
for key, value in stream.items():
|
||||||
logging.info(f" {key}: {value}")
|
safe_log_info(f" {key}: {value}")
|
||||||
|
|
||||||
# Skip if output file already exists
|
# Skip if output file already exists
|
||||||
if output_path.exists():
|
if output_path.exists():
|
||||||
output_size = output_path.stat().st_size
|
output_size = output_path.stat().st_size
|
||||||
logging.info(f"Skipping {input_path} - output already exists: {output_path}")
|
safe_log_info(f"Skipping {input_path} - output already exists: {output_path}")
|
||||||
logging.info(f"Output size: {format_size(output_size)}")
|
safe_log_info(f"Output size: {format_size(output_size)}")
|
||||||
print(f"{Colors.YELLOW}Skipping {input_path} - output already exists{Colors.ENDC}")
|
print(f"{Colors.YELLOW}Skipping {input_path} - output already exists{Colors.ENDC}")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Get audio labels
|
# Get audio labels
|
||||||
audio_labels = get_audio_labels(str(input_path))
|
audio_labels = get_audio_labels(str(input_path))
|
||||||
logging.info(f"Audio labels: {audio_labels}")
|
safe_log_info(f"Audio labels: {audio_labels}")
|
||||||
|
|
||||||
# FFmpeg command with NVIDIA HEVC encoder and maximum quality
|
# FFmpeg command with NVIDIA HEVC encoder and maximum quality
|
||||||
cmd = [
|
cmd = [
|
||||||
@@ -138,12 +231,23 @@ def encode_dvr(input_file, output_dir, gpu):
|
|||||||
|
|
||||||
# Log FFmpeg output in real-time
|
# Log FFmpeg output in real-time
|
||||||
while True:
|
while True:
|
||||||
output = process.stderr.readline()
|
try:
|
||||||
if output == '' and process.poll() is not None:
|
output = process.stderr.readline()
|
||||||
|
if output == '' and process.poll() is not None:
|
||||||
|
break
|
||||||
|
if output:
|
||||||
|
text = output.strip()
|
||||||
|
if text.startswith('frame=') or ' fps=' in text:
|
||||||
|
safe_log_info(f"Progress: {text}", f"{Colors.PURPLE}Progress: {text}{Colors.ENDC}")
|
||||||
|
else:
|
||||||
|
safe_log_info(f"FFmpeg: {text}", f"{Colors.GREEN}FFmpeg: {text}{Colors.ENDC}")
|
||||||
|
except (OSError, IOError) as e:
|
||||||
|
# I/O error reading from pipe - log it
|
||||||
|
safe_log_error(f"I/O error reading FFmpeg output: {e}")
|
||||||
break
|
break
|
||||||
if output:
|
except Exception as e:
|
||||||
logging.info(f"FFmpeg: {output.strip()}")
|
# Unexpected error
|
||||||
print(f"{Colors.PURPLE}FFmpeg: {output.strip()}{Colors.ENDC}")
|
safe_log_error(f"Unexpected error processing FFmpeg output: {e}")
|
||||||
|
|
||||||
if process.returncode == 0:
|
if process.returncode == 0:
|
||||||
# Get output file info
|
# Get output file info
|
||||||
@@ -151,23 +255,27 @@ def encode_dvr(input_file, output_dir, gpu):
|
|||||||
output_size = int(output_info['format']['size'])
|
output_size = int(output_info['format']['size'])
|
||||||
compression_ratio = input_size / output_size if output_size > 0 else 0
|
compression_ratio = input_size / output_size if output_size > 0 else 0
|
||||||
|
|
||||||
logging.info(f"Successfully encoded: {output_path}")
|
safe_log_info(f"Successfully encoded: {output_path}", f"{Colors.GREEN}Successfully encoded: {output_path}{Colors.ENDC}")
|
||||||
logging.info(f"Output size: {format_size(output_size)}")
|
safe_log_info(f"Output size: {format_size(output_size)}")
|
||||||
logging.info(f"Compression ratio: {compression_ratio:.2f}x")
|
safe_log_info(f"Compression ratio: {compression_ratio:.2f}x", f"Compression ratio: {compression_ratio:.2f}x")
|
||||||
print(f"{Colors.GREEN}Successfully encoded: {output_path}{Colors.ENDC}")
|
|
||||||
print(f"Compression ratio: {compression_ratio:.2f}x")
|
|
||||||
else:
|
else:
|
||||||
logging.error(f"FFmpeg process failed with return code {process.returncode}")
|
# Convert Windows error code to signed integer if needed
|
||||||
print(f"{Colors.RED}FFmpeg process failed with return code {process.returncode}{Colors.ENDC}")
|
return_code = process.returncode
|
||||||
|
if return_code > 2147483647: # If it's a large unsigned int, convert to signed
|
||||||
|
return_code = return_code - 4294967296
|
||||||
|
safe_log_error(f"FFmpeg process failed with return code {return_code}",
|
||||||
|
f"{Colors.RED}FFmpeg process failed with return code {return_code}{Colors.ENDC}")
|
||||||
|
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
logging.error(f"Error encoding {input_path}: {e}")
|
safe_log_error(f"Error encoding {input_path}: {e}", f"{Colors.RED}Error encoding {input_path}: {e}{Colors.ENDC}")
|
||||||
print(f"{Colors.RED}Error encoding {input_path}: {e}{Colors.ENDC}")
|
except Exception as e:
|
||||||
|
safe_log_error(f"Unexpected error encoding {input_path}: {type(e).__name__}: {e}",
|
||||||
|
f"{Colors.RED}Unexpected error encoding {input_path}: {e}{Colors.ENDC}")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# Get GPU selection
|
# Get GPU selection
|
||||||
gpu = get_gpu_selection()
|
gpu = get_gpu_selection()
|
||||||
logging.info(f"Selected GPU: {gpu}")
|
safe_log_info(f"Selected GPU: {gpu}")
|
||||||
|
|
||||||
input_dir = "input"
|
input_dir = "input"
|
||||||
output_dir = "output"
|
output_dir = "output"
|
||||||
@@ -178,14 +286,12 @@ if __name__ == "__main__":
|
|||||||
total_files = len(files)
|
total_files = len(files)
|
||||||
|
|
||||||
if total_files == 0:
|
if total_files == 0:
|
||||||
logging.info("No files to process in input directory")
|
safe_log_info("No files to process in input directory", f"{Colors.YELLOW}No files to process in input directory{Colors.ENDC}")
|
||||||
print(f"{Colors.YELLOW}No files to process in input directory{Colors.ENDC}")
|
|
||||||
else:
|
else:
|
||||||
logging.info(f"Found {total_files} files to process")
|
safe_log_info(f"Found {total_files} files to process", f"{Colors.BLUE}Found {total_files} files to process{Colors.ENDC}")
|
||||||
print(f"{Colors.BLUE}Found {total_files} files to process{Colors.ENDC}")
|
|
||||||
|
|
||||||
for i, file in enumerate(files, 1):
|
for i, file in enumerate(files, 1):
|
||||||
input_file = os.path.join(input_dir, file)
|
input_file = os.path.join(input_dir, file)
|
||||||
logging.info(f"Processing file {i}/{total_files}: {file}")
|
safe_log_info(f"Processing file {i}/{total_files}: {file}")
|
||||||
print(f"\n{Colors.BLUE}Processing file {i}/{total_files}: {file}{Colors.ENDC}")
|
print(f"\n{Colors.BLUE}Processing file {i}/{total_files}: {file}{Colors.ENDC}")
|
||||||
encode_dvr(input_file, output_dir, gpu)
|
encode_dvr(input_file, output_dir, gpu)
|
||||||
Reference in New Issue
Block a user