Audio Discard Tool (Free Space from Unwanted Audio and Subtitles)

GitHub: https://github.com/iwalton3/media-scripts/tree/master/media-discard

This script allows you to delete unwanted audio tracks not in your language to save space on your Plex server. Extra audio tracks can account for a lot of storage space, with some foreign-language surround sound audio taking up over 10 percent of the file size.

This script will automatically repack your mkv files to remove audio and subtitles that are not in your preferred language. The script defaults to English and Japanese (for anime). Please be warned that this script will irrevocably delete content from your library and will cause files to be re-written to disk. Use it wisely.

You need python3 and mkvtoolnix to use this script. The script is intended for Linux, but you may be able to get it to run in cygwin. To adjust the languages to save, change the permitted_locales variable.

Example usage:

find /path/to/Plex -iname "*.mkv" | while read -r line; do PYTHONUNBUFFERED="yes" plex-media-discard.py "$line" 2>&1 | tee -a /var/log/plex-discard-log; done

Script contents:

#!/usr/bin/env python3
import sys
import subprocess
import json
import shutil
import os.path
import os
import random

fname = sys.argv[1]
metadata = json.loads(subprocess.check_output(["mkvmerge","-J",fname]).decode())

permitted_locales = {None, 'und', 'eng', 'jpn', 'mis', 'mul', 'zxx'}
saved_audio_tracks = []
saved_subtitle_tracks = []
has_bad_tracks = False

for track in metadata["tracks"]:
    props = track["properties"]
    if track["type"] == "audio":
        if props["language"] in permitted_locales:
            saved_audio_tracks.append(str(track["id"]))
        else:
            has_bad_tracks = True
            print("Discard audio: #{0} {1} ({2})".format(track["id"], props.get("track_name"), props.get("language")))
    elif track["type"] == "subtitles":
        if props["language"] in permitted_locales:
            saved_subtitle_tracks.append(str(track["id"]))
        else:
            has_bad_tracks = True
            print("Discard subtitle: #{0} {1} ({2})".format(track["id"], props.get("track_name"), props.get("language")))

if has_bad_tracks:
    if len(saved_audio_tracks) == 0:
        print("File {0} would have no audio tracks!".format(fname))
        sys.exit(1)
    tempfile = "_temp_{0}.mkv".format(random.randint(0,1000000))
    options = ["mkvmerge","-o",tempfile,
        "-a",",".join(saved_audio_tracks)]
    if len(saved_subtitle_tracks) == 0:
        options.append("-S")
    else:
        options.extend(["-s", ",".join(saved_subtitle_tracks)])
    options.append(fname)
    subprocess.check_call(options)
    orig_size = os.path.getsize(fname)
    new_size = os.path.getsize(tempfile)
    if orig_size * 0.50 > new_size:
        print("File {0} is too small after conversion.".format(fname))
        os.remove(tempfile)
        sys.exit(1)
    print("Moving file {0}...".format(fname))
    shutil.move(fname, fname+".bak")
    try:
        shutil.move(tempfile, fname)
    except Exception:
        print("File {0} could not be moved.".format(fname))
        shutil.move(fname+".bak", fname)
    if os.path.isfile(fname+".bak"):
        os.remove(fname+".bak")
    print("Size reduced {0} MiB ({1:n}%)".format((orig_size-new_size)//(1024**2), 100-(new_size*100//orig_size)))
else:
    print("File {0} is already minified!".format(fname))
2 Likes

For completeness, is your script recursive?

No, the script accepts the name of a single file and will process it. However, the “example usage” includes the command needed to run the file over the directory and log what happens. The find command is available as part of cygwin.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.