2026-08-22 00:58:40 -04:00
2026-02-21 10:43:42 -05:00
2026-01-01 15:37:38 -05:00
2026-08-22 00:58:40 -04:00
2026-05-17 21:21:28 -04:00
2026-08-22 00:58:40 -04:00
2026-05-17 21:21:28 -04:00
2025-10-04 23:46:10 -04:00
2026-08-20 10:51:14 -04:00
2026-08-20 10:51:14 -04:00
2026-08-20 10:51:14 -04:00
2026-08-20 10:51:14 -04:00
2026-08-22 00:58:40 -04:00
2026-08-22 00:58:40 -04:00
2026-05-17 21:21:28 -04:00
2026-08-20 10:51:14 -04:00
2026-08-20 10:51:14 -04:00
2026-08-22 00:58:40 -04:00
2026-08-20 10:51:14 -04:00
2026-05-17 21:21:28 -04:00
2026-08-20 10:51:14 -04:00
2026-08-20 10:51:14 -04:00
2026-08-20 10:51:14 -04:00
2026-08-20 10:51:14 -04:00
2026-08-20 10:51:14 -04:00
2026-08-20 10:51:14 -04:00
2026-08-20 10:51:14 -04:00
2026-08-22 00:58:40 -04:00

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

  1. Key Features
  2. Quick Start
  3. Installation
  4. Basic Usage
  5. Command Reference
  6. Batch Processing
  7. Audio Options
  8. Undefined Language Replacement
  9. Encoder Selection
  10. Configuration
  11. Project Structure
  12. Encoding Process
  13. Advanced Features
  14. Logging
  15. 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)
  • Subtitle Cleanup - Remove forced flags with --unforce-subs
  • 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
  • File Replacement - Replace originals or create suffix versions with --replace
  • Move Mode - Save disk space by moving files during processing with --move
  • Keep Original - Preserve source files after encoding with --keep-original

Advanced Features

  • Video Cropping - Center-crop to remove letterboxing with --crop
  • Travel Mode - 720p optimized encoding for portable drives with --travel
  • Mux-Only Mode - Re-mux without encoding using --no-encode
  • Network Retry - Auto-retry for temporarily unavailable network sources
  • Plex Integration - Wait intervals between files for Plex detection

🚀 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):

  1. --language eng (CLI): Tags ALL audio streams as 'eng'
  2. --default-language spa + detected und: Replaces with 'spa'
  3. config.xml default_language: eng + detected und: Replaces with 'eng'
  4. --no-replace-und flag: Skips all und replacement

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

Output & File Options

--title-suffix <TEXT>  # Text inserted before [EHX] suffix (e.g., "1080p", "v2", "WebRip")
                       # Output: "Movie Name - 1080p [EHX].mkv"
                       # If not specified, uses config.xml setting (default: empty)

Using Title Suffix:

# Single folder with suffix
python main.py "P:\movies\Collection" --title-suffix "WebRip-1080p"

# Batch mode with different suffixes per item
# paths.txt:
P:\movies\Movie1 --title-suffix "1080p"
P:\movies\Movie2 --title-suffix "4K"
P:\tv\Show --title-suffix "WebRip-1080p"

python main.py --paths-file paths.txt

Output Examples:

Input:  Movie Name.mkv
Output: Movie Name - 1080p [EHX].mkv (with --title-suffix "1080p")

Input:  Show.S01E01.mkv
Output: Show.S01E01 - WebRip-1080p [EHX].mkv (with --title-suffix "WebRip-1080p")

Input:  Video.mkv
Output: Video - [EHX].mkv (no --title-suffix, default behavior)

Audio Titles & Track Control

--keep-all-titles      # Preserve title metadata on audio tracks (default: titles are stripped)
--strip-all-titles     # Remove audio titles (default behavior)

Subtitle Control

--unforce-subs         # Remove forced flag from all subtitle tracks

Encoding Control

--no-encode            # Skip encoding: copy video/audio streams as-is (mux-only mode)
                       # Useful with --unforce-subs to only re-mux subtitles
