1062 lines
32 KiB
Markdown
1062 lines
32 KiB
Markdown
# 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](#-key-features)
|
||
2. [Quick Start](#-quick-start)
|
||
3. [Installation](#installation)
|
||
4. [Basic Usage](#basic-usage)
|
||
5. [Command Reference](#command-reference)
|
||
6. [Batch Processing](#batch-processing)
|
||
7. [Audio Options](#audio-options)
|
||
8. [Undefined Language Replacement](#undefined-language-und-replacement)
|
||
9. [Encoder Selection](#encoder-selection)
|
||
10. [Configuration](#configuration)
|
||
11. [Project Structure](#project-structure)
|
||
12. [Encoding Process](#encoding-process)
|
||
13. [Troubleshooting](#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
|
||
|
||
```bash
|
||
# 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
|
||
|
||
```bash
|
||
# 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
|
||
```bash
|
||
--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
|
||
```bash
|
||
--cq <value> # Use CQ mode with specific quality (0-51, lower=better)
|
||
--m {cq,bitrate} # Force encoding mode (default: smart two-phase)
|
||
```
|
||
|
||
### Resolution
|
||
```bash
|
||
--r {480,720,1080} # Force specific output resolution
|
||
# Default: scale 4K→1080p, preserve lower
|
||
```
|
||
|
||
### Audio Options
|
||
```bash
|
||
--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:
|
||
|
||
```xml
|
||
<!-- 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:
|
||
```bash
|
||
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:
|
||
```bash
|
||
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**
|
||
```bash
|
||
# 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
|
||
```
|
||
```bash
|
||
python main.py --paths-file paths.txt
|
||
```
|
||
|
||
### Processing
|
||
```bash
|
||
--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
|
||
|
||
```bash
|
||
# 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):
|
||
```bash
|
||
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:
|
||
|
||
```csv
|
||
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:**
|
||
```bash
|
||
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:
|
||
|
||
```csv
|
||
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:**
|
||
|
||
```bash
|
||
# 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"
|
||
```
|
||
|
||
```bash
|
||
python main.py --paths-file movies.txt --encoder hevc --strip-all-titles
|
||
```
|
||
|
||
**Example 2: TV Shows by Season**
|
||
|
||
`tv.csv`:
|
||
```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"
|
||
```
|
||
|
||
```bash
|
||
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"
|
||
```
|
||
|
||
```bash
|
||
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
|
||
|
||
```bash
|
||
# 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:**
|
||
```batch
|
||
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:**
|
||
```bash
|
||
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`):
|
||
```bash
|
||
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`):
|
||
```bash
|
||
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:
|
||
|
||
```bash
|
||
# 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**
|
||
```bash
|
||
python main.py "C:\TV" \
|
||
--audio-channels "0:2,1:2" \
|
||
--audio-titles "0:English,1:Commentary"
|
||
```
|
||
|
||
**Scenario 2: Movie Collection (Multiple Languages)**
|
||
```bash
|
||
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)**
|
||
```bash
|
||
python main.py "C:\Anime" --audio-channels "0:2,1:2,2:2"
|
||
```
|
||
|
||
---
|
||
|
||
## Encoder Selection
|
||
|
||
### Dual Encoder Support
|
||
|
||
**AV1 NVENC (Default)**
|
||
```bash
|
||
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**
|
||
```bash
|
||
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
|
||
```xml
|
||
<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
|
||
```xml
|
||
<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
|
||
```xml
|
||
<audio>
|
||
<stereo>
|
||
<high>192000</high>
|
||
<medium>160000</medium>
|
||
</stereo>
|
||
<multi_channel>
|
||
<medium>448000</medium>
|
||
<low>384000</low>
|
||
</multi_channel>
|
||
</audio>
|
||
```
|
||
|
||
### Bitrate Fallback (Phase 2)
|
||
```xml
|
||
<fallback>
|
||
<bitrate_1080>8000</bitrate_1080>
|
||
<bitrate_720>6000</bitrate_720>
|
||
<bitrate_480>4000</bitrate_480>
|
||
</fallback>
|
||
```
|
||
|
||
### Subtitle Detection
|
||
```xml
|
||
<subtitles>
|
||
<enabled>true</enabled>
|
||
<extensions>.vtt,.srt,.ass,.ssa,.sub</extensions>
|
||
<codec>subrip</codec> <!-- subrip for .srt format -->
|
||
</subtitles>
|
||
```
|
||
|
||
### General Settings
|
||
```xml
|
||
<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:
|
||
|
||
```bash
|
||
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):
|
||
|
||
```bash
|
||
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:
|
||
|
||
```bash
|
||
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
|
||
|
||
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
|