mirror of
https://github.com/Belphemur/CBZOptimizer.git
synced 2025-10-14 04:28:51 +02:00
144 lines
4.0 KiB
Go
144 lines
4.0 KiB
Go
package commands
|
|
|
|
import (
|
|
"fmt"
|
|
utils2 "github.com/belphemur/CBZOptimizer/v2/internal/utils"
|
|
"github.com/belphemur/CBZOptimizer/v2/pkg/converter"
|
|
"github.com/belphemur/CBZOptimizer/v2/pkg/converter/constant"
|
|
"github.com/spf13/cobra"
|
|
"github.com/thediveo/enumflag/v2"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
var converterType constant.ConversionFormat
|
|
|
|
func init() {
|
|
command := &cobra.Command{
|
|
Use: "optimize [folder]",
|
|
Short: "Optimize all CBZ/CBR files in a folder recursively",
|
|
Long: "Optimize all CBZ/CBR files in a folder recursively.\nIt will take all the different pages in the CBZ/CBR files and convert them to the given format.\nThe original CBZ/CBR files will be kept intact depending if you choose to override or not.",
|
|
RunE: ConvertCbzCommand,
|
|
Args: cobra.ExactArgs(1),
|
|
}
|
|
formatFlag := enumflag.New(&converterType, "format", constant.CommandValue, enumflag.EnumCaseInsensitive)
|
|
_ = formatFlag.RegisterCompletion(command, "format", constant.HelpText)
|
|
|
|
command.Flags().Uint8P("quality", "q", 85, "Quality for conversion (0-100)")
|
|
command.Flags().IntP("parallelism", "n", 2, "Number of chapters to convert in parallel")
|
|
command.Flags().BoolP("override", "o", false, "Override the original CBZ/CBR files")
|
|
command.Flags().BoolP("split", "s", false, "Split long pages into smaller chunks")
|
|
command.PersistentFlags().VarP(
|
|
formatFlag,
|
|
"format", "f",
|
|
fmt.Sprintf("Format to convert the images to: %s", constant.ListAll()))
|
|
command.PersistentFlags().Lookup("format").NoOptDefVal = constant.DefaultConversion.String()
|
|
|
|
AddCommand(command)
|
|
}
|
|
|
|
func ConvertCbzCommand(cmd *cobra.Command, args []string) error {
|
|
path := args[0]
|
|
if path == "" {
|
|
return fmt.Errorf("path is required")
|
|
}
|
|
|
|
if !utils2.IsValidFolder(path) {
|
|
return fmt.Errorf("the path needs to be a folder")
|
|
}
|
|
|
|
quality, err := cmd.Flags().GetUint8("quality")
|
|
if err != nil || quality <= 0 || quality > 100 {
|
|
return fmt.Errorf("invalid quality value")
|
|
}
|
|
|
|
override, err := cmd.Flags().GetBool("override")
|
|
if err != nil {
|
|
return fmt.Errorf("invalid quality value")
|
|
}
|
|
|
|
split, err := cmd.Flags().GetBool("split")
|
|
if err != nil {
|
|
return fmt.Errorf("invalid split value")
|
|
}
|
|
|
|
parallelism, err := cmd.Flags().GetInt("parallelism")
|
|
if err != nil || parallelism < 1 {
|
|
return fmt.Errorf("invalid parallelism value")
|
|
}
|
|
|
|
chapterConverter, err := converter.Get(converterType)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get chapterConverter: %v", err)
|
|
}
|
|
|
|
err = chapterConverter.PrepareConverter()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to prepare converter: %v", err)
|
|
}
|
|
// Channel to manage the files to process
|
|
fileChan := make(chan string)
|
|
// Channel to collect errors
|
|
errorChan := make(chan error, parallelism)
|
|
|
|
// WaitGroup to wait for all goroutines to finish
|
|
var wg sync.WaitGroup
|
|
|
|
// Start worker goroutines
|
|
for i := 0; i < parallelism; i++ {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
for path := range fileChan {
|
|
err := utils2.Optimize(&utils2.OptimizeOptions{
|
|
ChapterConverter: chapterConverter,
|
|
Path: path,
|
|
Quality: quality,
|
|
Override: override,
|
|
Split: split,
|
|
})
|
|
if err != nil {
|
|
errorChan <- fmt.Errorf("error processing file %s: %w", path, err)
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
// Walk the path and send files to the channel
|
|
err = filepath.WalkDir(path, func(path string, info os.DirEntry, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !info.IsDir() {
|
|
fileName := strings.ToLower(info.Name())
|
|
if strings.HasSuffix(fileName, ".cbz") || strings.HasSuffix(fileName, ".cbr") {
|
|
fileChan <- path
|
|
}
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("error walking the path: %w", err)
|
|
}
|
|
|
|
close(fileChan) // Close the channel to signal workers to stop
|
|
wg.Wait() // Wait for all workers to finish
|
|
close(errorChan) // Close the error channel
|
|
|
|
var errs []error
|
|
for err := range errorChan {
|
|
errs = append(errs, err)
|
|
}
|
|
|
|
if len(errs) > 0 {
|
|
return fmt.Errorf("encountered errors: %v", errs)
|
|
}
|
|
|
|
return nil
|
|
}
|