Skip to content

TheBluWiz/MuxMaster

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

103 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

muxm MuxMaster

Version Platform License

MuxMaster (muxm) — a single-command video repacking and encoding utility that handles Dolby Vision, HDR10, audio track selection, subtitle processing, and container muxing so you don't have to. Pick a profile, point it at a file, and get a properly encoded output without memorizing ffmpeg flags.

# Install via Homebrew (macOS)
brew install TheBluWiz/muxm/muxm

# Encode for Apple TV Direct Play — that's it
muxm --profile atv-directplay-hq movie.mkv

Table of Contents


💡 Why MuxMaster?

Getting a Blu-ray rip to direct-play correctly on an Apple TV, a Roku, or through Plex — without the server transcoding on the fly — is a surprisingly deep problem. The video might be Dolby Vision Profile 7 that needs conversion to Profile 8.1. The audio might be TrueHD, which your player can't direct-play, so you need E-AC-3 — but you also want a stereo AAC fallback for when you're watching on a phone. The forced subtitles need to be burned in because MP4 containers don't handle PGS bitmaps. And the color space metadata needs to survive the whole process.

You can solve all of this with raw ffmpeg, but the command will be 15+ flags long, different for every source file, and you'll need to inspect the source with ffprobe first to decide what half of those flags should be. Every new file is a new puzzle.

HandBrake is the go-to GUI for video encoding, and it's excellent for what it does. But its preset system doesn't adapt to what's actually in the file. It can't detect that your source is already Apple TV-compliant and skip the encode. It doesn't extract Dolby Vision RPUs, convert between DV profiles, or inject them back into re-encoded video. It won't selectively OCR your PGS subtitles to SRT when the output container can't carry bitmaps. And it won't generate a JSON report of everything it did for your records. HandBrake gives you a good encode; MuxMaster gives you an opinionated pipeline that understands the relationship between your source, your target device, and every stream in the file.

Tdarr solves the batch-processing and automation problem well, especially at library scale. But it requires a server, a database, a web UI, and Node.js — it's infrastructure. If you want to process a single file, or a handful of files, with precise control over DV handling, audio track selection, and subtitle policy, Tdarr's plugin system means writing JavaScript to configure what muxm handles with a single --profile flag. Tdarr is a media library manager; MuxMaster is a per-file encoding tool that aims to make every decision correctly so you don't have to inspect the output.

MuxMaster sits in the gap between "I know ffmpeg well enough to do this manually" and "I need a server-based automation platform." It's a single Bash script with only three required dependencies (ffmpeg, jq, and bc) and optional tooling for Dolby Vision and subtitle OCR. It understands Dolby Vision at the RPU level, and its profile system encodes the tribal knowledge of what actually works on real hardware into repeatable, overridable presets.

Configuration is where the design philosophy comes together. Most CLI tools expect you to read the source code to learn which variables exist, then hand-build a dotfile from scratch. muxm --create-config generates a complete, commented config file pre-seeded with a real profile's values — you start from a working baseline and customize, not from a blank page. Configs cascade through three tiers (system, user, project) so an encoding team can lock organization defaults in /etc/.muxmrc while individuals override their preferred CRF or audio settings in ~/.muxmrc and specific project directories can pin a streaming profile. And --print-effective-config shows you the fully resolved result of all those layers before you commit to an encode, so you always know exactly what's about to happen.


🎯 Format Profiles

Profiles are named presets that configure muxm for a specific use case in a single flag. Every setting a profile changes can be individually overridden with CLI flags.

muxm --profile <n> input.mkv
Profile Goal Container Video Audio DV
dv-archival Lossless preservation MKV Copy (no re-encode) Lossless passthrough Preserve
hdr10-hq Max HDR10 quality MKV HEVC CRF 17 Lossless + stereo fallback Strip
atv-directplay-hq Apple TV Direct Play MP4 HEVC Main10 (copy if compliant) E-AC-3 + AAC stereo P8.1 auto
streaming Modern HEVC streaming MP4 HEVC CRF 20 E-AC-3 448k + AAC stereo Strip
animation Anime/cartoon optimized MKV HEVC CRF 16, 10-bit Lossless + stereo fallback Strip
universal Play anywhere MP4 H.264 SDR (tone-map HDR) AAC stereo Strip

dv-archival — Dolby Vision Archival

