I trying to create a python batch code that uses mkvtoolnix to detect and remove empty pgs tracks from mkv files. i got so i manage to detect and print which tracks to keep, but it’s having problems actually removing the empty track
here is the script:
import os
import json
import subprocess
def run_command(command):
"""Run a shell command and return the output."""
try:
print(f"Running command: {' '.join(command)}") # Debugging
result = subprocess.run(command, capture_output=True, text=True, check=True)
print(f"stdout: {result.stdout}") # Debugging
print(f"stderr: {result.stderr}") # Debugging
return result.stdout
except subprocess.CalledProcessError as e:
print(f"Error running command: {' '.join(command)}")
print(f"stderr: {e.stderr}")
print(f"stdout: {e.stdout}")
return None
def get_mkv_metadata(mkv_file):
"""Extracts MKV metadata using mkvmerge."""
output = run_command(["mkvmerge", "-J", mkv_file])
return json.loads(output) if output else None
def get_empty_pgs_tracks(mkv_file):
"""Finds empty PGS subtitle tracks."""
metadata = get_mkv_metadata(mkv_file)
if not metadata:
return []
empty_tracks = []
for track in metadata["tracks"]:
if track["type"] == "subtitles" and "PGS" in track["properties"].get("codec_id", ""):
track_id = track["id"]
if is_pgs_track_empty(mkv_file, track_id):
empty_tracks.append(track_id)
print(f"Empty PGS subtitle tracks: {empty_tracks}") # Debugging
return empty_tracks
def is_pgs_track_empty(mkv_file, track_id):
"""Checks if a PGS track is empty using FFmpeg by extracting frames."""
temp_dir = "pgs_temp"
os.makedirs(temp_dir, exist_ok=True)
output_pattern = os.path.join(temp_dir, f"track_{track_id}_%03d.png")
run_command(["ffmpeg", "-y", "-loglevel", "error", "-i", mkv_file, "-map", f"0:{track_id}", output_pattern])
images = [f for f in os.listdir(temp_dir) if f.startswith(f"track_{track_id}_")]
is_empty = len(images) == 0 # True if no images (empty track)
# Cleanup extracted images
for file in images:
os.remove(os.path.join(temp_dir, file))
os.rmdir(temp_dir)
return is_empty
def get_empty_subtitle_tracks(mkv_file):
"""Finds empty subtitle tracks (SUP format)."""
metadata = get_mkv_metadata(mkv_file)
if not metadata:
return []
empty_tracks = []
for track in metadata["tracks"]:
if track["type"] == "subtitles":
track_id = track["id"]
if is_subtitle_track_empty(mkv_file, track_id):
empty_tracks.append(track_id)
print(f"Empty subtitle tracks: {empty_tracks}") # Debugging
return empty_tracks
def is_subtitle_track_empty(mkv_file, track_id):
"""Extracts and checks if a subtitle track is empty."""
temp_file = f"subtitle_{track_id}.sup"
run_command(["mkvextract", "tracks", mkv_file, f"{track_id}:{temp_file}"])
if os.path.exists(temp_file):
if os.path.getsize(temp_file) <= 100: # Very small or empty
os.remove(temp_file)
return True
os.remove(temp_file)
return False
def remove_empty_tracks(mkv_file):
"""Removes empty subtitle and PGS tracks from an MKV file."""
empty_pgs_tracks = get_empty_pgs_tracks(mkv_file)
empty_subtitle_tracks = get_empty_subtitle_tracks(mkv_file)
metadata = get_mkv_metadata(mkv_file)
if not metadata:
print(f"Failed to get metadata for {mkv_file}.")
return
# Debugging: Show all detected tracks
print("\n--- Track List ---")
for track in metadata["tracks"]:
print(f"Track {track['id']} | Type: {track['type']} | Codec: {track['properties'].get('codec_id', '')}")
# Identify valid tracks to keep
valid_tracks = [
track["id"] for track in metadata["tracks"]
if track["type"] != "subtitles" or track["id"] not in empty_subtitle_tracks + empty_pgs_tracks
]
print(f"\nValid tracks to keep: {valid_tracks}") # Debugging
if not empty_pgs_tracks and not empty_subtitle_tracks:
print(f"No empty subtitle or PGS tracks found in {mkv_file}. Skipping remux.")
return
output_file = mkv_file.replace(".mkv", "_cleaned.mkv")
mkvmerge_cmd = ["mkvmerge", "-o", output_file, mkv_file]
# Only include valid tracks
track_order = ",".join([f"0:{track_id}" for track_id in valid_tracks])
mkvmerge_cmd.extend(["--track-order", track_order])
print(f"\nFinal mkvmerge command: {' '.join(mkvmerge_cmd)}") # Debugging
run_command(mkvmerge_cmd)
def process_directory(directory_path):
"""Processes all MKV files in the given directory."""
for file_name in os.listdir(directory_path):
if file_name.lower().endswith(".mkv"):
mkv_file = os.path.join(directory_path, file_name)
remove_empty_tracks(mkv_file)
if __name__ == "__main__":
directory_path = r"C:\Users\travi\Videos\MediaRips"
process_directory(directory_path)