A fast, safe Rust tool to analyze and remove unnecessary streams from MKV files based on language preferences.
- 🚀 Fast & Safe - Written in Rust for performance and memory safety
- 📊 Detailed Analysis - Display comprehensive stream information with beautiful tables
- 🌍 Language Filtering - Filter audio and subtitle tracks by language codes (ordered by preference)
- 🏷️ Title-Based Selection - Advanced subtitle filtering by both language and title prefix matching
- ✂️ Stream Removal - Remove unwanted streams using mkvmerge with proper error handling
- ⚡ Smart Optimization - Automatically detects when processing is unnecessary and uses hardlinking/copying instead
- 🎯 Default Flag Management - Properly sets default flags based on language preferences (only one default per type)
- 📁 Batch Processing - Process entire directories with optional recursive traversal and glob filtering
- 🔍 Path Validation - Comprehensive validation prevents nested source/target scenarios
- 📄 Flexible Output - Support for both directory and file targets
- 🎬 Sonarr Integration - Native support as a Sonarr import script with proper communication
- ⚙️ Simplified Configuration - Easy YAML configuration with language preferences
- 🔍 Dry-run Mode - Preview changes without modifying files
- 🎨 Rich Output - Colored terminal output with emojis and formatted tables
- 🛡️ Stream Protection - Prevents removal of all audio streams, warns about subtitle removal
- 📎 Attachment Preservation - All video and attachment streams are always kept
- Rust (1.70 or later)
- ffprobe (from FFmpeg) - for detailed stream information
- mkvmerge (from MKVToolNix) - required for stream removal and modifications
# Clone the repository
git clone <repository-url>
cd mkv-slimmer
# Build with Cargo
cargo build --release
# Build for Sonarr
rustup target add x86_64-unknown-linux-musl
cargo build --target x86_64-unknown-linux-musl --release
# Or run directly
cargo run -- --help# Process MKV file and output to specified directory
cargo run -- movie.mkv /path/to/output/directory
# Process MKV file and output to specified file
cargo run -- movie.mkv /path/to/output/file.mkv
# Or using the compiled binary
./target/release/mkv-slimmer movie.mkv /path/to/output/directory# Keep only English and Japanese audio, English subtitles
cargo run -- movie.mkv /output/dir -a eng -a jpn -s eng
# Dry run with custom config (preview changes without modifying)
cargo run -- movie.mkv /output/dir -n -c custom-settings.yaml
# Keep Spanish and Japanese audio (Spanish will be default as it's listed first)
cargo run -- movie.mkv /output/dir -a spa -a jpn
# Smart optimization: if all streams are kept and defaults are correct,
# the tool will hardlink/copy instead of using mkvmerge
cargo run -- movie.mkv /output/dir -a eng -a jpn -a spa -s eng -s jpn# Process all MKV files in a directory
cargo run -- /movies/folder /output/dir
# Process recursively (maintains directory structure)
cargo run -- /movies/folder /output/dir --recursive
# Filter files with glob patterns
cargo run -- /movies/folder /output/dir --filter "*.mkv"
cargo run -- /movies/folder /output/dir -r -f "series/**/*.mkv"
# Combine with other options
cargo run -- /movies/folder /output/dir -r -f "*.mkv" -a eng -a jpn -s eng -nMKV Slimmer can be used as a Sonarr import script:
# Example Sonarr script configuration
# Set as Script Import Path in Sonarr settings
./target/release/mkv-slimmer "$1" "$2" -a eng -a jpn -s eng
# The tool automatically:
# - Detects Sonarr environment variables
# - Respects transfer mode (Move, Copy, HardLink, HardLinkOrCopy)
# - Outputs proper status commands ([MoveStatus] MoveComplete/RenameRequested)
# - Handles cross-filesystem moves with copy+delete fallbackThe tool uses a simple configuration system:
- CLI parameters (highest priority) - Override configuration settings
- settings.yaml file (default) - Main configuration file
- Interactive prompts (fallback) - For missing required values when running in a TTY
# Languages to keep (ordered by preference - first available becomes default)
audio:
keep_languages:
- jpn # Japanese (first preference)
- und # Undefined (fallback)
subtitles:
# Subtitle preferences can be:
# - Language only: "eng"
# - Language with title prefix: "eng, Dialogue"
keep_languages:
- hun # Hungarian (first preference)
- und # Undefined (second preference)
- "eng, Dialogue" # English with title starting with "Dialogue"
- eng # English (any title)
- jpn # Japanese (fourth preference)
# Note: Video and attachment streams are always kept
# Processing options
processing:
dry_run: false- Ordered Lists: Languages in
keep_languagesare ordered by preference - First Available Wins: The first language from the list that exists in the video becomes the default
- Single Default: Only one stream per type is marked as default (the first found)
- Automatic Fallback: If the first preference doesn't exist, it tries the next one
Subtitles can be selected based on both language and title prefix:
- Language only:
"eng"- Matches any English subtitle - Language with title:
"eng, Dialogue"- Matches English subtitles with titles starting with "Dialogue" - Case-insensitive: Title matching is case-insensitive
- Prefix matching: Only the beginning of the title needs to match
- Commas in titles: Titles can contain commas - only the first comma separates language from title
Examples:
"eng, Full Subtitles"matches "Full Subtitles - Complete Translation""jpn, Signs, Songs & Lyrics"matches "Signs, Songs & Lyrics (Karaoke)""eng, Dialogue"does NOT match "Signs & Songs"
<INPUT_PATH>- Path to the MKV file or directory to process (required)<TARGET_PATH>- Path where the modified MKV will be created (can be a file or directory) (required)-a, --audio-languages <LANG>- Languages to keep for audio tracks (ordered by preference, can be specified multiple times)-s, --subtitle-languages <LANG>- Languages to keep for subtitle tracks (ordered by preference, can be specified multiple times, supports "lang" or "lang, title prefix" format)-r, --recursive- Process directories recursively (maintains subdirectory structure)-f, --filter <PATTERN>- Glob pattern to filter files (filename in non-recursive mode, relative path in recursive mode)-n, --dry-run- Show what would be removed without modifying-c, --config <FILE>- Alternative config file path (default: settings.yaml)-h, --help- Print help information-V, --version- Print version information
- File → File: Uses the provided path instead of the input filename
- File → Directory: Current behavior, appends input filename to output directory
- Directory → Directory: Current behavior for batch processing
- Directory → File: Not allowed (returns error)
- ffprobe (from FFmpeg) - For detailed stream information
- mkvmerge (from MKVToolNix) - Required for stream removal and default flag modifications
clap- Command-line argument parsingserde/serde_yaml- Configuration managementtabled- Beautiful table formattingcolored- Terminal colors and stylinganyhow- Error handlingdialoguer- Interactive promptsmatroska- MKV parsing (backup to ffprobe)tokio- Async runtimeglob- Pattern matching for file filtering
The tool includes comprehensive safety measures:
- Audio Protection: Fails with an error if all audio streams would be removed
- Subtitle Warning: Shows a warning if all subtitle streams would be removed (but continues)
- Video/Attachment Preservation: All video and attachment streams are always kept
- Nested Directory Prevention: Prevents dangerous source/target relationships
- Same Directory Detection: Blocks processing when source and target are identical
- Infinite Loop Protection: Stops recursive processing from including its own output
# Stream protection
Error: All audio streams would be removed. Audio languages to keep: [fre, ger], but available languages are: [jpn, eng]
# Path validation
Error: Target directory cannot be nested within the source path.
Source: /movies/season1
Target: /movies/season1/processed
This would cause the output to be processed as input in recursive mode.📁 Analyzing: movie.mkv
📂 Target directory: /output/directory
🎵 Audio languages (ordered by preference): eng, jpn
📄 Subtitle languages (ordered by preference): eng, spa
🎬 Video Streams:
╭───┬───────┬────────────┬───────┬─────┬──────┬────────╮
│ # │ Codec │ Resolution │ FPS │ HDR │ Size │ Status │
├───┼───────┼────────────┼───────┼─────┼──────┼────────┤
│ 0 │ h264 │ 1920x1080 │ 23.98 │ No │ 2.1G │ KEEP │
╰───┴───────┴────────────┴───────┴─────┴──────┴────────╯
🎵 Audio Streams:
╭───┬───────┬──────────┬──────────┬─────────────┬──────┬─────────┬────────────────╮
│ # │ Codec │ Language │ Channels │ Sample Rate │ Size │ Default │ Status │
├───┼───────┼──────────┼──────────┼─────────────┼──────┼─────────┼────────────────┤
│ 1 │ ac3 │ eng │ 6 │ 48000 Hz │ 645M │ Yes │ KEEP (default) │
│ 2 │ aac │ jpn │ 2 │ 48000 Hz │ 156M │ No │ KEEP │
│ 3 │ ac3 │ spa │ 6 │ 48000 Hz │ 645M │ No │ REMOVE │
╰───┴───────┴──────────┴──────────┴─────────────┴──────┴─────────┴────────────────╯
📊 Summary:
Original size: 3.2 GB
After processing: 2.1 GB
Space savings: 1.1 GB (34.4%)
Streams to remove: 1
🎬 Processing streams...
🎯 Keeping 2 stream(s): #0, #1, #2
🔄 Running mkvmerge to create: /output/directory/movie.mkv
📁 Output file: /output/directory/movie.mkv
📊 Original size: 3.2 GB
📊 New size: 2.1 GB
💾 Space saved: 1.1 GB (34.4%)
✅ Stream processing completed successfully!
📁 Analyzing: movie.mkv
📂 Target directory: /output/directory
🎵 Audio languages (ordered by preference): eng, jpn, spa
📄 Subtitle languages (ordered by preference): eng, spa
🎬 Processing streams...
🎯 Keeping 4 stream(s): #0, #1, #2, #3
✨ No stream processing needed - linking/copying file instead
🔗 Hard linked to: /output/directory/movie.mkv
📁 Output file: /output/directory/movie.mkv
📊 File size: 3.2 GB
💾 Space saved: 0 B (0.0%) - no processing required
✅ Stream processing completed successfully!
- ✅ Stream Analysis - Complete with detailed metadata extraction
- ✅ Language Filtering - Full support for audio/subtitle filtering
- ✅ Configuration System - Three-layer config with validation
- ✅ Beautiful Output - Formatted tables with colors and emojis
- ✅ Stream Removal - Complete with mkvmerge integration and error handling
- ✅ Smart Optimization - Automatic detection and hardlinking/copying when no processing needed
- ✅ Default Flag Management - Proper setting of default flags based on language preferences
- ✅ Batch Processing - Complete with recursive directory support, glob filtering, and progress reporting
- ✅ Modular Architecture - Clean hierarchical structure with eliminated circular dependencies
The Rust implementation provides significant performance improvements:
- 🚀 Faster startup - No interpreter overhead
- 💾 Lower memory usage - Efficient memory management
- 🛡️ Memory safety - Zero-cost abstractions without runtime panics
- ⚡ Smart optimization - Automatic hardlinking/copying when no processing needed (instant operation)
- 🔧 Efficient mkvmerge usage - Only processes when necessary, with proper stream selection and default flag management