# 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