--ignore-tags          # Process files even if they contain ignore tags (e.g., already encoded files)
--force-encode         # Reconvert files even if size threshold is not met

File Handling

--replace              # Replace original file instead of creating suffix version
                       # Requires --no-encode (mux-only mode)
--move                 # Move source files to processing folder instead of copying
                       # Saves disk space; files deleted after processing unless --keep-original set
--keep-original        # Preserve original source files after processing instead of deleting

Processing & Wait Control

--test                 # Test mode: encode first file, show compression ratio, don't move
--wait, -w [seconds]   # Wait after each file to give Plex time to detect changes
                       # Default: 30s with --no-encode, 0s otherwise
--crop <pixels>        # Center-crop video to target height in pixels
                       # Example: --crop 816 for 1920x816 from 1920x1080

Travel Mode

--travel               # Travel mode: force 720p resolution and CQ+2 quality
                       # Requires --output flag for destination folder
--output <folder>      # Output folder for travel mode (creates subfolder based on input folder name)

Travel Mode Examples:

# Encode with travel-friendly settings (720p, optimized quality)
python main.py "P:\movies" --travel --output "D:\External\Movies"
# Result: Creates D:\External\Movies\Movies folder with 720p encoded files

Queue & Retry Mode

--retry-minutes <N>    # Minutes to wait between retry attempts when batch fails
                       # Triggered when 2+ 'Folder not found' errors occur
                       # Useful for network sources that may be temporarily unavailable
                       # Default: from config.xml if not specified
--retry-timeout <N>    # Total minutes to keep retrying before giving up
                       # Default: from config.xml if not specified
                       # If source becomes reachable during retry, queue restarts immediately

Queue/Retry Mode Examples:

# Batch mode with automatic retry for network sources
python main.py --paths-file network_paths.txt --retry-minutes 5 --retry-timeout 120
# Will retry every 5 minutes for up to 2 hours if network paths are temporarily unavailable

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:

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

Usage:

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

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,2160}                  # Resolution (max, downscales if source larger)
--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 (default)
--keep-all-titles                        # Preserve audio titles
--unforce-subs                           # Remove forced subtitle flag
--title-suffix "1080p"                   # Add custom text before [EHX] suffix
--no-encode                              # Mux only, no encoding
--ignore-tags                            # Process files with ignore tags
--force-encode                           # Reconvert even if size threshold not met
--replace                                # Replace original instead of suffix (with --no-encode)
--move                                   # Move source to processing folder (saves space)
--keep-original                          # Preserve original files after processing
--test                                   # Test mode (first file only)
--crop <pixels>                          # Center-crop video to height
--wait [seconds]                         # Wait after each file
--travel                                 # Travel mode: 720p + CQ+2
--output <folder>                        # Output folder for travel mode

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:

  1. User-specified via --audio-channels (highest priority)
  2. Commentary detection (if titled "Commentary", forces 2-channel)
  3. 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:

  1. Detect Subtitles

    • Looks for exact match: Video.vtt, Video.srt, etc.
    • Also searches for language-prefixed: Video.en.vtt, Video.eng.vtt
  2. Analyze Source

    • Detect resolution (width × height)
    • Extract audio streams (codec, channels, bitrate, language)
    • Extract video codec and frame rate
  3. Determine Parameters

    • Target Resolution: Scale 4K→1080p, preserve lower resolutions (or use --r override)
    • 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)
  4. 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)
  5. Size Validation

    • Compare output size vs original size
    • If output > 75% of original (default threshold), retry with Phase 2 (bitrate mode)
  6. Move File

    • Move from temp folder → original location
    • Add - [EHX] suffix to filename
    • Delete original file and subtitle file
  7. 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.mkv to 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

Video Cropping

Center-crop video to a specific height (useful for letterboxed content):

# Crop 1920x1080 video to 1920x816 (removes letterboxing equally from top/bottom)
python main.py "P:\movies" --crop 816

