mirror of
https://github.com/Belphemur/CBZOptimizer.git
synced 2025-10-14 04:28:51 +02:00
feat(gif): support gif file
See .gif file extension support and more exception handling Fixes #105
This commit is contained in:
@@ -113,8 +113,9 @@ func ConvertCbzCommand(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
// Channel to manage the files to process
|
// Channel to manage the files to process
|
||||||
fileChan := make(chan string)
|
fileChan := make(chan string)
|
||||||
// Channel to collect errors
|
// Slice to collect errors with mutex for thread safety
|
||||||
errorChan := make(chan error, parallelism)
|
var errs []error
|
||||||
|
var errMutex sync.Mutex
|
||||||
|
|
||||||
// WaitGroup to wait for all goroutines to finish
|
// WaitGroup to wait for all goroutines to finish
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
@@ -138,7 +139,9 @@ func ConvertCbzCommand(cmd *cobra.Command, args []string) error {
|
|||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Int("worker_id", workerID).Str("file_path", path).Err(err).Msg("Worker encountered error")
|
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 {
|
} else {
|
||||||
log.Debug().Int("worker_id", workerID).Str("file_path", path).Msg("Worker completed file successfully")
|
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")
|
log.Debug().Msg("File channel closed, waiting for workers to complete")
|
||||||
wg.Wait() // Wait for all workers to finish
|
wg.Wait() // Wait for all workers to finish
|
||||||
log.Debug().Msg("All workers completed")
|
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 {
|
if len(errs) > 0 {
|
||||||
log.Error().Int("error_count", len(errs)).Msg("Command completed with errors")
|
log.Error().Int("error_count", len(errs)).Msg("Command completed with errors")
|
||||||
|
@@ -6,6 +6,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
|
_ "image/gif"
|
||||||
_ "image/jpeg"
|
_ "image/jpeg"
|
||||||
"image/png"
|
"image/png"
|
||||||
"runtime"
|
"runtime"
|
||||||
@@ -167,10 +168,15 @@ func (converter *Converter) ConvertChapter(ctx context.Context, chapter *manga.C
|
|||||||
|
|
||||||
splitNeeded, img, format, err := converter.checkPageNeedsSplit(page, split)
|
splitNeeded, img, format, err := converter.checkPageNeedsSplit(page, split)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
select {
|
var pageIgnoredError *converterrors.PageIgnoredError
|
||||||
case errChan <- err:
|
if errors.As(err, &pageIgnoredError) {
|
||||||
case <-ctx.Done():
|
log.Info().Err(err).Msg("Page ignored due to image decode error")
|
||||||
return
|
} else {
|
||||||
|
select {
|
||||||
|
case errChan <- err:
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if img != nil {
|
if img != nil {
|
||||||
wgConvertedPages.Add(1)
|
wgConvertedPages.Add(1)
|
||||||
@@ -357,7 +363,7 @@ func (converter *Converter) checkPageNeedsSplit(page *manga.Page, splitRequested
|
|||||||
img, format, err := image.Decode(reader)
|
img, format, err := image.Decode(reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug().Uint16("page_index", page.Index).Err(err).Msg("Failed to decode page image")
|
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()
|
bounds := img.Bounds()
|
||||||
|
@@ -5,6 +5,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
"image/gif"
|
||||||
"image/jpeg"
|
"image/jpeg"
|
||||||
"image/png"
|
"image/png"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -44,6 +45,11 @@ func encodeImage(img image.Image, format string) (*bytes.Buffer, string, error)
|
|||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
return buf, ".jpg", nil
|
return buf, ".jpg", nil
|
||||||
|
case "gif":
|
||||||
|
if err := gif.Encode(buf, img, nil); err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
return buf, ".gif", nil
|
||||||
case "webp":
|
case "webp":
|
||||||
PrepareEncoder()
|
PrepareEncoder()
|
||||||
if err := Encode(buf, img, 80); err != nil {
|
if err := Encode(buf, img, 80); err != nil {
|
||||||
@@ -131,10 +137,11 @@ func TestConverter_ConvertChapter(t *testing.T) {
|
|||||||
pages: []*manga.Page{
|
pages: []*manga.Page{
|
||||||
createTestPage(t, 1, 800, 1200, "png"),
|
createTestPage(t, 1, 800, 1200, "png"),
|
||||||
createTestPage(t, 2, 800, 1200, "jpeg"),
|
createTestPage(t, 2, 800, 1200, "jpeg"),
|
||||||
|
createTestPage(t, 3, 800, 1200, "gif"),
|
||||||
},
|
},
|
||||||
split: false,
|
split: false,
|
||||||
expectSplit: false,
|
expectSplit: false,
|
||||||
numExpected: 2,
|
numExpected: 3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Tall image with split enabled",
|
name: "Tall image with split enabled",
|
||||||
@@ -147,7 +154,7 @@ func TestConverter_ConvertChapter(t *testing.T) {
|
|||||||
name: "Tall image without split",
|
name: "Tall image without split",
|
||||||
pages: []*manga.Page{createTestPage(t, 1, 800, webpMaxHeight+100, "png")},
|
pages: []*manga.Page{createTestPage(t, 1, 800, webpMaxHeight+100, "png")},
|
||||||
split: false,
|
split: false,
|
||||||
expectError: true,
|
expectError: false,
|
||||||
numExpected: 1,
|
numExpected: 1,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -236,6 +243,12 @@ func TestConverter_convertPage(t *testing.T) {
|
|||||||
isToBeConverted: true,
|
isToBeConverted: true,
|
||||||
expectWebP: true,
|
expectWebP: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Convert GIF to WebP",
|
||||||
|
format: "gif",
|
||||||
|
isToBeConverted: true,
|
||||||
|
expectWebP: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Already WebP",
|
name: "Already WebP",
|
||||||
format: "webp",
|
format: "webp",
|
||||||
@@ -333,8 +346,8 @@ func TestConverter_ConvertChapter_Timeout(t *testing.T) {
|
|||||||
// Create a test chapter with a few pages
|
// Create a test chapter with a few pages
|
||||||
pages := []*manga.Page{
|
pages := []*manga.Page{
|
||||||
createTestPage(t, 1, 800, 1200, "jpeg"),
|
createTestPage(t, 1, 800, 1200, "jpeg"),
|
||||||
createTestPage(t, 2, 800, 1200, "jpeg"),
|
createTestPage(t, 2, 800, 1200, "png"),
|
||||||
createTestPage(t, 3, 800, 1200, "jpeg"),
|
createTestPage(t, 3, 800, 1200, "gif"),
|
||||||
}
|
}
|
||||||
|
|
||||||
chapter := &manga.Chapter{
|
chapter := &manga.Chapter{
|
||||||
|
Reference in New Issue
Block a user