diff --git a/media/recode_video.sh b/media/recode_video.sh new file mode 100755 index 0000000..82ba0a4 --- /dev/null +++ b/media/recode_video.sh @@ -0,0 +1,258 @@ +#!/bin/bash + +# Colores para output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +CYAN='\033[0;36m' +NC='\033[0m' # No Color + +# Función para mostrar mensajes con colores +print_colored() { + echo -e "${2}${1}${NC}" +} + +# Función para validar número +is_number() { + [[ $1 =~ ^[0-9]+$ ]] +} + +# Función para obtener duración del video +get_duration() { + ffprobe -v quiet -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$1" 2>/dev/null | cut -d. -f1 +} + +# Función para detectar hardware disponible +detect_hardware() { + hw_available="" + if nvidia-smi &>/dev/null; then hw_available="${hw_available}nvenc "; fi + if [ -d "/dev/dri" ] && ls /dev/dri/render* &>/dev/null; then hw_available="${hw_available}vaapi "; fi + echo "$hw_available" +} + +# Función para mostrar opciones de encoder +show_encoder_options() { + local hw=$(detect_hardware) + print_colored "\n=== OPCIONES DE ENCODER ===" "$YELLOW" + echo "1) SOFTWARE (x265) - Mejor calidad, más lento" + [[ $hw == *"nvenc"* ]] && echo "2) NVIDIA (NVENC) - Rápido, buena calidad" + [[ $hw == *"vaapi"* ]] && echo "3) VAAPI (Intel/AMD) - Rápido, calidad variable" + echo -e "\nHardware detectado: ${hw:-ninguno}" +} + +# Función para mostrar valores de calidad +show_quality_info() { + case $encoder_type in + "software") + print_colored "\n=== CALIDAD CRF (x265) ===" "$YELLOW" + echo "18-20: Excelente | 22-24: Muy alta (Rec.) | 26-28: Buena | 30-32: Aceptable" ;; + "nvenc") + print_colored "\n=== CALIDAD CQ (NVENC) ===" "$YELLOW" + echo "19-21: Excelente | 23-25: Muy alta (Rec.) | 27-29: Buena | 31-33: Aceptable" ;; + "vaapi") + print_colored "\n=== CALIDAD ICQ (VAAPI) ===" "$YELLOW" + echo "18-20: Excelente | 22-24: Muy alta (Rec.) | 26-28: Buena | 30-32: Aceptable" ;; + esac +} + +# Función para pedir encoder +ask_encoder() { + show_encoder_options + while true; do + read -p "Selecciona el encoder (1-3): " encoder_choice + case $encoder_choice in + 1) encoder_type="software"; break ;; + 2) [[ $(detect_hardware) == *"nvenc"* ]] && { encoder_type="nvenc"; break; } || print_colored "NVENC no disponible." "$RED" ;; + 3) [[ $(detect_hardware) == *"vaapi"* ]] && { encoder_type="vaapi"; break; } || print_colored "VAAPI no disponible." "$RED" ;; + *) print_colored "Opción inválida." "$RED" ;; + esac + done +} + +# Función para pedir calidad +ask_quality() { + show_quality_info + case $encoder_type in + "software"|"vaapi") quality_range="18-32" ;; + "nvenc") quality_range="19-33" ;; + esac + + while true; do + read -p "Introduce el valor de calidad ($quality_range): " quality_value + local min=$(echo $quality_range | cut -d'-' -f1) + local max=$(echo $quality_range | cut -d'-' -f2) + if is_number "$quality_value" && [ "$quality_value" -ge "$min" ] && [ "$quality_value" -le "$max" ]; then break; else print_colored "Error: Introduce un número entre $quality_range." "$RED"; fi + done +} + +# Función para construir los argumentos de audio dinámicamente +build_audio_args() { + local input_file="$1" + local mode="$2" + local audio_args="" + + # Si el modo NO requiere tocar audio (1 y 3), copiamos todo + if [ "$mode" == "1" ] || [ "$mode" == "3" ]; then + echo "-c:a copy" + return + fi + + # Obtenemos el número de streams de audio + local num_audio_streams=$(ffprobe -v error -select_streams a -show_entries stream=index -of csv=p=0 "$input_file" | wc -l) + + # Si no hay audio, no ponemos argumentos de audio + if [ "$num_audio_streams" -eq 0 ]; then + return + fi + + # Iteramos por cada stream de audio (índice 0 hasta N-1) + for ((i=0; i 0 y < 256k, copiamos. Si es >= 256k o desconocido (0), convertimos. + if [ "$bitrate_kbps" -gt 0 ] && [ "$bitrate_kbps" -lt 256 ]; then + audio_args="$audio_args -c:a:$i copy" + else + audio_args="$audio_args -c:a:$i ac3 -b:a:$i 256k" + fi + done + + echo "$audio_args" +} + +# Función para generar comando ffmpeg +generate_ffmpeg_command() { + local input_file="$1" + local output_file="$2" + local mode="$3" + + local video_opts="" + local filter_opts="" + local pre_opts="" + + # 1. VIDEO + if [ "$mode" == "2" ]; then + video_opts="-c:v copy" + else + case $encoder_type in + "software") video_opts="-c:v libx265 -crf $quality_value -preset medium" ;; + "nvenc") video_opts="-c:v hevc_nvenc -cq $quality_value -preset p4" ;; + "vaapi") + pre_opts="-vaapi_device /dev/dri/renderD128" + video_opts="-c:v hevc_vaapi -global_quality $quality_value" + ;; + esac + fi + + # 2. FILTROS (Escalado) + if [ "$mode" == "3" ] || [ "$mode" == "4" ]; then + if [ "$encoder_type" == "vaapi" ] && [ "$mode" != "2" ]; then + filter_opts="-vf 'format=nv12,hwupload,scale_vaapi=1920:1080:force_original_aspect_ratio=decrease:force_divisible_by=2'" + elif [ "$mode" != "2" ]; then + filter_opts="-vf 'scale=1920:1080:force_original_aspect_ratio=decrease:force_divisible_by=2'" + fi + else + if [ "$encoder_type" == "vaapi" ] && [ "$mode" != "2" ]; then + filter_opts="-vf 'format=nv12,hwupload'" + fi + fi + + # 3. AUDIO (Llamada a la nueva función inteligente) + local audio_opts=$(build_audio_args "$input_file" "$mode") + + # 4. CONSTRUIR COMANDO + # Usamos -map 0 para incluir todos los streams, luego aplicamos los codecs específicos + echo "ffmpeg $pre_opts -i \"$input_file\" $video_opts $filter_opts -map 0 $audio_opts -c:s copy \"$output_file\"" +} + +# --- INICIO DEL SCRIPT --- + +print_colored "====" "$CYAN" +print_colored " SCRIPT DE RECODIFICACIÓN MEJORADO v3 (Audio Multi-Track) " "$CYAN" +print_colored "====" "$CYAN" + +if ! command -v ffmpeg &> /dev/null; then print_colored "Error: ffmpeg no está instalado." "$RED"; exit 1; fi + +# Rutas +while true; do + read -p "Introduce la ruta donde buscar los videos: " input_path + input_path="${input_path/#\~/$HOME}" + if [ -d "$input_path" ]; then break; else print_colored "Ruta inválida." "$RED"; fi +done + +read -p "Introduce la ruta de salida (Enter para actual): " output_path +if [ -z "$output_path" ]; then output_path="."; else output_path="${output_path/#\~/$HOME}"; mkdir -p "$output_path"; fi + +# Menú +print_colored "\n=== OPCIONES ===" "$YELLOW" +echo "1) CALIDAD: CONVERSIÓN A H.265" +echo "2) AUDIO: Solo Audio (Smart)" +echo "3) REDIMENSIONAR: 1080p (H.265)" +echo "4) COMPLETO: Convertir a 1080p (H.265) + Audio Smart" +echo "5) COMPLETO SIN REDIMENSION: CALIDAD (H.265) + Audio Smart" + +while true; do + read -p "Opción (1-5): " mode + case $mode in 1|2|3|4|5) break;; *) print_colored "Inválido." "$RED";; esac +done + +# Configuración Encoder +if [ "$mode" != "2" ]; then + ask_encoder + ask_quality + case $encoder_type in + "software") desc="H.265 Soft (CRF $quality_value)" ;; + "nvenc") desc="H.265 NVENC (CQ $quality_value)" ;; + "vaapi") desc="H.265 VAAPI (ICQ $quality_value)" ;; + esac +else + desc="Solo Audio" +fi +[[ "$mode" =~ [245] ]] && desc="$desc + Audio Smart" + +# Guardar config +echo -e "Modo: $desc\nFecha: $(date)\nOrigen: $input_path" > "$output_path/log_$(date +%Y%m%d).txt" + +# Buscar archivos +cd "$input_path" || exit 1 +video_files=() +while IFS= read -r -d '' file; do video_files+=("$file"); done < <(find . -maxdepth 1 -type f \( -iname "*.mkv" -o -iname "*.mp4" -o -iname "*.avi" -o -iname "*.mov" \) -print0 2>/dev/null) + +if [ ${#video_files[@]} -eq 0 ]; then print_colored "No hay videos." "$RED"; exit 1; fi +print_colored "Archivos encontrados: ${#video_files[@]}" "$GREEN" + +# Procesar +processed=0; failed=0 +for file in "${video_files[@]}"; do + filename=$(basename "$file") + output_file="$output_path/${filename%.*}_opt.mkv" + + if [ -f "$output_file" ]; then print_colored "Saltando $filename (ya existe)" "$YELLOW"; continue; fi + + print_colored "\nProcesando: $filename" "$CYAN" + + # Generar y ejecutar + ffmpeg_cmd=$(generate_ffmpeg_command "$file" "$output_file" "$mode") + + # Debug: descomentar para ver qué comando se ejecuta realmente + # echo "DEBUG CMD: $ffmpeg_cmd" + + if eval $ffmpeg_cmd; then + ((processed++)) + print_colored "✓ Completado" "$GREEN" + else + ((failed++)) + print_colored "✗ Error" "$RED" + fi +done + +print_colored "\nFin. Procesados: $processed | Errores: $failed" "$GREEN"