# Crop 4K to specific height
python main.py "P:\movies" --crop 1440

Use Case: Remove black bars from films while preserving aspect ratio.

File Replacement & Move Modes

Replace Mode - Replace original instead of creating suffix version:

# With --no-encode: re-mux without encoding
python main.py "P:\movies" --no-encode --replace --unforce-subs
# Result: Original.mkv is replaced (no -[EHX] suffix)

Move Mode - Save disk space by moving files instead of copying:

# Move to processing folder instead of copying (saves ~2x space during encoding)
python main.py "P:\movies" --move

# Keep original files after processing (default: deletes after successful encoding)
python main.py "P:\movies" --move --keep-original

Use Case: When encoding large libraries on limited disk space.

Subtitle Cleanup

Remove Forced Subtitle Flags:

# Re-mux without encoding, only remove forced flag
python main.py "P:\movies" --no-encode --unforce-subs

# Or with encoding
python main.py "P:\movies" --unforce-subs

Use Case: Normalize subtitle flags across your media library.

Ignore & Force Encode

Process Already-Encoded Files:

# Ignore files with encoding suffix (e.g., files already tagged as -[EHX])
python main.py "P:\movies" --ignore-tags

# Force re-encoding even if size threshold wouldn't allow it
python main.py "P:\movies" --force-encode

Use Case: Re-process files with different parameters or recover from partial batch runs.

Audio Title Preservation

Keep or Strip Audio Titles:

# Preserve title metadata on audio tracks
python main.py "P:\movies" --keep-all-titles

# Strip all titles (default)
python main.py "P:\movies"  # or --strip-all-titles

Use Case: Maintain descriptive audio track names vs. clean generic metadata.

Travel Mode

Encode with travel-optimized settings (smaller files for external drives):

# Create 720p optimized for portable use
python main.py "P:\movies" --travel --output "D:\External\Movies"
# Result: Automatic 720p + CQ+2 (slightly lower quality for smaller size)

Travel Mode Behavior:

  • Automatically forces 720p resolution
  • Uses CQ+2 (slightly lower quality)
  • Creates subfolder based on input folder name
  • Useful for portable media collections

Use Case: Encode your library for travel on limited-capacity drives.

Plex-Friendly Wait Intervals

Give Plex time to detect file changes:

# Wait 30 seconds after each file (useful with --no-encode to let Plex detect changes)
python main.py "P:\movies" --no-encode --wait

# Wait custom duration
python main.py "P:\movies" --wait 60  # Wait 60 seconds between files

Use Case: When processing Plex library in real-time without full library refresh.

Network Source Reliability (Queue & Retry)

Handle temporarily unavailable network sources:

# Batch with automatic retry for network paths
python main.py --paths-file network_paths.txt --retry-minutes 5 --retry-timeout 120

# Retry every 5 minutes for up to 2 hours
# Useful for mounted NAS/cloud storage that may disconnect

Retry Behavior:

  • Triggered when 2+ "Folder not found" errors occur
  • Retries at specified interval (default: from config.xml)
  • Continues retrying until timeout or path becomes reachable
  • Automatically restarts batch when source comes back online

Use Case: Robust batch processing for unreliable network sources (NAS, cloud, etc).


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

  1. Scan folder for video files
  2. Detect subtitles, audio streams, resolution
  3. Encode with AV1 codec (Phase 1: CQ)
  4. Check size threshold (default 75%)
  5. Retry with Bitrate mode if needed (Phase 2)
  6. Move encoded file to original location
  7. Cleanup original + subtitles + temp files
  8. 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_threshold in config.xml (default 0.75)
  • Run with --test to see compression ratio

Subtitles not embedding?

  • Verify filename: video.en.vtt or video.vtt
  • Check config.xml <subtitles><enabled>true</enabled>

Wrong audio quality?

  • Adjust CQ values in config.xml per content type
  • Use --cq override: 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.mkv to 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

Description
No description provided
Readme 5.8 MiB
Languages
Python 97.2%
Batchfile 2.8%