For collectors who want bit-perfect preservation. Copies video without re-encoding, passes lossless audio through, keeps all subtitles and chapters, and generates a JSON report. Skips processing entirely if the source already matches.

muxm --profile dv-archival movie.mkv

hdr10-hq — High Quality HDR10

Strips Dolby Vision layers and re-encodes to clean HDR10 HEVC at CRF 17. Preserves lossless audio (TrueHD, DTS-HD MA, FLAC) and adds a stereo fallback track. MKV output.

muxm --profile hdr10-hq movie.mkv

atv-directplay-hq — Apple TV Direct Play

Targets true Direct Play on Apple TV 4K via Plex: MP4 container, HEVC Main10 with DV Profile 8.1 when possible, E-AC-3 surround with AAC stereo fallback, and forced subtitle burn-in. Copies compliant video without re-encoding. Skips processing if source is already ATV-compliant.

muxm --profile atv-directplay-hq movie.mkv

streaming — Modern HEVC Streaming

Optimized for Plex, Jellyfin, and Emby on modern clients: Shield, Fire TV, Roku Ultra, smart TVs, and web browsers. HEVC CRF 20 with E-AC-3 surround at streaming-friendly bitrates, AAC stereo fallback, and soft subtitles. Strips DV and keeps HDR10. Balances quality with file size.

muxm --profile streaming movie.mkv

animation — Anime & Cartoon Optimized

Tuned for animation content: lower psy-rd/psy-rdoq to avoid ringing on hard cel edges, 10-bit even for SDR to eliminate banding in gradients, and lossless audio passthrough. MKV container preserves styled ASS/SSA subtitles. Keeps all subtitle tracks (up to 6) and chapter markers.

muxm --profile animation movie.mkv

universal — Universal Compatibility

Plays on everything: old Rokus, mobile devices, web browsers, non-HDR TVs. Tone-maps HDR to SDR, encodes to H.264, forces AAC stereo audio, burns forced subtitles, exports others as external SRT, and strips chapters and non-essential metadata.

muxm --profile universal movie.mkv

Overriding Profile Defaults

Profiles are starting points — every setting can be overridden with CLI flags:

# Use hdr10-hq but with a different CRF and no stereo fallback
muxm --profile hdr10-hq --crf 20 --no-stereo-fallback movie.mkv

# Use universal but keep chapters
muxm --profile universal --keep-chapters movie.mkv

# Use atv-directplay-hq but output to MKV (you'll get a warning)
muxm --profile atv-directplay-hq --output-ext mkv movie.mkv

🔧 How It Works

Expand to see the full encoding pipeline

When you run muxm, the script executes a multi-stage pipeline that inspects the source file, makes codec and container decisions based on the active profile, processes each stream type independently, and assembles the final output. Here's what happens under the hood:

1. Source Inspection. muxm calls ffprobe once and caches the full JSON metadata for the source file. Every subsequent decision — codec detection, color space identification, audio channel layout, subtitle format — reads from this cache rather than re-probing.

2. Profile Resolution. Settings are resolved through a layered precedence chain: hardcoded defaults → system config (/etc/.muxmrc) → user config (~/.muxmrc) → project config (./.muxmrc) → profile → CLI flags. Contradictory combinations (like --profile dv-archival --no-dv) trigger warnings but never errors — CLI flags always win.

3. Video Pipeline. The video stage handles the most complexity. It detects Dolby Vision by probing both stream metadata and frame-level side data, then identifies the DV profile (5, 7, or 8) and compatibility ID. For profiles that preserve DV, it extracts the RPU (Reference Processing Unit) via dovi_tool, converts between DV profiles when necessary (e.g., Profile 7 dual-layer → Profile 8.1 single-layer), encodes the base layer with x265, and injects the RPU back into the encoded stream. For non-DV profiles, it detects the source color space (BT.2020 PQ, BT.2020 HLG, or BT.709 SDR), sets the matching x265 color parameters and pixel format, and applies tone-mapping when the profile targets SDR output.

4. Audio Pipeline. Audio track selection is language-preference-aware and codec-aware. When multiple tracks exist, muxm scores each one based on language match, channel count, surround layout, codec preference, and bitrate — with configurable weights in .muxmrc (see man muxm for details). The pipeline picks the best available track (honoring --audio-lang-pref), decides whether to copy it through or transcode it based on the profile's codec requirements, and optionally generates a stereo AAC fallback track from the surround source. Lossless codecs (TrueHD, DTS-HD MA, FLAC) are passed through untouched when the profile and container support it.

