Updated lt-convert.sh
Amended correct file
This commit is contained in:
parent
fa6ff5aba1
commit
18d3658d55
|
|
@ -1,43 +1,57 @@
|
|||
#!/bin/bash
|
||||
# ===================================================================
|
||||
# LT Converter v3.0 — Professional Edition (December 2025)
|
||||
# GIT Converter v2.7 — Professional Edition (December 2025)
|
||||
# Author: LeakTechnologies
|
||||
# Fixed: Converted folder always created in script directory
|
||||
# Fixed: Filter chaining, input validation, bitrate handling
|
||||
# Modular Architecture for optimal performance
|
||||
# ===================================================================
|
||||
|
||||
# Force window size in Git Bash on Windows
|
||||
# Force window size and font in Git Bash on Windows
|
||||
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "win32" ]]; then
|
||||
printf '\e[8;45;100t'
|
||||
printf '\e[8;100;80t' # Window size 1000x800
|
||||
printf '\e[14]' # Font size 14
|
||||
fi
|
||||
|
||||
# Get the directory where the script is located
|
||||
# Get directory where the script is located
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# Always create Converted folder in the script's directory
|
||||
OUT="$SCRIPT_DIR/Converted"
|
||||
mkdir -p "$OUT"
|
||||
|
||||
# Detect optimal encoder at startup
|
||||
optimal_encoder=$(detect_hardware)
|
||||
# Source modules
|
||||
source "$(dirname "$0")/modules/hardware.sh"
|
||||
source "$(dirname "$0")/modules/codec.sh"
|
||||
source "$(dirname "$0")/modules/quality.sh"
|
||||
source "$(dirname "$0")/modules/filters.sh"
|
||||
source "$(dirname "$0")/modules/encode.sh"
|
||||
|
||||
# Hardware detection and encoder selection
|
||||
detect_hardware() {
|
||||
echo "Detecting hardware and optimal encoder..."
|
||||
# Auto-detect encoder function
|
||||
auto_detect_encoder() {
|
||||
echo "Detecting hardware and optimal encoder..." >&2
|
||||
|
||||
# Detect GPU type
|
||||
gpu_type="none"
|
||||
|
||||
# Try NVIDIA detection
|
||||
if command -v nvidia-smi >/dev/null 2>&1 && nvidia-smi >/dev/null 2>&1; then
|
||||
gpu_type="nvidia"
|
||||
echo " ✓ NVIDIA GPU detected"
|
||||
elif lspci 2>/dev/null | grep -iq "amd\|radeon"; then
|
||||
echo " ✓ NVIDIA GPU detected" >&2
|
||||
# Try AMD detection with multiple methods
|
||||
elif command -v lspci >/dev/null 2>&1 && lspci 2>/dev/null | grep -iq "amd\|radeon\|advanced micro devices"; then
|
||||
gpu_type="amd"
|
||||
echo " ✓ AMD GPU detected"
|
||||
elif lspci 2>/dev/null | grep -iq "intel.*vga\|intel.*display"; then
|
||||
echo " ✓ AMD GPU detected (via lspci)" >&2
|
||||
elif command -v wmic >/dev/null 2>&1 && wmic path win32_VideoController get name 2>/dev/null | grep -iq "amd\|radeon"; then
|
||||
gpu_type="amd"
|
||||
echo " ✓ AMD GPU detected (via wmic)" >&2
|
||||
# Try Intel detection
|
||||
elif command -v lspci >/dev/null 2>&1 && lspci 2>/dev/null | grep -iq "intel.*vga\|intel.*display"; then
|
||||
gpu_type="intel"
|
||||
echo " ✓ Intel GPU detected"
|
||||
echo " ✓ Intel GPU detected (via lspci)" >&2
|
||||
elif command -v wmic >/dev/null 2>&1 && wmic path win32_VideoController get name 2>/dev/null | grep -iq "intel"; then
|
||||
gpu_type="intel"
|
||||
echo " ✓ Intel GPU detected (via wmic)" >&2
|
||||
else
|
||||
echo " ⚠ No GPU detected, will use CPU encoding"
|
||||
echo " ⚠ No GPU detected, will use CPU encoding" >&2
|
||||
fi
|
||||
|
||||
# Test encoder availability and speed
|
||||
|
|
@ -56,38 +70,41 @@ detect_hardware() {
|
|||
# Quick benchmark each encoder
|
||||
for enc in "${candidates[@]}"; do
|
||||
if ffmpeg -hide_banner -loglevel error -encoders | grep -q "$enc"; then
|
||||
echo " Testing $enc..."
|
||||
start_time=$(date +%s.%N)
|
||||
if ffmpeg -hide_banner -loglevel error -y -f lavfi -i "testsrc=duration=2:size=320x240:rate=1" \
|
||||
echo " Testing $enc..." >&2
|
||||
start_time=$(date +%s)
|
||||
if timeout 10 ffmpeg -hide_banner -loglevel error -y -f lavfi -i "testsrc=duration=1:size=320x240:rate=1" \
|
||||
-c:v "$enc" -f null - >/dev/null 2>&1; then
|
||||
end_time=$(date +%s.%N)
|
||||
test_time=$(echo "$end_time - $start_time" | bc -l 2>/dev/null || echo "1")
|
||||
echo " $enc: ${test_time}s"
|
||||
if (( $(echo "$test_time < $best_time" | bc -l 2>/dev/null || echo "0") )); then
|
||||
end_time=$(date +%s)
|
||||
test_time=$((end_time - start_time))
|
||||
echo " $enc: ${test_time}s (SUCCESS)" >&2
|
||||
if [[ $test_time -lt $best_time ]]; then
|
||||
best_time=$test_time
|
||||
best_encoder=$enc
|
||||
fi
|
||||
else
|
||||
echo " $enc: FAILED" >&2
|
||||
fi
|
||||
else
|
||||
echo " $enc: NOT AVAILABLE" >&2
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -n "$best_encoder" ]]; then
|
||||
echo " ✓ Selected: $best_encoder (fastest encoder)"
|
||||
echo " ✓ Selected: $best_encoder (fastest encoder)" >&2
|
||||
echo "$best_encoder"
|
||||
return 0
|
||||
else
|
||||
echo " ⚠ No working encoder found, defaulting to libx265"
|
||||
echo " ⚠ No working encoder found, defaulting to libx265" >&2
|
||||
echo "libx265"
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
# Display header
|
||||
clear
|
||||
cat << "EOF"
|
||||
|
||||
╔═══════════════════════════════════════════════════════════════════════════════╗
|
||||
║ ║
|
||||
║ LT Converter v3.0 (December 2025) ║
|
||||
║ GIT Converter v2.7 (December 2025) ║
|
||||
║ by LeakTechnologies ║
|
||||
║ ║
|
||||
║ High-quality batch conversion with hardware acceleration ║
|
||||
|
|
@ -103,205 +120,110 @@ cat << "EOF"
|
|||
|
||||
EOF
|
||||
|
||||
# — Resolution —
|
||||
echo " Choose your resolution:"
|
||||
# Auto-detect encoder first
|
||||
echo -e "\n🔍 Auto-detecting optimal encoder..."
|
||||
optimal_encoder=$(auto_detect_encoder)
|
||||
echo "✅ Selected: $optimal_encoder"
|
||||
|
||||
echo
|
||||
echo " 1) Source file resolution (no upscale)"
|
||||
echo " 2) 720p (1280×720)"
|
||||
echo " 3) 1080p (1920×1080)"
|
||||
echo " 4) 1440p (2560×1440)"
|
||||
echo " 5) 4K (3840×2160)"
|
||||
echo " 6) 2X Upscale"
|
||||
echo " 7) 4X Upscale"
|
||||
echo "Press space to continue..."
|
||||
read -n 1 -s
|
||||
|
||||
# Get user settings
|
||||
echo
|
||||
echo "╔═══════════════════════════════════════════════════════════════╗"
|
||||
echo "║ Choose Encoder/GPU ║"
|
||||
echo "╚═══════════════════════════════════════════════════════════════╝"
|
||||
echo
|
||||
echo " 1) Keep auto-detected: $optimal_encoder"
|
||||
echo " 2) NVIDIA HEVC NVENC"
|
||||
echo " 3) NVIDIA AV1 NVENC"
|
||||
echo " 4) AMD HEVC AMF"
|
||||
echo " 5) AMD AV1 AMF"
|
||||
echo " 6) Intel HEVC Quick Sync"
|
||||
echo " 7) Intel AV1 Quick Sync"
|
||||
echo " 8) CPU SVT-AV1"
|
||||
echo " 9) CPU x265 HEVC"
|
||||
echo " 10) Custom encoder selection"
|
||||
echo
|
||||
|
||||
while true; do
|
||||
read -p " Enter 1–7 → " res
|
||||
if [[ -n "$res" && "$res" =~ ^[1-7]$ ]]; then
|
||||
read -p " Enter 1–10 → " enc_choice
|
||||
if [[ -n "$enc_choice" && "$enc_choice" =~ ^([1-9]|10)$ ]]; then
|
||||
break
|
||||
else
|
||||
echo " Invalid input. Please enter a number between 1 and 7."
|
||||
echo " Invalid input. Please enter a number between 1 and 10."
|
||||
fi
|
||||
done
|
||||
|
||||
case $res in
|
||||
1) scale="" ; res_name="Source" ;;
|
||||
2) scale="1280:720" ; res_name="720p" ;;
|
||||
3) scale="1920:1080" ; res_name="1080p" ;;
|
||||
4) scale="2560:1440" ; res_name="1440p" ;;
|
||||
5) scale="3840:2160" ; res_name="4K" ;;
|
||||
6) scale="iw*2:ih*2" ; res_name="2X" ;;
|
||||
7) scale="iw*4:ih*4" ; res_name="4X" ;;
|
||||
esac
|
||||
|
||||
# — Scaling Algorithm (only if upscaling) —
|
||||
if [[ -n "$scale" ]]; then
|
||||
clear
|
||||
cat << "EOF"
|
||||
╔═══════════════════════════════════════════════════════════════╗
|
||||
║ Choose Scaling Algorithm ║
|
||||
╚═══════════════════════════════════════════════════════════════╝
|
||||
|
||||
1) Bicubic (fast, good quality)
|
||||
2) Lanczos (best quality, slower)
|
||||
3) Bilinear (fastest, basic quality)
|
||||
EOF
|
||||
echo
|
||||
|
||||
while true; do
|
||||
read -p " Enter 1–3 → " scale_opt
|
||||
if [[ -n "$scale_opt" && "$scale_opt" =~ ^[1-3]$ ]]; then
|
||||
break
|
||||
else
|
||||
echo " Invalid input. Please enter a number between 1 and 3."
|
||||
fi
|
||||
done
|
||||
|
||||
case $scale_opt in
|
||||
1) scale_flags="bicubic" ;;
|
||||
2) scale_flags="lanczos" ;;
|
||||
3) scale_flags="bilinear" ;;
|
||||
esac
|
||||
else
|
||||
scale_flags="lanczos" # Default for consistency
|
||||
fi
|
||||
|
||||
# — Encoding Options —
|
||||
clear
|
||||
cat << "EOF"
|
||||
╔═════════════════════════════════════════════════════════════╗
|
||||
║ Encoding Options ║
|
||||
╚═══════════════════════════════════════════════════════════════╝
|
||||
|
||||
Auto-detected encoder: $optimal_encoder
|
||||
|
||||
1) Original FPS
|
||||
2) 60 FPS
|
||||
EOF
|
||||
echo
|
||||
|
||||
while true; do
|
||||
read -p " Enter 1–2 → " c
|
||||
if [[ -n "$c" && "$c" =~ ^[1-2]$ ]]; then
|
||||
break
|
||||
else
|
||||
echo " Invalid input. Please enter 1 or 2."
|
||||
fi
|
||||
done
|
||||
|
||||
# Set container and FPS
|
||||
ext="mkv"
|
||||
fps_filter=""
|
||||
suf=""
|
||||
if [[ "$c" == "2" ]]; then
|
||||
fps_filter="fps=60"
|
||||
suf="_60fps"
|
||||
fi
|
||||
|
||||
# Use auto-detected encoder
|
||||
codec="$optimal_encoder"
|
||||
|
||||
# — Color Correction Option —
|
||||
clear
|
||||
cat << "EOF"
|
||||
╔═══════════════════════════════════════════════════════════════╗
|
||||
║ Color Correction Option ║
|
||||
╚═══════════════════════════════════════════════════════════════╝
|
||||
|
||||
1) No color correction
|
||||
2) Restore pink skin tones (Topaz AI fix)
|
||||
3) Warm color boost
|
||||
4) Cool color boost
|
||||
5) 2000s DVD Restore
|
||||
6) 90s Quality Restore
|
||||
7) VHS Quality Restore
|
||||
8) Anime Preservation (clean lines & colors)
|
||||
EOF
|
||||
echo
|
||||
|
||||
while true; do
|
||||
read -p " Enter 1–8 → " color_opt
|
||||
if [[ -n "$color_opt" && "$color_opt" =~ ^[1-8]$ ]]; then
|
||||
break
|
||||
else
|
||||
echo " Invalid input. Please enter a number between 1 and 8."
|
||||
fi
|
||||
done
|
||||
|
||||
case $color_opt in
|
||||
1) color_filter=""; color_suf="" ;;
|
||||
2) color_filter="eq=contrast=1.05:brightness=0.02:saturation=1.1:hue=-0.02"; color_suf="_colorfix" ;;
|
||||
3) color_filter="eq=contrast=1.03:brightness=0.01:saturation=1.15:hue=-0.01"; color_suf="_warm" ;;
|
||||
4) color_filter="eq=contrast=1.03:brightness=0.01:saturation=0.95:hue=0.02"; color_suf="_cool" ;;
|
||||
5) color_filter="eq=contrast=1.08:brightness=0.03:saturation=1.2:hue=0:gamma=0.95,unsharp=4:4:0.8:4:4:0.0"; color_suf="_dvdrestore" ;;
|
||||
6) color_filter="eq=contrast=1.12:brightness=0.05:saturation=1.3:hue=0:gamma=0.92,unsharp=5:5:1.0:5:5:0.0,hqdn3d=3:2:2:3"; color_suf="_90srestore" ;;
|
||||
7) color_filter="eq=contrast=1.15:brightness=0.08:saturation=1.4:hue=0:gamma=0.90,unsharp=5:5:1.0:5:5:0.0,hqdn3d=3:2:2:3"; color_suf="_vhsrestore" ;;
|
||||
8) color_filter="eq=contrast=1.02:brightness=0:saturation=1.05:hue=0:gamma=1.0,unsharp=3:3:0.5:3:3:0.0,gradfun=2.5:2.5"; color_suf="_anime" ;;
|
||||
esac
|
||||
|
||||
# — Quality Selection —
|
||||
clear
|
||||
cat << "EOF"
|
||||
╔═══════════════════════════════════════════════════════════════╗
|
||||
║ Choose Quality Mode ║
|
||||
╚═══════════════════════════════════════════════════════════════╝
|
||||
|
||||
1) High Quality AV1 (CRF 18) - Best compression
|
||||
2) High Quality HEVC (CRF 18) - Fast encoding
|
||||
3) Near-Lossless AV1 (CRF 16) - Maximum quality
|
||||
4) Near-Lossless HEVC (CRF 16) - Quality/Speed balance
|
||||
5) Custom bitrate
|
||||
EOF
|
||||
echo
|
||||
|
||||
while true; do
|
||||
read -p " Enter 1–5 → " b
|
||||
if [[ -n "$b" && "$b" =~ ^[1-5]$ ]]; then
|
||||
break
|
||||
else
|
||||
echo " Invalid input. Please enter a number between 1 and 5."
|
||||
fi
|
||||
done
|
||||
|
||||
# Set quality parameters based on auto-detected encoder
|
||||
case $b in
|
||||
case $enc_choice in
|
||||
1)
|
||||
if [[ "$codec" == *"av1"* ]]; then
|
||||
quality_params="-crf 18 -preset 6"
|
||||
quality_name="AV1 CRF 18"
|
||||
else
|
||||
quality_params="-crf 18 -quality 23"
|
||||
quality_name="HEVC CRF 18"
|
||||
fi
|
||||
echo "✅ Keeping: $optimal_encoder"
|
||||
;;
|
||||
2)
|
||||
if [[ "$codec" == *"av1"* ]]; then
|
||||
quality_params="-crf 16 -preset 4"
|
||||
quality_name="AV1 CRF 16"
|
||||
else
|
||||
quality_params="-crf 16 -quality 28"
|
||||
quality_name="HEVC CRF 16"
|
||||
fi
|
||||
optimal_encoder="hevc_nvenc"
|
||||
echo "✅ Selected: NVIDIA HEVC NVENC"
|
||||
;;
|
||||
3)
|
||||
echo
|
||||
echo "Enter bitrate (e.g., 5000k, 8000k):"
|
||||
read -p "→ " custom_bitrate
|
||||
if [[ -n "$custom_bitrate" ]]; then
|
||||
quality_params="-b:v $custom_bitrate"
|
||||
quality_name="Custom $custom_bitrate"
|
||||
optimal_encoder="av1_nvenc"
|
||||
echo "✅ Selected: NVIDIA AV1 NVENC"
|
||||
;;
|
||||
4)
|
||||
optimal_encoder="hevc_amf"
|
||||
echo "✅ Selected: AMD HEVC AMF"
|
||||
;;
|
||||
5)
|
||||
optimal_encoder="av1_amf"
|
||||
echo "✅ Selected: AMD AV1 AMF"
|
||||
;;
|
||||
6)
|
||||
optimal_encoder="hevc_qsv"
|
||||
echo "✅ Selected: Intel HEVC Quick Sync"
|
||||
;;
|
||||
7)
|
||||
optimal_encoder="av1_qsv"
|
||||
echo "✅ Selected: Intel AV1 Quick Sync"
|
||||
;;
|
||||
8)
|
||||
optimal_encoder="libsvtav1"
|
||||
echo "✅ Selected: CPU SVT-AV1"
|
||||
;;
|
||||
9)
|
||||
optimal_encoder="libx265"
|
||||
echo "✅ Selected: CPU x265 HEVC"
|
||||
;;
|
||||
10)
|
||||
echo -e "\n⚙️ Available Encoders:"
|
||||
ffmpeg -hide_banner -encoders | grep -E "(hevc|av1|h265)" | grep -v "V\|D" | awk '{print $2}' | nl
|
||||
|
||||
while true; do
|
||||
read -p "Enter encoder name: " custom_enc
|
||||
if ffmpeg -hide_banner -loglevel error -encoders | grep -q "$custom_enc"; then
|
||||
optimal_encoder="$custom_enc"
|
||||
echo "✅ Selected: $custom_enc"
|
||||
break
|
||||
else
|
||||
quality_params="-crf 18 -quality 23"
|
||||
quality_name="HEVC CRF 18"
|
||||
echo "❌ Encoder '$custom_enc' not found. Please try again."
|
||||
fi
|
||||
done
|
||||
;;
|
||||
esac
|
||||
|
||||
# Force 8-bit input with performance optimization
|
||||
bitdepth_filter="-pix_fmt yuv420p -movflags +faststart"
|
||||
setup_codec_and_container
|
||||
get_resolution_settings
|
||||
get_fps_settings
|
||||
get_quality_settings "$optimal_encoder"
|
||||
get_color_correction
|
||||
|
||||
# Build filter chain
|
||||
build_filter_chain
|
||||
|
||||
# Set container from user selection
|
||||
ext="$OUTPUT_CONTAINER"
|
||||
|
||||
# Display encoding summary
|
||||
echo
|
||||
echo "Encoding → $codec | $res_name | $ext $suf${color_suf} @ $quality_name"
|
||||
echo "Encoding → $ENCODER | $res_name | $ext $suf${color_suf} @ $quality_name"
|
||||
echo
|
||||
|
||||
# Process video files - handle drag-and-drop vs double-click
|
||||
|
|
@ -323,54 +245,8 @@ else
|
|||
echo "Processing all video files in current directory: ${#video_files[@]} file(s)"
|
||||
fi
|
||||
|
||||
for f in "${video_files[@]}"; do
|
||||
[[ -f "$f" ]] || continue
|
||||
|
||||
# Extract basename for output filename (handles both relative and absolute paths)
|
||||
basename_f=$(basename "$f")
|
||||
out="$OUT/${basename_f%.*}${suf}${color_suf}__cv.$ext"
|
||||
[[ -f "$out" ]] && { echo "SKIP $f (already exists)"; continue; }
|
||||
|
||||
# Build filter chain
|
||||
filter_chain=""
|
||||
if [[ -n "$scale" ]]; then
|
||||
filter_chain="scale=${scale}:flags=${scale_flags}"
|
||||
fi
|
||||
|
||||
if [[ -n "$fps_filter" ]]; then
|
||||
if [[ -n "$filter_chain" ]]; then
|
||||
filter_chain="${filter_chain},${fps_filter}"
|
||||
else
|
||||
filter_chain="$fps_filter"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "$color_filter" ]]; then
|
||||
if [[ -n "$filter_chain" ]]; then
|
||||
filter_chain="${filter_chain},${color_filter}"
|
||||
else
|
||||
filter_chain="$color_filter"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Processing: $f → $(basename "$out")"
|
||||
|
||||
# Build optimized ffmpeg command
|
||||
if [[ -n "$filter_chain" ]]; then
|
||||
ffmpeg -y -i "$f" -pix_fmt yuv420p -vf "$filter_chain" \
|
||||
-c:v "$codec" $quality_params -c:a aac -b:a 192k -ac 2 "$out"
|
||||
else
|
||||
ffmpeg -y -i "$f" -pix_fmt yuv420p \
|
||||
-c:v "$codec" $quality_params -c:a aac -b:a 192k -ac 2 "$out"
|
||||
fi
|
||||
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo "DONE → $(basename "$out")"
|
||||
else
|
||||
echo "ERROR → Failed to process $f"
|
||||
fi
|
||||
echo
|
||||
done
|
||||
# Process all files
|
||||
process_files "$ENCODER" "$quality_params" "$filter_chain" "$suf" "$color_suf" "$ext" "${video_files[@]}"
|
||||
|
||||
echo "========================================================"
|
||||
echo "All finished — files in '$OUT'"
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user