LinuxstorageTested on real hardware

Plex Default-Subtitle Sweep (Linux)

Clear default subtitle flags across a media library with ffmpeg stream-copy remux so files Direct Play instead of forcing a transcode — plus a cron job for new downloads.

Distrosubuntu, debian, alpine
Shellbash
Updated
Script
bash
#!/usr/bin/env bash
# Clear the DEFAULT flag on subtitle streams across a media tree.
# Stream-copy remux only (no re-encode, no quality loss).
# Forced subtitle streams keep their forced flag.
set -euo pipefail

MEDIA_DIR="${1:-/media}"

find "$MEDIA_DIR" -type f -name '*.mkv' -print0 | while IFS= read -r -d '' f; do
  # Which subtitle streams are marked default?
  defaults=$(ffprobe -v error -select_streams s \
    -show_entries stream=index:stream_disposition=default,forced \
    -of csv=p=0 "$f" | awk -F, '$2==1 {print $1","$3}')

  [ -z "$defaults" ] && continue   # nothing defaulted, skip (idempotent)

  args=()
  while IFS=, read -r idx forced; do
    # keep forced foreign-dialogue subs forced, just not defaulted
    if [ "$forced" = "1" ]; then
      args+=(-disposition:s:"$idx" forced)
    else
      args+=(-disposition:s:"$idx" 0)
    fi
  done <<< "$defaults"

  tmp="${f%.mkv}.sweep.mkv"
  if ffmpeg -v error -fflags +genpts -i "$f" \
      -map 0 -map_metadata 0 -map_chapters 0 -c copy \
      -max_interleave_delta 0 "${args[@]}" "$tmp"; then
    mv -f "$tmp" "$f"
    echo "swept: $f"
  else
    rm -f "$tmp"
    echo "FAILED (left untouched): $f" >&2
  fi
done

What this does

A subtitle track marked default tells the client to display it, which forces Plex to burn the subtitle into the video — a full transcode. On low-power hardware that transcode stutters or fails outright. This script walks a media tree, finds every MKV with a defaulted subtitle stream, and clears the default flag with an ffmpeg stream-copy remux (no re-encode, so zero quality loss and it runs in seconds per file). Forced foreign-dialogue subtitles keep their forced flag so they still show when needed.

After the sweep, files Direct Play and the subtitles are still there — just off by default, ready to toggle on manually.

Why this approach

Plex decides whether to Direct Play or transcode based on whether the client can handle the stream natively. One of the triggers that forces a transcode is a subtitle track marked “default” with an image-based format (like PGS, the format used on most Blu-ray rips). Image subtitles can’t be passed through — they have to be burned into the video frame, which requires a full video transcode. On a capable server, this is inconvenient. On a low-power NAS or LXC with no hardware video encoder, it means stuttering or complete failure.

The script in this playbook clears the default flag using ffmpeg’s stream-copy mode — it rewrites the MKV container without touching the video or audio streams, so there’s no quality loss and no re-encode. The operation takes a few seconds per file rather than the hours a re-encode would take. After the sweep, the subtitle tracks are still in the file and available for manual selection; they just won’t auto-enable and trigger a transcode.

Forced tracks (foreign-language dialogue like subtitles in an otherwise-English film) keep their forced flag, so they continue to display automatically in the appropriate context. The script filters specifically for default=1 without forced=1, so it only clears the tracks that would trigger unnecessary transcodes.

Running this sweep once after building a library from ripped or downloaded files is a significant quality-of-life improvement. Pairing it with a cron job (shown in the Auto-sweep section below) means future downloads get the same treatment automatically.

Prerequisites

  • ffmpeg and ffprobe installed (sudo apt-get install -y ffmpeg)
  • Read/write access to the media folder
  • Run against a backup or a copy first if you’re nervous — the remux is lossless but it does overwrite in place

Usage

Point it at your media root (defaults to /media):

bash

chmod +x sub-sweep.sh
./sub-sweep.sh /media/Movies

Auto-sweep new downloads with cron

To keep new downloads clean, run a lightweight version every 30 minutes that only touches recently-modified files:

bash

# Only scan MKVs modified in the last 90 minutes, then sweep them.
# Add to root's crontab:  crontab -e
*/30 * * * * find /media -name '*.mkv' -mmin -90 -print0 | \
xargs -0 -I{} /usr/local/bin/sub-sweep-one.sh "{}" >> /var/log/sub-sweep.log 2>&1

Notes

  • Timestamp errors: some MKVs fail with “Can’t write packet with unknown timestamp.” The -fflags +genpts -max_interleave_delta 0 flags in the script fix that — keep them.
  • Idempotent: a file with no defaulted subtitle is skipped, so re-running is safe and cheap.
  • MKV only: the script targets .mkv. MP4 rarely carries the problematic image subtitle formats, but you can add -o -name '*.mp4' to the find if needed.
  • Not mkvpropedit? mkvpropedit can flip the flag without a remux and is faster if installed, but ffmpeg is far more commonly available (it ships on most NAS boxes), so this script uses it.