package main import ( "bytes" "encoding/json" "fmt" "os" "os/exec" "path/filepath" "runtime" "strconv" "strings" "time" "gwutils" ) func printHeader() { fmt.Println("╔══════════════════════════════════════════════════════════════╗") fmt.Println("║ GWEncoder v3.0 ║") fmt.Println("║ Unified Video Encoding Tool ║") fmt.Println("╚══════════════════════════════════════════════════════════════╝") } func printHelp() { printHeader() fmt.Println() fmt.Println("❓ GWENCODER HELP & USAGE") fmt.Println("══════════════════════════════════════════════════════════════") fmt.Println("GWEncoder is a unified video encoding tool with multiple modes:") fmt.Println() fmt.Println("🚀 ENCODING MODES:") fmt.Println(" --fast Fast AV1 encoding (MKV, CRF 32, Opus 64kbps, preset 10)") fmt.Println(" --web Web-optimized encoding (WEBM, CRF 40, Opus 64kbps, preset 10)") fmt.Println(" --quick Quick encoding (MKV, CRF 32, Opus 80kbps, preset 10)") fmt.Println(" --tiny Tiny encoding (MP4, CRF 45, Opus 64kbps, preset 8)") fmt.Println(" --full Full interactive mode with all codecs and options") fmt.Println() fmt.Println("📊 INFORMATION OPTIONS:") fmt.Println(" --help Show this help information") fmt.Println(" --info Show system information") fmt.Println(" --stats Show encoding statistics") fmt.Println() fmt.Println("⚙️ FEATURES:") fmt.Println("• Multiple codecs: AV1, HEVC, H264, X265") fmt.Println("• Hardware acceleration support (NVENC)") fmt.Println("• Automatic file detection") fmt.Println("• Progress tracking with ETA") fmt.Println("• Subtitle extraction") fmt.Println("• Configuration saving/loading") fmt.Println("• Preset system for quick encoding") fmt.Println() fmt.Println("📁 SUPPORTED FORMATS:") fmt.Println("• Input: WMV, AVI, MP4, MKV, MPG, TS, WEBM") fmt.Println("• Output: MKV, MP4, WEBM") fmt.Println() fmt.Println("💡 EXAMPLES:") fmt.Println(" ./gwencoder --fast # Fast AV1 encoding") fmt.Println(" ./gwencoder --web # Web-optimized encoding") fmt.Println(" ./gwencoder --quick # Quick encoding") fmt.Println(" ./gwencoder --tiny # Tiny file encoding") fmt.Println(" ./gwencoder --full # Full interactive mode") fmt.Println(" ./gwencoder --help # Show this help") fmt.Println(" ./gwencoder --info # Show system info") fmt.Println("══════════════════════════════════════════════════════════════") } func printSystemInfo() { printHeader() fmt.Println() fmt.Println("💻 SYSTEM INFORMATION") fmt.Println("══════════════════════════════════════════════════════════════") fmt.Printf("OS: %s\n", runtime.GOOS) fmt.Printf("Architecture: %s\n", runtime.GOARCH) fmt.Printf("CPU Cores: %d\n", runtime.NumCPU()) fmt.Printf("Physical Cores: %d\n", gwutils.GetPhysicalCores()) if gwutils.CheckFFmpeg() { fmt.Println("✅ FFmpeg: Available") } else { fmt.Println("❌ FFmpeg: Not available") } if gwutils.CheckFFprobe() { fmt.Println("✅ FFprobe: Available") } else { fmt.Println("❌ FFprobe: Not available") } fmt.Println("══════════════════════════════════════════════════════════════") } func printStats() { printHeader() fmt.Println() fmt.Println("📊 ENCODING STATISTICS") fmt.Println("══════════════════════════════════════════════════════════════") // Try to read stats from file if data, err := os.ReadFile("gwencoder_stats.json"); err == nil { var stats map[string]interface{} if json.Unmarshal(data, &stats) == nil { fmt.Printf("Total files encoded: %v\n", stats["total_files"]) fmt.Printf("Last encoding: %v\n", stats["last_encoding"]) fmt.Printf("Average time: %v\n", stats["average_time"]) } else { fmt.Println("No encoding history found.") } } else { fmt.Println("No encoding history found.") } fmt.Println("══════════════════════════════════════════════════════════════") } func encodeFile(file string, mode gwutils.EncodingMode) (time.Duration, int64, error) { base := strings.TrimSuffix(filepath.Base(file), filepath.Ext(file)) out := fmt.Sprintf("%s-AV1-%s-GWELL.%s", base, strings.Title(mode.Name), mode.Container) start := time.Now() // Get video duration for progress calculation totalDuration := gwutils.GetVideoDuration(file) // Get dynamic audio bitrate based on channel count audioBitratePerChannel, _ := strconv.Atoi(mode.AudioBitrate) audioBitrateStr := gwutils.GetAudioBitrate(file, audioBitratePerChannel) audioBitrateParts := strings.Fields(audioBitrateStr) // Calculate physical cores physicalCores := gwutils.GetPhysicalCores() // AV1 parameters based on mode var svtParams string if mode.Name == "Web-optimized" { svtParams = fmt.Sprintf("\"preset=%s:tune=0:scd=1:aq-mode=1:lp=%d:keyint=240:film-grain=0:input-depth=8:fast-decode=1:lookahead=90:enable-tf=0\"", mode.Preset, physicalCores) } else if mode.Name == "Quick" { svtParams = fmt.Sprintf("\"preset=%s:tune=0:scd=1:aq-mode=1:lp=%d:keyint=240:film-grain=0:input-depth=8:fast-decode=1:lookahead=90:enable-tf=0\"", mode.Preset, physicalCores) } else if mode.Name == "Tiny" { svtParams = fmt.Sprintf("\"preset=%s:tune=1:scd=1:aq-mode=2:lp=%d:keyint=240:film-grain=0:input-depth=8:fast-decode=1:lookahead=120:enable-tf=0\"", mode.Preset, physicalCores) } else { // Fast mode svtParams = fmt.Sprintf("\"preset=%s:tune=0:scd=0:aq-mode=1:lp=%d:keyint=240:film-grain=0:input-depth=8:fast-decode=1:lookahead=60:enable-tf=0\"", mode.Preset, physicalCores) } // Build FFmpeg arguments args := []string{"-y", "-i", file} args = append(args, "-c:v", "libsvtav1") args = append(args, "-crf", mode.CRF) args = append(args, "-b:v", "0") args = append(args, "-maxrate", fmt.Sprintf("%sk", mode.Maxrate)) args = append(args, "-bufsize", fmt.Sprintf("%sk", mode.Maxrate)) args = append(args, "-svtav1-params", svtParams) args = append(args, "-c:a", "libopus") args = append(args, audioBitrateParts...) args = append(args, "-map", "0:v", "-map", "0:a") args = append(args, "-g", "240") args = append(args, "-sn") // Skip subtitles for speed args = append(args, out) fmt.Printf("🎬 %s AV1 Encoding: %s → %s\n", mode.Name, file, out) fmt.Printf("⚡ %s settings:\n", mode.Name) fmt.Printf(" • Using %d physical cores\n", physicalCores) fmt.Printf(" • CRF=%s (%s quality)\n", mode.CRF, mode.Name) fmt.Printf(" • Preset=%s (encoding speed)\n", mode.Preset) fmt.Printf(" • Opus audio at %skbps per channel\n", mode.AudioBitrate) fmt.Printf(" • %s container\n", strings.ToUpper(mode.Container)) if totalDuration > 0 { fmt.Printf("📐 Duration: %s\n", gwutils.FormatTime(totalDuration)) } cmd := exec.Command("ffmpeg", args...) // Capture stderr to see actual error messages var stderr bytes.Buffer cmd.Stderr = &stderr if err := cmd.Start(); err != nil { fmt.Printf("❌ Failed to start encoding %s: %v\n", file, err) return 0, 0, err } // Wait for completion if err := cmd.Wait(); err != nil { fmt.Printf("❌ Failed to encode %s: %v\n", file, err) fmt.Printf("🔧 FFmpeg stderr: %s\n", stderr.String()) gwutils.AppendToFile("gwencoder_errors.txt", fmt.Sprintf("Failed: %s - %v\nStderr: %s\n", file, err, stderr.String())) return 0, 0, err } duration := time.Since(start) // Get output file size var fileSize int64 if stat, err := os.Stat(out); err == nil { fileSize = stat.Size() } fmt.Printf("✅ %s encoding complete: %s in %s\n", mode.Name, file, duration) fmt.Printf("📊 Output file size: %.2f MB\n", float64(fileSize)/(1024*1024)) gwutils.AppendToFile("gwencoder_log.txt", fmt.Sprintf("Encoded %s in %d seconds using %s mode\n", file, int(duration.Seconds()), mode.Name)) updateStats(file, int(duration.Seconds()), mode.Name) return duration, fileSize, nil } func updateStats(file string, duration int, mode string) { stats := map[string]interface{}{ "total_files": "1", "last_encoding": fmt.Sprintf("%s (%s)", file, mode), "average_time": fmt.Sprintf("%d seconds", duration), "last_update": time.Now().Format("2006-01-02 15:04:05"), } if data, err := json.Marshal(stats); err == nil { os.WriteFile("gwencoder_stats.json", data, 0644) } } func runFullMode() { fmt.Println("🎬 FULL INTERACTIVE MODE") fmt.Println("══════════════════════════════════════════════════════════════") fmt.Println("This mode provides full access to all encoding options.") fmt.Println("For now, this is a placeholder for the full GWEncoder.go functionality.") fmt.Println("Use specific modes (--fast, --web, --quick, --tiny) for immediate encoding.") fmt.Println() // This would integrate the full GWEncoder.go functionality // For now, just show available options fmt.Println("Available quick modes:") fmt.Println(" --fast Fast AV1 encoding") fmt.Println(" --web Web-optimized encoding") fmt.Println(" --quick Quick encoding") fmt.Println(" --tiny Tiny file encoding") } func main() { if len(os.Args) < 2 { printHelp() return } arg := os.Args[1] modes := gwutils.GetDefaultModes() switch arg { case "--help", "-h": printHelp() case "--info": printSystemInfo() case "--stats": printStats() case "--fast": printHeader() fmt.Println() fmt.Println("⚡ FAST ENCODING MODE") fmt.Println("══════════════════════════════════════════════════════════════") fmt.Println("Using fast encoding settings:") fmt.Println("• AV1 codec with speed preset (10)") fmt.Println("• Opus audio at 64kbps per channel") fmt.Println("• MKV container for better compatibility") fmt.Println("• CRF 32 for fast encoding") fmt.Println() mode := modes["fast"] fmt.Printf("⚡ %s AV1 settings:\n", mode.Name) fmt.Printf(" • Using %d physical cores\n", gwutils.GetPhysicalCores()) fmt.Printf(" • CRF=%s (fast encoding quality)\n", mode.CRF) fmt.Printf(" • Preset=%s (speed optimized)\n", mode.Preset) fmt.Printf(" • Opus audio at %skbps per channel\n", mode.AudioBitrate) fmt.Printf(" • %s container\n", strings.ToUpper(mode.Container)) fmt.Println("📏 Using original resolution") fmt.Println() excludePatterns := []string{"GWELL", "AV1-"} files := gwutils.FindMediaFiles(excludePatterns) if len(files) == 0 { fmt.Println("❌ No video files found in current directory.") return } fmt.Printf("🚀 Starting %s encoding of %d files...\n", mode.Name, len(files)) fmt.Println() for _, file := range files { encodeFile(file, mode) fmt.Println() } case "--web": printHeader() fmt.Println() fmt.Println("🌐 WEB-OPTIMIZED ENCODING MODE") fmt.Println("══════════════════════════════════════════════════════════════") fmt.Println("Using web-optimized settings:") fmt.Println("• AV1 codec with balanced preset (10)") fmt.Println("• Opus audio at 64kbps per channel") fmt.Println("• WEBM container for web compatibility") fmt.Println("• CRF 40 for web-optimized quality") fmt.Println() mode := modes["web"] fmt.Printf("🌐 %s AV1 settings:\n", mode.Name) fmt.Printf(" • Using %d physical cores\n", gwutils.GetPhysicalCores()) fmt.Printf(" • CRF=%s (web-optimized quality)\n", mode.CRF) fmt.Printf(" • Preset=%s (balanced encoding)\n", mode.Preset) fmt.Printf(" • Opus audio at %skbps per channel\n", mode.AudioBitrate) fmt.Printf(" • %s container for web compatibility\n", strings.ToUpper(mode.Container)) fmt.Println("📏 Using original resolution") fmt.Println() excludePatterns := []string{"GWELL", "AV1-"} files := gwutils.FindMediaFiles(excludePatterns) if len(files) == 0 { fmt.Println("❌ No video files found in current directory.") return } fmt.Printf("🚀 Starting %s encoding of %d files...\n", mode.Name, len(files)) fmt.Println() for _, file := range files { encodeFile(file, mode) fmt.Println() } case "--quick": printHeader() fmt.Println() fmt.Println("⚡ QUICK ENCODING MODE") fmt.Println("══════════════════════════════════════════════════════════════") fmt.Println("Using quick encoding settings:") fmt.Println("• AV1 codec with balanced preset (10)") fmt.Println("• Opus audio at 80kbps per channel") fmt.Println("• MKV container for better compatibility") fmt.Println("• CRF 32 for quick encoding") fmt.Println() mode := modes["quick"] fmt.Printf("⚡ %s AV1 settings:\n", mode.Name) fmt.Printf(" • Using %d physical cores\n", gwutils.GetPhysicalCores()) fmt.Printf(" • CRF=%s (quick encoding quality)\n", mode.CRF) fmt.Printf(" • Preset=%s (balanced encoding)\n", mode.Preset) fmt.Printf(" • Opus audio at %skbps per channel\n", mode.AudioBitrate) fmt.Printf(" • %s container\n", strings.ToUpper(mode.Container)) fmt.Println("📏 Using original resolution") fmt.Println() excludePatterns := []string{"GWELL", "AV1-"} files := gwutils.FindMediaFiles(excludePatterns) if len(files) == 0 { fmt.Println("❌ No video files found in current directory.") return } fmt.Printf("🚀 Starting %s encoding of %d files...\n", mode.Name, len(files)) fmt.Println() for _, file := range files { encodeFile(file, mode) fmt.Println() } case "--tiny": printHeader() fmt.Println() fmt.Println("📦 TINY ENCODING MODE") fmt.Println("══════════════════════════════════════════════════════════════") fmt.Println("Using tiny encoding settings:") fmt.Println("• AV1 codec with quality preset (8)") fmt.Println("• Opus audio at 64kbps per channel") fmt.Println("• MP4 container for maximum compatibility") fmt.Println("• CRF 45 for smallest file size") fmt.Println() mode := modes["tiny"] fmt.Printf("📦 %s AV1 settings:\n", mode.Name) fmt.Printf(" • Using %d physical cores\n", gwutils.GetPhysicalCores()) fmt.Printf(" • CRF=%s (tiny file quality)\n", mode.CRF) fmt.Printf(" • Preset=%s (quality encoding)\n", mode.Preset) fmt.Printf(" • Opus audio at %skbps per channel\n", mode.AudioBitrate) fmt.Printf(" • %s container for maximum compatibility\n", strings.ToUpper(mode.Container)) fmt.Println("📏 Using original resolution") fmt.Println() excludePatterns := []string{"GWELL", "AV1-"} files := gwutils.FindMediaFiles(excludePatterns) if len(files) == 0 { fmt.Println("❌ No video files found in current directory.") return } fmt.Printf("🚀 Starting %s encoding of %d files...\n", mode.Name, len(files)) fmt.Println() for _, file := range files { encodeFile(file, mode) fmt.Println() } case "--full": printHeader() runFullMode() default: fmt.Println("❌ Unknown option:", arg) fmt.Println("Use --help for usage information") } }