5. Subtitle Pipeline. Subtitles are categorized into forced, full, and SDH tracks. PGS bitmap subtitles are OCR'd to SRT (via pgsrip or sub2srt) when the output container can't carry them natively. Forced subtitles can be burned into the video stream; other tracks can be embedded or exported as external .srt files. The pipeline respects language preferences and can exclude SDH tracks.

6. Final Mux. All processed streams are assembled into the target container (MP4, MKV, M4V, or MOV) with correct codec tagging, chapter markers, subtitle disposition flags, and metadata. For Dolby Vision in MP4, muxm verifies that the dvcC/dvvC container signaling record is present via MP4Box.

7. Verification. The output file is validated with ffprobe to confirm it's non-empty and parseable. Optionally, a SHA-256 checksum is written and a JSON report is generated documenting every decision, warning, and stream mapping from the run.

If any stage fails, muxm logs the failure, cleans up incomplete temp files, and exits with a descriptive error code. The --dry-run flag executes the entire decision pipeline without writing real output, so you can preview exactly what muxm would do before committing to an encode.


📦 Installation

Compatibility

muxm requires Bash 4.3+ and runs on macOS (10.15 Catalina or later) and modern Linux distributions (Ubuntu 20.04+, Fedora 33+, Debian 11+, Arch). It is tested primarily on macOS with Homebrew-installed ffmpeg builds.

Homebrew (recommended — macOS)

brew install TheBluWiz/muxm/muxm
muxm --install-completions      # bash/zsh tab completion

This installs muxm with its required dependencies (bash 4.3+, ffmpeg, jq) and the man page automatically.

Optional dependencies — for Dolby Vision, subtitle OCR, and subtitle burn-in:

muxm --install-dependencies       # installs everything missing in one pass

Or install individually as needed:

brew install dovi_tool            # Dolby Vision RPU handling
brew install gpac                 # DV container signaling (MP4Box)
brew install tesseract            # PGS subtitle OCR engine
brew install ffmpeg-full          # ffmpeg with libass (for --sub-burn-forced) + tesseract

Manual Install (macOS/Linux)

git clone https://github.com/TheBluWiz/MuxMaster.git
cd MuxMaster
chmod +x muxm
sudo cp muxm /usr/local/bin/muxm
muxm --setup                     # installs dependencies, man page, and tab completion

Dependencies

Required:

  • ffmpeg and ffprobe – core encoding and media inspection
  • jq – JSON parsing for stream metadata and reporting
  • bc – arithmetic evaluation for frame-rate and bitrate calculations

Optional (auto-disabled if missing):

  • ffmpeg with libass (ffmpeg-full on Homebrew) – required for subtitle burn-in (--sub-burn-forced); the standard ffmpeg package works for all other features
  • dovi_tool – Dolby Vision RPU extraction/injection; DV handling is automatically disabled if missing
  • MP4Box (gpac) – DV container-level signaling verification
  • tesseract – OCR engine required by pgsrip for PGS bitmap subtitle conversion
  • sub2srt or pgsrip – PGS bitmap subtitle OCR to SRT (configurable via --ocr-tool)

Setup Helpers

# First-time setup (manual installs only): dependencies, man page, and tab completion
muxm --setup

# Or run individually:
muxm --install-dependencies
muxm --install-man
muxm --install-completions

# Generate a config file pre-filled with a profile's defaults
muxm --create-config user atv-directplay-hq   # ~/.muxmrc
muxm --create-config project streaming         # ./.muxmrc

# Remove installed components
muxm --uninstall-man
muxm --uninstall-completions

Upgrading

Homebrew:

brew upgrade muxm

The man page and tab completions are refreshed automatically on every upgrade — no extra steps needed.

Manual installs:

Pull the latest version and re-run setup to update the man page, completions, and dependencies:

cd MuxMaster && git pull
sudo cp muxm /usr/local/bin/muxm
muxm --setup

🚀 Usage

muxm [options] <source> [target.mp4]

Arguments

  • <source> – Input media file (e.g., movie.mkv)
  • [target] – Output file (optional; defaults to <source>.<output-ext>). If the derived output path would overwrite the source file, muxm auto-appends a version number: movie(1).mp4, movie(2).mp4, etc.

Key Flags

