mirror of
https://github.com/Belphemur/CBZOptimizer.git
synced 2025-10-13 20:18:52 +02:00
fix(chapter): fix chapter conversion.
Still need to figure out the memory issues Consolidates image conversion logic into a dedicated method. This change streamlines the conversion process by centralizing the setting of converted image data, extension, and size. It also introduces a flag to track whether an image has been converted. The old resource cleanup has been removed since it is not needed anymore.
This commit is contained in:
10
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
10
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="GoDfaErrorMayBeNotNil" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<methods>
|
||||||
|
<method importPath="github.com/belphemur/CBZOptimizer/converter" receiver="Converter" name="ConvertChapter" />
|
||||||
|
</methods>
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
16
.vscode/launch.json
vendored
Normal file
16
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Launch Package",
|
||||||
|
"type": "go",
|
||||||
|
"request": "launch",
|
||||||
|
"mode": "auto",
|
||||||
|
"program": "${fileDirname}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@@ -1,6 +1,9 @@
|
|||||||
package manga
|
package manga
|
||||||
|
|
||||||
import "image"
|
import (
|
||||||
|
"bytes"
|
||||||
|
"image"
|
||||||
|
)
|
||||||
|
|
||||||
// PageContainer is a struct that holds a manga page, its image, and the image format.
|
// PageContainer is a struct that holds a manga page, its image, and the image format.
|
||||||
type PageContainer struct {
|
type PageContainer struct {
|
||||||
@@ -12,16 +15,18 @@ type PageContainer struct {
|
|||||||
Format string
|
Format string
|
||||||
// IsToBeConverted is a boolean flag indicating whether the image needs to be converted to another format.
|
// IsToBeConverted is a boolean flag indicating whether the image needs to be converted to another format.
|
||||||
IsToBeConverted bool
|
IsToBeConverted bool
|
||||||
|
// HasBeenConverted is a boolean flag indicating whether the image has been converted to another format.
|
||||||
|
HasBeenConverted bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewContainer(Page *Page, img image.Image, format string, isToBeConverted bool) *PageContainer {
|
func NewContainer(Page *Page, img image.Image, format string, isToBeConverted bool) *PageContainer {
|
||||||
return &PageContainer{Page: Page, Image: img, Format: format, IsToBeConverted: isToBeConverted}
|
return &PageContainer{Page: Page, Image: img, Format: format, IsToBeConverted: isToBeConverted, HasBeenConverted: false}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close releases resources held by the PageContainer
|
// SetConverted sets the converted image, its extension, and its size in the PageContainer.
|
||||||
func (pc *PageContainer) Close() {
|
func (pc *PageContainer) SetConverted(converted *bytes.Buffer, extension string) {
|
||||||
pc.Image = nil
|
pc.Page.Contents = converted
|
||||||
if pc.Page != nil && pc.Page.Contents != nil {
|
pc.Page.Extension = extension
|
||||||
pc.Page.Contents.Reset()
|
pc.Page.Size = uint64(converted.Len())
|
||||||
}
|
pc.HasBeenConverted = true
|
||||||
}
|
}
|
||||||
|
@@ -80,7 +80,6 @@ func (converter *Converter) ConvertChapter(chapter *manga.Chapter, quality uint8
|
|||||||
go func(pageToConvert *manga.PageContainer) {
|
go func(pageToConvert *manga.PageContainer) {
|
||||||
defer func() {
|
defer func() {
|
||||||
wgConvertedPages.Done()
|
wgConvertedPages.Done()
|
||||||
pageToConvert.Close() // Clean up resources
|
|
||||||
<-guard
|
<-guard
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -243,9 +242,7 @@ func (converter *Converter) convertPage(container *manga.PageContainer, quality
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
container.Page.Contents = converted
|
container.SetConverted(converted, ".webp");
|
||||||
container.Page.Extension = ".webp"
|
|
||||||
container.Page.Size = uint64(converted.Len())
|
|
||||||
return container, nil
|
return container, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -4,37 +4,96 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
"image/draw"
|
"image/jpeg"
|
||||||
"image/png"
|
"image/png"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
_ "image/jpeg"
|
||||||
|
|
||||||
|
_ "golang.org/x/image/webp"
|
||||||
|
|
||||||
"github.com/belphemur/CBZOptimizer/v2/internal/manga"
|
"github.com/belphemur/CBZOptimizer/v2/internal/manga"
|
||||||
"github.com/belphemur/CBZOptimizer/v2/pkg/converter/constant"
|
"github.com/belphemur/CBZOptimizer/v2/pkg/converter/constant"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func createTestImage(width, height int) image.Image {
|
func createTestImage(width, height int, format string) (image.Image, error) {
|
||||||
img := image.NewRGBA(image.Rect(0, 0, width, height))
|
img := image.NewRGBA(image.Rect(0, 0, width, height))
|
||||||
draw.Draw(img, img.Bounds(), &image.Uniform{color.White}, image.Point{}, draw.Src)
|
|
||||||
return img
|
// Create a gradient pattern to ensure we have actual image data
|
||||||
|
for y := 0; y < height; y++ {
|
||||||
|
for x := 0; x < width; x++ {
|
||||||
|
img.Set(x, y, color.RGBA{
|
||||||
|
R: uint8((x * 255) / width),
|
||||||
|
G: uint8((y * 255) / height),
|
||||||
|
B: 100,
|
||||||
|
A: 255,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return img, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTestPage(t *testing.T, index int, width, height int) *manga.Page {
|
func encodeImage(img image.Image, format string) (*bytes.Buffer, string, error) {
|
||||||
img := createTestImage(width, height)
|
buf := new(bytes.Buffer)
|
||||||
var buf bytes.Buffer
|
|
||||||
err := png.Encode(&buf, img)
|
switch format {
|
||||||
|
case "jpeg", "jpg":
|
||||||
|
if err := jpeg.Encode(buf, img, &jpeg.Options{Quality: 85}); err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
return buf, ".jpg", nil
|
||||||
|
case "webp":
|
||||||
|
PrepareEncoder()
|
||||||
|
if err := Encode(buf, img, 80); err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
return buf, ".webp", nil
|
||||||
|
case "png":
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
if err := png.Encode(buf, img); err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
return buf, ".png", nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createTestPage(t *testing.T, index int, width, height int, format string) *manga.Page {
|
||||||
|
img, err := createTestImage(width, height, format)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
buf, ext, err := encodeImage(img, format)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
return &manga.Page{
|
return &manga.Page{
|
||||||
Index: uint16(index),
|
Index: uint16(index),
|
||||||
Contents: &buf,
|
Contents: buf,
|
||||||
Extension: ".png",
|
Extension: ext,
|
||||||
Size: uint64(buf.Len()),
|
Size: uint64(buf.Len()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateConvertedImage(t *testing.T, page *manga.Page) {
|
||||||
|
require.NotNil(t, page.Contents)
|
||||||
|
require.Greater(t, page.Contents.Len(), 0)
|
||||||
|
|
||||||
|
// Try to decode the image
|
||||||
|
img, format, err := image.Decode(bytes.NewReader(page.Contents.Bytes()))
|
||||||
|
require.NoError(t, err, "Failed to decode converted image")
|
||||||
|
|
||||||
|
if page.Extension == ".webp" {
|
||||||
|
assert.Equal(t, "webp", format, "Expected WebP format")
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NotNil(t, img)
|
||||||
|
bounds := img.Bounds()
|
||||||
|
assert.Greater(t, bounds.Dx(), 0, "Image width should be positive")
|
||||||
|
assert.Greater(t, bounds.Dy(), 0, "Image height should be positive")
|
||||||
|
}
|
||||||
|
|
||||||
// TestConverter_ConvertChapter tests the ConvertChapter method of the WebP converter.
|
// TestConverter_ConvertChapter tests the ConvertChapter method of the WebP converter.
|
||||||
// It verifies various scenarios including:
|
// It verifies various scenarios including:
|
||||||
// - Converting single normal images
|
// - Converting single normal images
|
||||||
@@ -63,7 +122,7 @@ func TestConverter_ConvertChapter(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Single normal image",
|
name: "Single normal image",
|
||||||
pages: []*manga.Page{createTestPage(t, 1, 800, 1200)},
|
pages: []*manga.Page{createTestPage(t, 1, 800, 1200, "jpeg")},
|
||||||
split: false,
|
split: false,
|
||||||
expectSplit: false,
|
expectSplit: false,
|
||||||
numExpected: 1,
|
numExpected: 1,
|
||||||
@@ -71,8 +130,8 @@ func TestConverter_ConvertChapter(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "Multiple normal images",
|
name: "Multiple normal images",
|
||||||
pages: []*manga.Page{
|
pages: []*manga.Page{
|
||||||
createTestPage(t, 1, 800, 1200),
|
createTestPage(t, 1, 800, 1200, "png"),
|
||||||
createTestPage(t, 2, 800, 1200),
|
createTestPage(t, 2, 800, 1200, "jpeg"),
|
||||||
},
|
},
|
||||||
split: false,
|
split: false,
|
||||||
expectSplit: false,
|
expectSplit: false,
|
||||||
@@ -80,14 +139,14 @@ func TestConverter_ConvertChapter(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Tall image with split enabled",
|
name: "Tall image with split enabled",
|
||||||
pages: []*manga.Page{createTestPage(t, 1, 800, 5000)},
|
pages: []*manga.Page{createTestPage(t, 1, 800, 5000, "jpeg")},
|
||||||
split: true,
|
split: true,
|
||||||
expectSplit: true,
|
expectSplit: true,
|
||||||
numExpected: 3, // Based on cropHeight of 2000
|
numExpected: 3, // Based on cropHeight of 2000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Tall image without split",
|
name: "Tall image without split",
|
||||||
pages: []*manga.Page{createTestPage(t, 1, 800, webpMaxHeight+100)},
|
pages: []*manga.Page{createTestPage(t, 1, 800, webpMaxHeight+100, "png")},
|
||||||
split: false,
|
split: false,
|
||||||
expectError: true,
|
expectError: true,
|
||||||
numExpected: 1,
|
numExpected: 1,
|
||||||
@@ -128,6 +187,11 @@ func TestConverter_ConvertChapter(t *testing.T) {
|
|||||||
require.NotNil(t, convertedChapter)
|
require.NotNil(t, convertedChapter)
|
||||||
assert.Len(t, convertedChapter.Pages, tt.numExpected)
|
assert.Len(t, convertedChapter.Pages, tt.numExpected)
|
||||||
|
|
||||||
|
// Validate all converted images
|
||||||
|
for _, page := range convertedChapter.Pages {
|
||||||
|
validateConvertedImage(t, page)
|
||||||
|
}
|
||||||
|
|
||||||
// Verify page order
|
// Verify page order
|
||||||
for i := 1; i < len(convertedChapter.Pages); i++ {
|
for i := 1; i < len(convertedChapter.Pages); i++ {
|
||||||
prevPage := convertedChapter.Pages[i-1]
|
prevPage := convertedChapter.Pages[i-1]
|
||||||
@@ -189,9 +253,10 @@ func TestConverter_convertPage(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
page := createTestPage(t, 1, 100, 100)
|
page := createTestPage(t, 1, 100, 100, tt.format)
|
||||||
container := manga.NewContainer(page, createTestImage(100, 100), tt.format, tt.isToBeConverted)
|
img, err := createTestImage(100, 100, tt.format)
|
||||||
defer container.Close()
|
require.NoError(t, err)
|
||||||
|
container := manga.NewContainer(page, img, tt.format, tt.isToBeConverted)
|
||||||
|
|
||||||
converted, err := converter.convertPage(container, 80)
|
converted, err := converter.convertPage(container, 80)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -199,6 +264,7 @@ func TestConverter_convertPage(t *testing.T) {
|
|||||||
|
|
||||||
if tt.expectWebP {
|
if tt.expectWebP {
|
||||||
assert.Equal(t, ".webp", converted.Page.Extension)
|
assert.Equal(t, ".webp", converted.Page.Extension)
|
||||||
|
validateConvertedImage(t, converted.Page)
|
||||||
} else {
|
} else {
|
||||||
assert.NotEqual(t, ".webp", converted.Page.Extension)
|
assert.NotEqual(t, ".webp", converted.Page.Extension)
|
||||||
}
|
}
|
||||||
@@ -238,7 +304,7 @@ func TestConverter_checkPageNeedsSplit(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
page := createTestPage(t, 1, 800, tt.imageHeight)
|
page := createTestPage(t, 1, 800, tt.imageHeight, "jpeg")
|
||||||
|
|
||||||
needsSplit, img, format, err := converter.checkPageNeedsSplit(page, tt.split)
|
needsSplit, img, format, err := converter.checkPageNeedsSplit(page, tt.split)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user