package cmd import ( "fmt" "image" "image/jpeg" "image/png" "log" "os" "path/filepath" "strings" "github.com/chai2010/webp" "github.com/spf13/cobra" ) // convertCmd represents the convert command var convertCmd = &cobra.Command{ Use: "convert [input] [output]", Short: "Convert images between different formats", Long: `Convert images between different formats including JPG, PNG, and WebP. The output format is determined by the output file extension or the --format flag. Examples: # Convert a single file dttt convert input.jpg output.webp # Batch convert all images in a directory dttt convert ./input_dir ./output_dir --batch --format webp # Batch convert with specific extensions and quality dttt convert ./photos ./webp_photos --batch --format webp --quality 85 --extensions jpg,png`, Args: cobra.RangeArgs(1, 2), Run: func(cmd *cobra.Command, args []string) { isBatch, _ := cmd.Flags().GetBool("batch") quality, _ := cmd.Flags().GetInt("quality") format, _ := cmd.Flags().GetString("format") extensions, _ := cmd.Flags().GetString("extensions") if isBatch { if len(args) != 2 { log.Fatal("Batch mode requires input and output directories") } inputDir := args[0] outputDir := args[1] if format == "" { log.Fatal("Batch mode requires specifying an output format with --format") } batchConvert(inputDir, outputDir, format, extensions, quality) } else { if len(args) != 2 { log.Fatal("Single file mode requires input and output file paths") } inputFile := args[0] outputFile := args[1] convertImage(inputFile, outputFile, quality) } }, } func init() { rootCmd.AddCommand(convertCmd) // Add flags convertCmd.Flags().IntP("quality", "q", 90, "Quality for lossy formats (1-100)") convertCmd.Flags().BoolP("batch", "b", false, "Enable batch conversion mode") convertCmd.Flags().StringP("format", "f", "", "Output format for batch conversion (jpg, jpeg, png, webp)") convertCmd.Flags().StringP("extensions", "e", "jpg,jpeg,png,webp", "Comma-separated list of file extensions to process in batch mode") } func batchConvert(inputDir, outputDir, format, extensions string, quality int) { // Ensure output directory exists if err := os.MkdirAll(outputDir, 0755); err != nil { log.Fatal("Failed to create output directory:", err) } // Parse extensions validExts := strings.Split(extensions, ",") for i, ext := range validExts { validExts[i] = strings.ToLower(strings.TrimSpace(ext)) if !strings.HasPrefix(validExts[i], ".") { validExts[i] = "." + validExts[i] } } // Ensure format has a dot prefix and is lowercase format = strings.ToLower(format) if !strings.HasPrefix(format, ".") { format = "." + format } // Get all files from input directory files, err := os.ReadDir(inputDir) if err != nil { log.Fatal("Error reading input directory:", err) } // Track conversion statistics successCount := 0 errorCount := 0 for _, file := range files { if file.IsDir() { continue } // Check if file has a valid extension fileExt := strings.ToLower(filepath.Ext(file.Name())) isValidExt := false for _, ext := range validExts { if ext == fileExt { isValidExt = true break } } if !isValidExt { continue } // Generate input and output file paths inputPath := filepath.Join(inputDir, file.Name()) baseName := strings.TrimSuffix(file.Name(), fileExt) outputPath := filepath.Join(outputDir, baseName+format) // Convert the file err := convertImageWithError(inputPath, outputPath, quality) if err != nil { fmt.Printf("Error converting %s: %v\n", file.Name(), err) errorCount++ } else { successCount++ } } fmt.Printf("Batch conversion complete: %d files converted successfully, %d failed\n", successCount, errorCount) } func convertImageWithError(inputFile, outputFile string, quality int) error { // Open input file input, err := os.Open(inputFile) if err != nil { return fmt.Errorf("error opening input file: %w", err) } defer input.Close() // Decode input image img, format, err := image.Decode(input) if err != nil { return fmt.Errorf("error decoding input image: %w", err) } // Create output file output, err := os.Create(outputFile) if err != nil { return fmt.Errorf("error creating output file: %w", err) } defer output.Close() // Determine output format based on file extension and encode outFormat := getOutputFormat(outputFile) switch strings.ToLower(filepath.Ext(outputFile)) { case ".jpg", ".jpeg": err = jpeg.Encode(output, img, &jpeg.Options{Quality: quality}) case ".png": err = png.Encode(output, img) case ".webp": err = webp.Encode(output, img, &webp.Options{Lossless: quality == 100, Quality: float32(quality)}) default: return fmt.Errorf("unsupported output format: %s", filepath.Ext(outputFile)) } if err != nil { return fmt.Errorf("error encoding output image: %w", err) } fmt.Printf("Converted %s (%s) to %s\n", filepath.Base(inputFile), format, outFormat) return nil } func convertImage(inputFile, outputFile string, quality int) { err := convertImageWithError(inputFile, outputFile, quality) if err != nil { log.Fatal(err) } fmt.Println("Image converted successfully!") } func getOutputFormat(filename string) string { ext := strings.ToLower(filepath.Ext(filename)) switch ext { case ".jpg", ".jpeg": return "JPEG" case ".png": return "PNG" case ".webp": return "WebP" default: return ext } }