32 KiB
AV1 Batch Video Transcoder
A high-performance batch video transcoding tool using NVIDIA's AV1 NVENC or HEVC NVENC codec with intelligent audio/subtitle handling, interactive stream selection, and automatic quality optimization.
📋 Table of Contents
- Key Features
- Quick Start
- Installation
- Basic Usage
- Command Reference
- Batch Processing
- Audio Options
- Undefined Language Replacement
- Encoder Selection
- Configuration
- Project Structure
- Encoding Process
- Troubleshooting
✨ Key Features
Video Encoding
- Dual Encoder Support - NVIDIA AV1 NVENC (8-bit, default) or HEVC NVENC (10-bit)
- Smart Resolution - Detects source resolution, scales 4K→1080p, preserves lower resolutions
- GPU Acceleration - Hardware encoding via NVIDIA NVENC (RTX 2060+)
- Two-Phase Mode - CQ mode first, automatic Bitrate fallback if size threshold exceeded
- Quality Presets - Per-content-type CQ values (TV/Movies, 720p/1080p)
Audio Processing
- Smart Audio Processing - Auto-detects bitrate, AAC for stereo, EAC3 for 5.1
- Stream Selection - Keep only best English + Commentary (automatic) or choose manually (interactive)
- Interactive Selection - View all audio streams and select which ones to keep per file
- Channel Control - Force specific channel counts (2-channel stereo or 6-channel 5.1 surround)
- Audio Titles - Rename/tag audio streams with custom titles
- Language Tagging - Optional language code metadata for audio streams
Subtitles & Metadata
- Subtitle Embedding - Auto-detects and embeds subtitles (.vtt, .srt, .ass, .ssa, .sub)
- Language-Prefixed Subtitles - Finds language-specific files (movie.en.vtt, movie.eng.vtt)
- Automatic Cleanup - Deletes subtitle files after embedding
Processing Features
- CSV Tracking - Detailed conversion logs with compression ratios and media context
- Automatic Cleanup - Deletes originals + subtitles after successful encoding
- Test Mode - Encode one file, check compression ratio before batch processing
- Structured Logging - JSON logs with media type, show name, season/episode context
- File Tagging - Output files get
- [EHX]suffix for easy identification
🚀 Quick Start
Requirements
- Python 3.8+
- FFmpeg with libfdk-aac support
- NVIDIA GPU (GeForce RTX 2060+, Quadro, or newer)
- NVIDIA CUDA Toolkit (for NVENC support)
Installation
# Clone repository
git clone https://github.com/yourusername/conversion_project.git
cd conversion_project
# Install Python dependencies (if any needed in future)
# pip install -r requirements.txt
Basic Usage
# Encode a TV folder (smart mode with AV1, default)
python main.py "P:\tv\Show Name"
# Use HEVC encoder (10-bit, better Plex compatibility)
python main.py "P:\tv\Show Name" --encoder nvenc
# Test single file before batch processing
python main.py "P:\tv\Show Name" --test
# Force specific quality (CQ 30)
python main.py "P:\movies\Movie" --cq 30
# Force bitrate mode
python main.py "P:\tv\Show" --m bitrate
# Specific resolution
python main.py "P:\movies" --r 720
# Tag audio with language
python main.py "P:\tv\Show" --language eng
# Interactive audio selection
python main.py "P:\tv\Show" --filter-audio --interactive
Command Reference
Encoder Selection
--encoder {av1,nvenc}
av1(default): AV1 NVENC, 8-bit, smaller files (~baseline compression)nvenc: HEVC NVENC, 10-bit, larger files (~80-90% of AV1), better compatibility
Quality & Mode
--cq <value> # Use CQ mode with specific quality (0-51, lower=better)
--m {cq,bitrate} # Force encoding mode (default: smart two-phase)
Resolution
--r {480,720,1080} # Force specific output resolution
# Default: scale 4K→1080p, preserve lower
Audio Options
--filter-audio # Enable automatic audio filtering (keep English + Commentary)
--interactive # Interactive mode: show audio streams, user selects which to keep
--audio-select "0,1" # Pre-select specific audio streams (indices)
--audio-titles "0:English,1:Commentary" # Rename/title audio tracks
--audio-channels "0:2,1:6" # Force channel count (2=stereo, 6=5.1 surround)
--language eng # Tag audio streams with language code
--default-language spa # Set default language for undefined (und) audio tracks
--no-replace-und # Disable undefined language tag replacement
Undefined Language (und) Replacement
Automatically replaces undefined (und) audio language tags with a configurable default language. Most useful when your video library has audio with undefined language tags that should actually be marked as a specific language.
Configuration
Add to config.xml in the <general> section:
<!-- Default language for undefined (und) audio tracks -->
<!-- Set to desired ISO 639-2 code (eng, spa, fra, deu, etc.) or 'und' to disable -->
<default_language>eng</default_language>
<!-- Replace undefined language tracks with default language -->
<replace_undefined_language>true</replace_undefined_language>
CLI Arguments
--default-language <LANG> - Override the default language for a single run:
python main.py "P:\movies\Collection" --default-language spa
python main.py "P:\tv\Show" --default-language fra
--no-replace-und - Disable undefined language replacement for a single run:
python main.py "P:\movies\Collection" --no-replace-und
Behavior
Priority Chain (highest to lowest):
--language eng(CLI): Tags ALL audio streams as 'eng'--default-language spa+ detectedund: Replaces with 'spa'- config.xml
default_language: eng+ detectedund: Replaces with 'eng' --no-replace-undflag: Skips allundreplacement
Key Points:
- Only affects 'und' language tags (existing language tags are preserved)
- Works with batch processing (
--paths-file) - Compatible with TV/movie/anime detection
- Works in CQ mode, Bitrate mode, and Smart mode
Common Language Codes
| Code | Language | Code | Language |
|---|---|---|---|
| eng | English | jpn | Japanese |
| spa | Spanish | kor | Korean |
| fra | French | zho | Chinese (Mandarin) |
| deu | German | rus | Russian |
| ita | Italian | por | Portuguese |
Full list: https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes
Examples
Example 1: Most content is English with und tags
# config.xml already has: <default_language>eng</default_language>
python main.py "P:\movies"
# Result: All 'und' audio streams are tagged as 'eng'
Example 2: Mixed language batch with different defaults
# paths.txt
"P:\movies\English Collection" --default-language eng
"P:\movies\Spanish Collection" --default-language spa
"P:\movies\Mixed" --no-replace-und
python main.py --paths-file paths.txt
Processing
--test # Test mode: encode first file, show compression ratio, don't move
Batch Processing
Overview
Process multiple folders sequentially with different parameters for each. Perfect for encoding your entire media library with specific settings per movie/show.
Quick Start
# Simple list format (paths.txt)
python main.py --paths-file paths.txt
# CSV format with custom parameters (paths_batch.csv)
python main.py --paths-file paths_batch.csv
File Formats
Format 1: Simple List (paths.txt)
One path per line, with optional per-row parameters:
P:\movies\Nobody 2 (2025)
P:\movies\The French Dispatch (2021) --r 720
P:\movies\Let's Be Cops (2014) --r 720 --cq 28
P:\movies\Akira (1988) --encoder av1 --strip-all-titles
## Batch Processing
### Overview
Process multiple folders sequentially with different parameters for each. This replaces the need for manual `.bat` files or shell scripts. Perfect for encoding your entire media library with specific settings per movie/show.
### Quick Start
#### Simple List Format (paths.txt)
One path per line, with optional per-row parameters:
P:\movies\Nobody 2 (2025) P:\movies\The French Dispatch (2021) --r 720 P:\movies\Let's Be Cops (2014) --r 720 --cq 28 P:\movies\Akira (1988) --encoder av1 --strip-all-titles
**Usage:**
```bash
python main.py --paths-file paths.txt
With base parameters (applied to all rows unless overridden):
python main.py --paths-file paths.txt --encoder hevc --strip-all-titles
CSV Format (paths_batch.csv)
First column is path, remaining columns are optional parameters:
path,resolution,cq,encoder,notes
"P:\movies\Nobody 2 (2025)","--r 1080","--cq 32","--encoder hevc","4K source"
"P:\movies\The French Dispatch (2021)","--r 720","--cq 28","--encoder av1","720p source"
"P:\movies\Let's Be Cops (2014)","--r 720"
"P:\movies\Akira (1988)","","--cq 26"
Usage:
python main.py --paths-file paths_batch.csv
The CSV header row is ignored - use it for your own documentation.
Supported List Formats
Simple List (One per line)
# Path only (uses base parameters)
P:\movies\Movie1
# Path with space-separated parameters
P:\movies\Movie2 --r 720
P:\movies\Movie3 --r 720 --cq 28 --encoder av1
# Quoted paths with spaces
"P:\movies\Movie with Spaces" --r 720
# Comment out paths with #
# P:\movies\Disabled --r 480
CSV Format (Recommended for complex setups)
First column is always the path. Remaining columns can be any parameters:
path,resolution,quality,encoder,language
"P:\movies\Movie1","--r 1080","--cq 32","--encoder hevc","--language eng"
"P:\movies\Movie2","--r 720","--cq 28","--encoder av1"
"P:\movies\Movie3","--r 720"
Per-Row Parameter Override
Row parameters override base CLI parameters for that specific path.
Example:
# Base command with --strip-all-titles applied to all
python main.py --paths-file paths.txt --strip-all-titles
With paths.txt:
P:\movies\Movie1 # Uses --strip-all-titles from base
P:\movies\Movie2 --keep-all-titles # OVERRIDES to keep titles, still strips
P:\movies\Movie3 --encoder av1 # OVERRIDES encoder to av1
P:\movies\Movie4 --r 720 --cq 28 # OVERRIDES resolution and quality
All Supported Per-Row Parameters
Any CLI parameter can be used in rows:
--r {480,720,1080} # Resolution
--cq <value> # CQ quality (0-51, lower is better)
--m {cq,bitrate} # Encoding mode
--encoder {av1,hevc} # Video encoder
--audio-channels "0:2,1:6" # Force audio channels
--audio-titles "0:English,1:Commentary" # Audio track names
--audio-select "0,1" # Pre-select audio streams
--language eng # Language tag
--filter-audio # Enable audio filtering
--strip-all-titles # Remove audio titles
--keep-all-titles # Preserve audio titles
--unforce-subs # Remove forced subtitle flag
--no-encode # Mux only, no encoding
--test # Test mode (first file only)
--crop-height <pixels> # Crop to height
--color-bit {8,10} # Color bit depth (HEVC only)
Real-World Examples
Example 1: Movie Library with Varying Quality
movies.txt:
P:\movies\4K Movies\Movie1 --r 1080 --cq 26
P:\movies\4K Movies\Movie2 --r 1080 --cq 26
P:\movies\720p Movies\Movie3 --r 720 --cq 28
P:\movies\Anime\Anime1 --encoder av1 --cq 26
P:\movies\Anime\Anime2 --encoder av1 --cq 26 --audio-channels "0:2"
python main.py --paths-file movies.txt --encoder hevc --strip-all-titles
Example 2: TV Shows by Season
tv.csv:
path,params
"P:\tv\Breaking Bad\Season 1","--cq 26"
"P:\tv\Breaking Bad\Season 2","--cq 28"
"P:\tv\Breaking Bad\Season 3","--cq 28 --r 720"
"P:\tv\Game of Thrones\Season 1","--encoder hevc --cq 24"
"P:\tv\Game of Thrones\Season 2","--encoder hevc --cq 26"
python main.py --paths-file tv.csv --filter-audio
Example 3: Mixed Content with Language Tags
archive.txt:
# Movies
P:\content\Movies\Action\MovieA --r 1080 --cq 28 --encoder hevc
P:\content\Movies\Drama\MovieB --r 1080 --cq 26 --encoder hevc
# TV Shows
P:\content\TV\Show1\Season1 --cq 28 --filter-audio
P:\content\TV\Show2\Season1 --cq 26 --filter-audio --language eng
# Anime
P:\content\Anime\SeriesA --encoder av1 --cq 24
P:\content\Anime\SeriesB --encoder av1 --cq 26 --audio-channels "0:2"
python main.py --paths-file archive.txt --strip-all-titles
Batch Processing Output
During execution, you'll see detailed progress:
════════════════════════════════════════════════════════════════════════════════
BATCH MODE: Processing paths from paths.txt
════════════════════════════════════════════════════════════════════════════════
────────────────────────────────────────────────────────────────────────────────
BATCH [1/5]: P:\movies\Movie1
────────────────────────────────────────────────────────────────────────────────
[... encoding output ...]
✓ [BATCH 1/5] Completed: Movie1
────────────────────────────────────────────────────────────────────────────────
BATCH [2/5]: P:\movies\Movie2
Parameters: --r 720
────────────────────────────────────────────────────────────────────────────────
[... encoding output ...]
✓ [BATCH 2/5] Completed: Movie2
[... continues ...]
════════════════════════════════════════════════════════════════════════════════
✓ BATCH PROCESSING COMPLETE: Processed 5 path(s)
════════════════════════════════════════════════════════════════════════════════
Key features:
- Each path prefixed with
[BATCH X/Y]in logs - If a path fails, processing continues to next path
- Overall summary shows completion status
Batch Processing Tips & Best Practices
1. Test Before Full Batch
Use --test flag per-row to verify settings:
P:\movies\TestMovie --test --r 720 --cq 28
P:\movies\Movie1 --r 720 --cq 28
P:\movies\Movie2 --r 720 --cq 28
2. Start Simple, Add Complexity
# Phase 1: Just paths
python main.py --paths-file paths.txt
# Phase 2: Add base parameters
python main.py --paths-file paths.txt --encoder hevc
# Phase 3: Add per-row customization in the file
3. Monitor Progress
Check logs in logs/conversion.log during batch processing:
- Each entry includes
[BATCH X/Y]prefix - All parameters are logged per file
- Search for errors with the batch prefix
4. CSV Format Tips
- Quote paths with spaces:
"P:\path with spaces" - Leave empty cells for defaults:
"P:\movies\Movie1","--r 720"(uses default CQ) - Or combine in one cell:
"P:\movies\Movie1","--r 720 --cq 28"
5. Line Endings
- Both Unix (LF) and Windows (CRLF) line endings work
- Avoid mixing line endings in the same file
6. Encoding Issues
If you get encoding errors:
- Ensure file is saved as UTF-8
- Use English characters in parameter values
- Avoid special characters in paths
Migration from .bat Files
Old .bat approach:
python main.py "P:\movies\Movie1"
python main.py "P:\movies\Movie2" --r 720
python main.py "P:\movies\Movie3" --r 720 --cq 28
New --paths-file approach:
python main.py --paths-file paths.txt
With paths.txt:
P:\movies\Movie1
P:\movies\Movie2 --r 720
P:\movies\Movie3 --r 720 --cq 28
Benefits:
- Single command to start batch
- All parameters visible in one file
- Easier to edit and maintain
- Better error handling (continues on failure)
- Unified logging with batch context
Audio Options
Three Main Audio Control Methods
| Option | Purpose | Format | Example |
|---|---|---|---|
--audio-select |
Pre-select which streams to keep | Comma-separated indices | --audio-select "0,1,2" |
--audio-titles |
Rename/title tracks | index:title pairs |
--audio-titles "0:English,1:Commentary" |
--audio-channels |
Set channel count per track | index:channels pairs |
--audio-channels "0:2,1:6" |
Audio Filtering
Automatic Filtering (--filter-audio):
python main.py "C:\Videos" --filter-audio
- Automatically keeps best English audio + all Commentary tracks
- Removes other languages
- No user interaction
Interactive Filtering (--filter-audio --interactive):
python main.py "C:\Videos" --filter-audio --interactive
- Shows all audio streams for each file
- User selects which streams to keep
- Different selections allowed per file
Stream Display Format:
🎵 AUDIO STREAM SELECTION
================================================================================
Stream #0: 2ch | Lang: eng | Bitrate: 128kbps
Stream #1: 6ch | Lang: eng | Bitrate: 448kbps
Stream #2: 2ch | Lang: spa | Bitrate: 128kbps
────────────────────────────────────────────────────────────────────────────
Enter stream numbers to keep (comma-separated, e.g.: 1,2 or just 2)
Leave blank to keep all streams
────────────────────────────────────────────────────────────────────────────
➜ Keep streams: 1,3
Audio Channel Control
--audio-channels allows forcing specific channel configurations:
# Force stereo for all tracks
python main.py "C:\Videos" --audio-channels "0:2,1:2"
# Keep 5.1 main audio, compress commentary to stereo
python main.py "C:\Videos" --audio-channels "0:6,1:2" --audio-titles "0:English 5.1,1:Commentary"
Valid Channels: 2 (stereo) or 6 (5.1 surround)
Automatic Bitrate Selection:
| Resolution | Stereo (2ch) | 5.1 Surround (6ch) |
|---|---|---|
| 1080p | 192 kbps | 384 kbps |
| 720p | 160 kbps | 320 kbps |
Audio Filtering Priority
When determining output channels for a stream:
- User-specified via
--audio-channels(highest priority) - Commentary detection (if titled "Commentary", forces 2-channel)
- Auto-detection (default, based on resolution and source channels)
Common Audio Scenarios
Scenario 1: TV Show with Commentary
python main.py "C:\TV" \
--audio-channels "0:2,1:2" \
--audio-titles "0:English,1:Commentary"
Scenario 2: Movie Collection (Multiple Languages)
python main.py "C:\Movies" \
--audio-select "0,1,2" \
--audio-channels "0:6,1:2,2:2" \
--audio-titles "0:Japanese 5.1,1:English Stereo,2:Spanish Stereo"
Scenario 3: Anime (All Stereo)
python main.py "C:\Anime" --audio-channels "0:2,1:2,2:2"
Encoder Selection
Dual Encoder Support
AV1 NVENC (Default)
python main.py "C:\Videos" --encoder av1
- Codec: av1_nvenc
- Preset: p7 (high quality)
- Bit Depth: 8-bit
- Pixel Format: yuv420p
- File Size: Smallest (~baseline)
- Use Case: Maximum file size reduction, modern playback devices
HEVC NVENC
python main.py "C:\Videos" --encoder nvenc
- Codec: hevc_nvenc
- Preset: slow (high quality)
- Bit Depth: 10-bit
- Pixel Format: yuv420p10le
- File Size: ~80-90% of AV1 size
- Use Case: Best quality archival, excellent Plex compatibility
Quality Comparison
| Aspect | HEVC NVENC | AV1 NVENC |
|---|---|---|
| File Size | ~80-90% of AV1 | Smallest (baseline) |
| Quality | Excellent | Excellent |
| Preset | slow (p6) | p7 |
| Bit Depth | 10-bit | 8-bit |
| Compatibility | Excellent (Plex) | Good (modern devices) |
| Encoding Speed | Fast | Fast |
⚙️ Configuration
Edit config.xml to customize encoding parameters:
Video Encoder Settings
<encoder default="av1">
<av1_nvenc preset="p7" bit_depth="8" pix_fmt="yuv420p" />
<hevc_nvenc preset="slow" bit_depth="10" pix_fmt="yuv420p10le" />
</encoder>
CQ Quality Per Content Type
<cq>
<tv_1080>28</tv_1080>
<tv_720>32</tv_720>
<movie_1080>32</movie_1080>
<movie_720>34</movie_720>
</cq>
Audio Bitrate Buckets
<audio>
<stereo>
<high>192000</high>
<medium>160000</medium>
</stereo>
<multi_channel>
<medium>448000</medium>
<low>384000</low>
</multi_channel>
</audio>
Bitrate Fallback (Phase 2)
<fallback>
<bitrate_1080>8000</bitrate_1080>
<bitrate_720>6000</bitrate_720>
<bitrate_480>4000</bitrate_480>
</fallback>
Subtitle Detection
<subtitles>
<enabled>true</enabled>
<extensions>.vtt,.srt,.ass,.ssa,.sub</extensions>
<codec>subrip</codec> <!-- subrip for .srt format -->
</subtitles>
General Settings
<general>
<processing_folder>.\processing</processing_folder>
<suffix> - [EHX]</suffix>
<video_extensions>.mkv,.mp4,.avi,.m2ts</video_extensions>
<reduction_ratio_threshold>0.75</reduction_ratio_threshold>
</general>
Project Structure
conversion_project/
├── main.py - CLI entry point for batch transcoding
├── config.xml - Configuration (encoding settings, audio buckets, etc.)
│
├── core/ - Core modules
│ ├── config_helper.py - XML configuration loader
│ ├── logger_helper.py - Logging setup (JSON structured logs)
│ ├── process_manager.py - Main transcoding orchestration
│ ├── encode_engine.py - FFmpeg command builder and execution
│ ├── audio_handler.py - Audio stream analysis, bitrate decisions, interactive selection
│ ├── video_handler.py - Video resolution detection and scaling logic
│ └── hardware_helper.py - Hardware detection (GPU/CPU)
│
├── /rename/ - Separate rename utility (rolling_rename.py)
├── /path_manager/ - GUI path management
│ ├── gui_path_manager.py
│ ├── transcode.bat
│ ├── paths.txt
│ └── cache/
│
├── logs/ - Log files and conversion tracker CSV
├── processing/ - Temporary encoding files (cleaned up after move)
└── README.md - This file
Encoding Process
Per-File Workflow
For each video file in the target folder:
-
Detect Subtitles
- Looks for exact match:
Video.vtt,Video.srt, etc. - Also searches for language-prefixed:
Video.en.vtt,Video.eng.vtt
- Looks for exact match:
-
Analyze Source
- Detect resolution (width × height)
- Extract audio streams (codec, channels, bitrate, language)
- Extract video codec and frame rate
-
Determine Parameters
- Target Resolution: Scale 4K→1080p, preserve lower resolutions (or use
--roverride) - CQ Value: Select based on content type and resolution (tv_1080, movie_720, etc.)
- Audio Processing:
- Apply filtering/selection if specified
- Choose codec (AAC for stereo, EAC3 for 5.1)
- Choose bitrate (depends on resolution and stream quality)
- Target Resolution: Scale 4K→1080p, preserve lower resolutions (or use
-
FFmpeg Encode
- Video: AV1 NVENC 8-bit yuv420p (or HEVC NVENC 10-bit)
- Audio: Per-stream decisions (copy if good quality, re-encode if excessive bitrate)
- Subtitles: Embed as SRT (if found)
-
Size Validation
- Compare output size vs original size
- If output > 75% of original (default threshold), retry with Phase 2 (bitrate mode)
-
Move File
- Move from temp folder → original location
- Add
- [EHX]suffix to filename - Delete original file and subtitle file
-
Logging
- Record to CSV tracker: filename, original size, output size, compression ratio
- Structured JSON logging with media context (show name, season, episode)
Audio Encoding Logic
For each audio stream:
Stereo audio (2 channels)?
├─ YES + 1080p:
│ ├─ If >192kbps → ENCODE to 192 kbps AAC
│ └─ If ≤192kbps → COPY (no re-encoding)
├─ YES + 720p:
│ ├─ If >160kbps → ENCODE to 160 kbps AAC
│ └─ If ≤160kbps → COPY (no re-encoding)
│
└─ NO (Multichannel/5.1+):
├─ ENCODE to 384 kbps or 448 kbps EAC3 (depending on resolution)
📊 Example Output
Input File:
SupernaturalS07E21.mkv (size: 1.5GB, 6-channel AC3 384kbps, 1920x1080)
SupernaturalS07E21.en.vtt (subtitle)
Processing:
Detected: 1920x1080 (1080p) → TV episode CQ 28
Audio: 6ch AC3 384kbps (needs re-encoding for 5.1)
Subtitle: Detected SupernaturalS07E21.en.vtt
Output File:
SupernaturalS07E21 - [EHX].mkv (size: 420MB, EAC3 384kbps, subtitle embedded)
Compression: 1500MB → 420MB (72% reduction)
Troubleshooting
Common Issues
"FFmpeg not found"
- Ensure FFmpeg is installed and in your system PATH
- Test with:
ffmpeg -version
"GPU out of memory"
- NVIDIA GPU is full, close other GPU applications
- Or reduce resolution with
--r 720
"Subtitle not embedding"
- Ensure subtitle file is in same folder as video
- Check subtitle extension is in config.xml (vtt, srt, ass, etc.)
- Verify filename matches exactly (e.g.,
Video.en.vtt)
"No audio streams detected"
- Run
ffprobe input.mkvto verify audio streams exist - Audio may be embedded as data track, not audio track
"CUDA Kernel Error" during encoding
- GPU driver is outdated, update NVIDIA drivers
- GPU may be overheating, check temperature
"Output file larger than original"
- Phase 1 (CQ mode) exceeded size threshold
- Phase 2 (bitrate mode) will retry automatically
- Or manually increase CQ value (lower quality) in config.xml
Advanced Features
Test Mode
Encodes first file only without moving it:
python main.py "P:\tv\Show" --test
Use Case: Test CQ values, check quality before batch processing entire library.
Language Tagging
Tags audio streams with language metadata (only if specified):
python main.py "P:\tv\Show" --language eng
Without the flag, original audio metadata is preserved.
Resolution Override
Force specific output resolution regardless of source:
python main.py "P:\movies" --r 720 # Force 720p
python main.py "P:\tv" --r 1080 # Force 1080p
CSV Conversion Tracking
All conversions logged to CSV file with:
- Filename
- Original size
- Output size
- Compression ratio
- Encoding method (CQ/Bitrate)
- Duration
- Timestamp
Logging
The system uses structured JSON logging with media context:
Log Fields
- timestamp: When the conversion occurred
- level: INFO, WARNING, ERROR
- message: Human-readable conversion status
- video_filename: Original filename
- media_type: "tv", "anime", "movie", or "other"
- show_name: Extracted from path (e.g., "Breaking Bad")
- season: TV/anime season number (optional)
- episode: TV/anime episode number (optional)
- method: "CQ" or "Bitrate"
- original_size_mb: Input file size
- output_size_mb: Output file size
- reduction_pct: Compression percentage
Log Files
- logs/conversion.log: Full structured JSON logs (INFO level and above)
- logs/conversion_failures.log: Error and failure logs only
- conversion_tracker.csv: Human-readable conversion summary
Recent Changes
Latest Updates
✅ Dual Encoder Support - Switch between AV1 and HEVC with --encoder flag
✅ Interactive Audio Selection - Manually choose which audio streams to keep
✅ Audio Channel Control - Force specific channel counts (2-ch stereo or 6-ch 5.1)
✅ Audio Stream Titles - Rename/tag individual audio tracks
✅ Structured JSON Logging - Media context in all logs (show name, season, episode)
✅ Subtitle Embedding - Auto-detect and embed subtitles with language prefixes
✅ Test Mode - Encode first file to verify settings before batch processing
Removed Features
❌ Sonarr/Radarr integration (simplified for reliability) ❌ Auto-rename based on external metadata ❌ Web UI (can be added back if needed)
📊 Example Output
Input:
Show.S01E01.mkv (1.5GB)
Show.S01E01.en.vtt (subtitle)
Output:
Show.S01E01 - [EHX].mkv (450MB, subtitle embedded, audio tagged)
Compression: 1.5GB → 450MB (30% ratio, 70% reduction)
🔧 Encoding Specs
| Setting | Value |
|---|---|
| Video Codec | AV1 (av1_nvenc) |
| Bit Depth | 8-bit (yuv420p) |
| GPU Preset | p1 (high quality) |
| Audio Codec | AAC |
| Audio Mode | Smart (copy or re-encode) |
| Container | MKV |
| Subtitles | Embedded SRT |
🎯 Workflow
- Scan folder for video files
- Detect subtitles, audio streams, resolution
- Encode with AV1 codec (Phase 1: CQ)
- Check size threshold (default 75%)
- Retry with Bitrate mode if needed (Phase 2)
- Move encoded file to original location
- Cleanup original + subtitles + temp files
- Log results to CSV tracker
📋 Requirements
- Windows 10/11 or Linux
- NVIDIA GPU with NVENC support
- NVIDIA CUDA Toolkit 11.0+
- FFmpeg compiled with av1_nvenc support
- Python 3.8+
🛠️ Troubleshooting
Files not moving?
- Check
reduction_ratio_thresholdin config.xml (default 0.75) - Run with
--testto see compression ratio
Subtitles not embedding?
- Verify filename:
video.en.vttorvideo.vtt - Check config.xml
<subtitles><enabled>true</enabled>
Wrong audio quality?
- Adjust CQ values in config.xml per content type
- Use
--cqoverride:python main.py folder --cq 30
"FFmpeg not found"
- Ensure FFmpeg is installed and in your system PATH
- Test with:
ffmpeg -version
"GPU out of memory"
- NVIDIA GPU is full, close other GPU applications
- Or reduce resolution with
--r 720
"No audio streams detected"
- Run
ffprobe input.mkvto verify audio streams exist - Audio may be embedded as data track, not audio track
"CUDA Kernel Error" during encoding
- GPU driver is outdated, update NVIDIA drivers
- GPU may be overheating, check temperature
"Output file larger than original"
- Phase 1 (CQ mode) exceeded size threshold
- Phase 2 (bitrate mode) will retry automatically
- Or manually increase CQ value (lower quality) in config.xml
📄 License
MIT