Flag Description
--profile NAME Apply a format profile (dv-archival, hdr10-hq, atv-directplay-hq, streaming, animation, universal)
--dry-run Simulate without writing output
--crf N Set video CRF value
-p, --preset NAME x265 encoder preset (e.g., slow, medium)
--video-codec libx265|libx264 Video encoder
--tonemap Tone-map HDR to SDR
--audio-lang-pref LANGS Audio language preference (comma-separated, e.g., eng,jpn)
--audio-force-codec CODEC Force all audio to a specific codec
--audio-lossless-passthrough Allow lossless codecs to pass through
--sub-lang-pref LANGS Subtitle language preference (comma-separated)
--sub-burn-forced Burn forced subtitles into video
--output-ext mp4|mkv|m4v|mov Output container
--report-json Generate a JSON report alongside the output file
--checksum Write a SHA-256 checksum file for the output
--strip-metadata Strip non-essential metadata
--skip-if-ideal Skip processing if source matches target
--replace-source Replace the original source file (interactive confirmation)
--force-replace-source Replace the original source file (no prompt; scripting-friendly)
--print-effective-config Show resolved config after config file imports

Setup & Management

Flag Description
--setup Full first-time setup: dependencies, man page, and tab completion
--install-dependencies Install required and optional tools via Homebrew/pipx
--install-man Install the muxm(1) manual page
--install-completions Install bash/zsh tab completion
--uninstall-man Remove the installed manual page
--uninstall-completions Remove installed tab completion
--create-config SCOPE [PROFILE] Generate a .muxmrc config file (system, user, or project)
--force-create-config SCOPE [PROFILE] Same as --create-config but overwrites an existing file

Run muxm --help for the full flag reference.


⚙️ Configuration

muxm has a layered configuration system designed so you set your preferences once and override only when you need to.

Settings are resolved through multiple levels, applied in this order (lowest → highest precedence):

Hardcoded defaults
  → /etc/.muxmrc          (system-wide)
    → ~/.muxmrc            (user defaults)
      → ./.muxmrc          (project-specific)
        → --profile <n> (format profile)
          → CLI flags      (highest — always wins)

Setting a Default Profile

Add to any .muxmrc file:

# ~/.muxmrc — always use Apple TV Direct Play unless overridden
PROFILE_NAME="atv-directplay-hq"

CLI --profile always overrides a config-file PROFILE_NAME.

Creating a Config File

Use --create-config to generate a full .muxmrc file pre-configured for a specific profile:

muxm --create-config <scope> [profile]
Scope Path Use case
system /etc/.muxmrc Organization-wide defaults (requires sudo)
user ~/.muxmrc Personal defaults across all projects
project $PWD/.muxmrc Per-project settings

The generated file contains the complete config template. Variables set by the chosen profile are uncommented and active; everything else is commented with defaults for easy customization.

Profile defaults to atv-directplay-hq if omitted. Use --force-create-config to overwrite an existing file.

Example workflow: You generate a user config seeded with the streaming profile (muxm --create-config user streaming). Now every encode defaults to HEVC CRF 20 with E-AC-3 audio — no flags needed. Later you create a project config in your anime directory seeded with animation (muxm --create-config project animation). Files encoded from that directory automatically get animation-tuned settings. And when you need a one-off override, muxm --crf 14 on the command line wins over everything without touching any config file.

Verifying Effective Configuration

When configs cascade through multiple layers, it's easy to lose track of what's actually active. --print-effective-config resolves every layer and shows you the final result before anything is encoded:

# See what the resolved config looks like after all sources merge
muxm --profile hdr10-hq --crf 20 --print-effective-config

Every variable is displayed grouped by section, with the active profile name and the source of each override (CLI, config file, or profile default). No more guessing what a run will do.


📋 Additional Features

