diff --git a/cmd/cbzoptimizer/commands/optimize_command.go b/cmd/cbzoptimizer/commands/optimize_command.go index ed7cc68..9149f3a 100644 --- a/cmd/cbzoptimizer/commands/optimize_command.go +++ b/cmd/cbzoptimizer/commands/optimize_command.go @@ -113,8 +113,9 @@ func ConvertCbzCommand(cmd *cobra.Command, args []string) error { // Channel to manage the files to process fileChan := make(chan string) - // Channel to collect errors - errorChan := make(chan error, parallelism) + // Slice to collect errors with mutex for thread safety + var errs []error + var errMutex sync.Mutex // WaitGroup to wait for all goroutines to finish var wg sync.WaitGroup @@ -138,7 +139,9 @@ func ConvertCbzCommand(cmd *cobra.Command, args []string) error { }) if err != nil { log.Error().Int("worker_id", workerID).Str("file_path", path).Err(err).Msg("Worker encountered error") - errorChan <- fmt.Errorf("error processing file %s: %w", path, err) + errMutex.Lock() + errs = append(errs, fmt.Errorf("error processing file %s: %w", path, err)) + errMutex.Unlock() } else { log.Debug().Int("worker_id", workerID).Str("file_path", path).Msg("Worker completed file successfully") } @@ -177,13 +180,6 @@ func ConvertCbzCommand(cmd *cobra.Command, args []string) error { log.Debug().Msg("File channel closed, waiting for workers to complete") wg.Wait() // Wait for all workers to finish log.Debug().Msg("All workers completed") - close(errorChan) // Close the error channel - - var errs []error - for err := range errorChan { - errs = append(errs, err) - log.Error().Err(err).Msg("Collected processing error") - } if len(errs) > 0 { log.Error().Int("error_count", len(errs)).Msg("Command completed with errors") diff --git a/pkg/converter/webp/webp_converter.go b/pkg/converter/webp/webp_converter.go index 174fdd5..8519829 100644 --- a/pkg/converter/webp/webp_converter.go +++ b/pkg/converter/webp/webp_converter.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "image" + _ "image/gif" _ "image/jpeg" "image/png" "runtime" @@ -167,10 +168,15 @@ func (converter *Converter) ConvertChapter(ctx context.Context, chapter *manga.C splitNeeded, img, format, err := converter.checkPageNeedsSplit(page, split) if err != nil { - select { - case errChan <- err: - case <-ctx.Done(): - return + var pageIgnoredError *converterrors.PageIgnoredError + if errors.As(err, &pageIgnoredError) { + log.Info().Err(err).Msg("Page ignored due to image decode error") + } else { + select { + case errChan <- err: + case <-ctx.Done(): + return + } } if img != nil { wgConvertedPages.Add(1) @@ -357,7 +363,7 @@ func (converter *Converter) checkPageNeedsSplit(page *manga.Page, splitRequested img, format, err := image.Decode(reader) if err != nil { log.Debug().Uint16("page_index", page.Index).Err(err).Msg("Failed to decode page image") - return false, nil, format, err + return false, nil, format, converterrors.NewPageIgnored(fmt.Sprintf("page %d: failed to decode image (%s)", page.Index, err.Error())) } bounds := img.Bounds() diff --git a/pkg/converter/webp/webp_converter_test.go b/pkg/converter/webp/webp_converter_test.go index 30329ba..bfce873 100644 --- a/pkg/converter/webp/webp_converter_test.go +++ b/pkg/converter/webp/webp_converter_test.go @@ -5,6 +5,7 @@ import ( "context" "image" "image/color" + "image/gif" "image/jpeg" "image/png" "sync" @@ -44,6 +45,11 @@ func encodeImage(img image.Image, format string) (*bytes.Buffer, string, error) return nil, "", err } return buf, ".jpg", nil + case "gif": + if err := gif.Encode(buf, img, nil); err != nil { + return nil, "", err + } + return buf, ".gif", nil case "webp": PrepareEncoder() if err := Encode(buf, img, 80); err != nil { @@ -131,10 +137,11 @@ func TestConverter_ConvertChapter(t *testing.T) { pages: []*manga.Page{ createTestPage(t, 1, 800, 1200, "png"), createTestPage(t, 2, 800, 1200, "jpeg"), + createTestPage(t, 3, 800, 1200, "gif"), }, split: false, expectSplit: false, - numExpected: 2, + numExpected: 3, }, { name: "Tall image with split enabled", @@ -147,7 +154,7 @@ func TestConverter_ConvertChapter(t *testing.T) { name: "Tall image without split", pages: []*manga.Page{createTestPage(t, 1, 800, webpMaxHeight+100, "png")}, split: false, - expectError: true, + expectError: false, numExpected: 1, }, } @@ -236,6 +243,12 @@ func TestConverter_convertPage(t *testing.T) { isToBeConverted: true, expectWebP: true, }, + { + name: "Convert GIF to WebP", + format: "gif", + isToBeConverted: true, + expectWebP: true, + }, { name: "Already WebP", format: "webp", @@ -333,8 +346,8 @@ func TestConverter_ConvertChapter_Timeout(t *testing.T) { // Create a test chapter with a few pages pages := []*manga.Page{ createTestPage(t, 1, 800, 1200, "jpeg"), - createTestPage(t, 2, 800, 1200, "jpeg"), - createTestPage(t, 3, 800, 1200, "jpeg"), + createTestPage(t, 2, 800, 1200, "png"), + createTestPage(t, 3, 800, 1200, "gif"), } chapter := &manga.Chapter{