Beyond profiles and the core encoding pipeline, muxm ships with a set of operational features that make it safer and easier to use in practice:

  • Skip-if-Ideal – Before encoding, muxm inspects the source to determine if it already matches the target profile. If it does, the file is linked or copied without re-encoding, saving time and avoiding generation loss. Enabled per-profile or via --skip-if-ideal.
  • Collision Handling – When the derived output filename matches the source (e.g., encoding movie.mp4 with the default .mp4 extension), muxm auto-versions the output to movie(1).mp4, movie(2).mp4, etc. instead of failing. Use --replace-source for interactive in-place replacement or --force-replace-source for scripted workflows.
  • Conflict Warnings – Running --profile dv-archival --no-dv doesn't error out — it warns you that the combination is contradictory and proceeds with your explicit flags taking precedence. The tool trusts you but lets you know when something looks wrong.
  • Dry-Run Mode--dry-run executes the entire decision pipeline (profile resolution, codec detection, DV identification, audio selection) and prints what it would do, without writing any output files.
  • JSON Reporting--report-json generates a machine-readable JSON report alongside the output file, documenting every decision, warning, codec mapping, and stream disposition from the run.
  • Checksum Verification – Optionally writes a SHA-256 checksum file for the output to verify integrity after transfer or archival.
  • Man Pagemuxm --install-man installs a full muxm(1) manual page with complete flag reference, profile documentation, and examples accessible via man muxm.
  • Tab Completionmuxm --install-completions installs bash/zsh tab completion for all flags, profiles, presets, and config scopes. Completes media file extensions when providing input files.

❓ FAQ

muxm says it requires Bash 4.3+ but I'm on macOS. macOS ships Bash 3.2 (2007) due to licensing. If you installed via Homebrew (brew install TheBluWiz/muxm/muxm), this is handled automatically — the formula rewrites the shebang to use Homebrew's bash. For manual installs, run brew install bash and make sure /opt/homebrew/bin/bash (Apple Silicon) or /usr/local/bin/bash (Intel) appears before /bin/bash in your $PATH.

Dolby Vision handling seems to be disabled / I don't see DV in my output. DV processing requires dovi_tool and, for MP4 container signaling, MP4Box (gpac). If either is missing, muxm silently disables DV features rather than failing. Run muxm --install-dependencies to install them, or check with muxm --dry-run — the output will show whether DV was detected and what the pipeline plans to do with it.

My file was copied instead of re-encoded. Is that a bug? Probably not. If the source already matches the target profile (correct codec, container, color space, and audio layout), muxm skips the encode and copies/links the file. This is the Skip-if-Ideal feature — it saves time and avoids generation loss. You'll see a message indicating the skip. To force a re-encode, omit --skip-if-ideal or override a setting (e.g., --crf 18) so the source no longer matches.

Which profile should I use? If you're playing through Plex on an Apple TV 4K: atv-directplay-hq. If you want broad device compatibility across Plex/Jellyfin/Emby clients: streaming. If you're archiving a Dolby Vision disc rip: dv-archival. If you need it to play on everything including old hardware and phones: universal. For anime or cartoons: animation. For clean HDR10 without DV complexity: hdr10-hq. When in doubt, start with --dry-run to preview what a profile will do to your file.

Can I process a batch of files? muxm is a per-file tool by design. For a batch, a simple shell loop works:

for f in *.mkv; do muxm --profile streaming "$f"; done

For library-scale automation, consider pairing muxm with find, xargs, or a job runner like GNU Parallel.

How do I know what settings are actually active? Run muxm --print-effective-config (with any profile and flags you plan to use). It resolves every config layer — defaults, system/user/project .muxmrc files, profile, and CLI flags — and prints the final result. Nothing is encoded; it just shows you exactly what would happen.

The output file is larger than I expected. CRF-based encoding targets quality, not file size. A visually complex source (grain, fast motion, high detail) will produce a larger file at the same CRF than a clean digital source. You can lower quality slightly with a higher --crf value (e.g., --crf 22 instead of the default), or use a slower --preset (slow or slower) which achieves better compression at the same quality — at the cost of longer encode time.


📄 License

MuxMaster is freeware for personal, non-commercial use. Any business, government, or organizational use requires a paid license.

Full license text available in LICENSE.md


🐛 Bug Reports

Found a bug? Please open an issue on GitHub. Include the output of muxm --version, the profile and flags you used, and any relevant log output. A --dry-run dump or --report-json output is especially helpful.

This is a solo-maintained project and I'm not accepting outside code contributions at this time. See CONTRIBUTING.md for details.


📬 Contact

If you're using MuxMaster, I'd love to hear about it — what's working, what's not, what workflows you're using it for. This is a solo project and real-world feedback shapes what gets built next.


👤 Author

Jamey Wicklund (@TheBluWiz)

About

MuxMaster – a versatile, cross-platform video repacking and encoding utility that preserves HDR, Dolby Vision, and high-quality audio while optimizing for Plex and Apple TV Direct Play. Supports smart codec handling, color space matching, error recovery, and optional stereo fallback.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages