From 673a7df699609885f92c2482a783df47f46c942b Mon Sep 17 00:00:00 2001 From: celogeek <65178+celogeek@users.noreply.github.com> Date: Sat, 15 Feb 2025 17:28:42 +0100 Subject: [PATCH 01/12] force cover and title to use jpeg format --- internal/pkg/epubimageprocessor/processor.go | 4 ++-- internal/pkg/epubtemplates/content.go | 4 ++-- pkg/epub/epub.go | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/pkg/epubimageprocessor/processor.go b/internal/pkg/epubimageprocessor/processor.go index bb2c368..98df60c 100644 --- a/internal/pkg/epubimageprocessor/processor.go +++ b/internal/pkg/epubimageprocessor/processor.go @@ -320,8 +320,8 @@ func (e EPUBImageProcessor) CoverTitleData(o CoverTitleDataOptions) (epubzip.Ima g.Draw(dst, o.Src) return epubzip.CompressImage( - "OEBPS/Images/"+o.Name+"."+e.Image.Format, - e.Image.Format, + "OEBPS/Images/"+o.Name+".jpeg", + "jpeg", dst, e.Image.Quality, ) diff --git a/internal/pkg/epubtemplates/content.go b/internal/pkg/epubtemplates/content.go index 7de258d..aa5483f 100644 --- a/internal/pkg/epubtemplates/content.go +++ b/internal/pkg/epubtemplates/content.go @@ -160,13 +160,13 @@ func (o Content) getManifest() []tag { {"item", tagAttrs{"id": "toc", "href": "toc.xhtml", "properties": "nav", "media-type": "application/xhtml+xml"}, ""}, {"item", tagAttrs{"id": "css", "href": "Text/style.css", "media-type": "text/css"}, ""}, {"item", tagAttrs{"id": "page_cover", "href": "Text/cover.xhtml", "media-type": "application/xhtml+xml"}, ""}, - {"item", tagAttrs{"id": "img_cover", "href": "Images/cover." + o.ImageOptions.Format, "media-type": o.ImageOptions.MediaType()}, ""}, + {"item", tagAttrs{"id": "img_cover", "href": "Images/cover.jpeg", "media-type": "image/jpeg"}, ""}, } if o.HasTitlePage { items = append(items, tag{"item", tagAttrs{"id": "page_title", "href": "Text/title.xhtml", "media-type": "application/xhtml+xml"}, ""}, - tag{"item", tagAttrs{"id": "img_title", "href": "Images/title." + o.ImageOptions.Format, "media-type": o.ImageOptions.MediaType()}, ""}, + tag{"item", tagAttrs{"id": "img_title", "href": "Images/title.jpeg", "media-type": "image/jpeg"}, ""}, ) if !o.ImageOptions.View.PortraitOnly { diff --git a/pkg/epub/epub.go b/pkg/epub/epub.go index debba78..9c44d66 100644 --- a/pkg/epub/epub.go +++ b/pkg/epub/epub.go @@ -115,7 +115,7 @@ func (e epub) writeCoverImage(wz epubzip.EPUBZip, img epubimage.EPUBImage, part, []byte(e.render(epubtemplates.Text, map[string]any{ "Title": title, "ViewPort": e.Image.View.Port(), - "ImagePath": "Images/cover." + e.Image.Format, + "ImagePath": "Images/cover.jpeg", "ImageStyle": img.ImgStyle(e.Image.View.Width, e.Image.View.Height, ""), })), ); err != nil { @@ -172,7 +172,7 @@ func (e epub) writeTitleImage(wz epubzip.EPUBZip, img epubimage.EPUBImage, title []byte(e.render(epubtemplates.Text, map[string]any{ "Title": title, "ViewPort": e.Image.View.Port(), - "ImagePath": "Images/title." + e.Image.Format, + "ImagePath": "Images/title.jpeg", "ImageStyle": img.ImgStyle(e.Image.View.Width, e.Image.View.Height, titleAlign), })), ); err != nil { From a5ef2a292737832778da8c0bb813d84a0c474f6e Mon Sep 17 00:00:00 2001 From: celogeek <65178+celogeek@users.noreply.github.com> Date: Sat, 15 Feb 2025 18:29:12 +0100 Subject: [PATCH 02/12] add format copy option --- internal/pkg/converter/converter.go | 7 ++++--- internal/pkg/epubimageprocessor/loader.go | 2 +- internal/pkg/epubimageprocessor/passthrough.go | 8 ++++++++ pkg/epub/epub.go | 14 +++++++++++++- 4 files changed, 26 insertions(+), 5 deletions(-) create mode 100644 internal/pkg/epubimageprocessor/passthrough.go diff --git a/internal/pkg/converter/converter.go b/internal/pkg/converter/converter.go index c0703c8..50e85ac 100644 --- a/internal/pkg/converter/converter.go +++ b/internal/pkg/converter/converter.go @@ -15,6 +15,7 @@ import ( "reflect" "regexp" "runtime" + "slices" "strings" "time" @@ -131,7 +132,7 @@ func (c *Converter) InitParse() { c.AddStringParam(&c.Options.Image.View.Color.Foreground, "foreground-color", c.Options.Image.View.Color.Foreground, "Foreground color in hexadecimal format RGB. Black=000, White=FFF") c.AddStringParam(&c.Options.Image.View.Color.Background, "background-color", c.Options.Image.View.Color.Background, "Background color in hexadecimal format RGB. Black=000, White=FFF, Light Gray=DDD, Dark Gray=777") c.AddBoolParam(&c.Options.Image.Resize, "resize", c.Options.Image.Resize, "Reduce image size if exceed device size") - c.AddStringParam(&c.Options.Image.Format, "format", c.Options.Image.Format, "Format of output images: jpeg (lossy), png (lossless)") + c.AddStringParam(&c.Options.Image.Format, "format", c.Options.Image.Format, "Format of output images: jpeg (lossy), png (lossless), copy (no processing)") c.AddFloatParam(&c.Options.Image.View.AspectRatio, "aspect-ratio", c.Options.Image.View.AspectRatio, "Aspect ratio (height/width) of the output\n -1 = same as device\n 0 = same as source\n1.6 = amazon advice for kindle") c.AddBoolParam(&c.Options.Image.View.PortraitOnly, "portrait-only", c.Options.Image.View.PortraitOnly, "Portrait only: force orientation to portrait only.") c.AddIntParam(&c.Options.TitlePage, "titlepage", c.Options.TitlePage, "Title page\n0 = never\n1 = always\n2 = only if epub is split") @@ -379,8 +380,8 @@ func (c *Converter) Validate() error { } // Format - if !(c.Options.Image.Format == "jpeg" || c.Options.Image.Format == "png") { - return errors.New("format should be jpeg or png") + if !slices.Contains([]string{"jpeg", "png", "copy"}, c.Options.Image.Format) { + return errors.New("format should be jpeg, png or copy") } // Aspect Ratio diff --git a/internal/pkg/epubimageprocessor/loader.go b/internal/pkg/epubimageprocessor/loader.go index a120385..ec2761c 100644 --- a/internal/pkg/epubimageprocessor/loader.go +++ b/internal/pkg/epubimageprocessor/loader.go @@ -53,7 +53,7 @@ func (e EPUBImageProcessor) isSupportedImage(path string) bool { return false } -// Load images from input +// load images from input func (e EPUBImageProcessor) load() (totalImages int, output chan task, err error) { fi, err := os.Stat(e.Input) if err != nil { diff --git a/internal/pkg/epubimageprocessor/passthrough.go b/internal/pkg/epubimageprocessor/passthrough.go new file mode 100644 index 0000000..672769a --- /dev/null +++ b/internal/pkg/epubimageprocessor/passthrough.go @@ -0,0 +1,8 @@ +package epubimageprocessor + +import "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubimage" + +func (e EPUBImageProcessor) PassThrough() (images []epubimage.EPUBImage, err error) { + images = make([]epubimage.EPUBImage, 0) + return images, nil +} diff --git a/pkg/epub/epub.go b/pkg/epub/epub.go index 9c44d66..ee22e05 100644 --- a/pkg/epub/epub.go +++ b/pkg/epub/epub.go @@ -3,6 +3,7 @@ package epub import ( "archive/zip" + "errors" "fmt" "math" "path/filepath" @@ -202,12 +203,23 @@ func (e epub) writeTitleImage(wz epubzip.EPUBZip, img epubimage.EPUBImage, title // extract image and split it into part func (e epub) getParts() (parts []epubPart, imgStorage epubzip.StorageImageReader, err error) { - images, err := e.imageProcessor.Load() + images, err := func() ([]epubimage.EPUBImage, error) { + if e.EPUBOptions.Image.Format == "copy" { + return e.imageProcessor.PassThrough() + } else { + return e.imageProcessor.Load() + } + }() if err != nil { return } + if len(images) == 0 { + err = errors.New("no image found") + return + } + // sort result by id and part sort.Slice(images, func(i, j int) bool { if images[i].Id == images[j].Id { From 87a127d04a4adc99ef1113d903efcce9cd7e4aa2 Mon Sep 17 00:00:00 2001 From: celogeek <65178+celogeek@users.noreply.github.com> Date: Sun, 16 Feb 2025 12:49:08 +0100 Subject: [PATCH 03/12] use epubimage format instead of global params --- internal/pkg/epubimage/epub_image.go | 5 +++++ internal/pkg/epubtemplates/content.go | 2 +- pkg/epuboptions/image.go | 4 ---- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/internal/pkg/epubimage/epub_image.go b/internal/pkg/epubimage/epub_image.go index 578e933..eb61663 100644 --- a/internal/pkg/epubimage/epub_image.go +++ b/internal/pkg/epubimage/epub_image.go @@ -73,6 +73,11 @@ func (i EPUBImage) EPUBImgPath() string { return "OEBPS/" + i.ImgPath() } +// MediaType of the epub image +func (i EPUBImage) MediaType() string { + return "image/" + i.Format +} + // ImgStyle style to apply to the image. // // center by default. diff --git a/internal/pkg/epubtemplates/content.go b/internal/pkg/epubtemplates/content.go index aa5483f..abd87f9 100644 --- a/internal/pkg/epubtemplates/content.go +++ b/internal/pkg/epubtemplates/content.go @@ -144,7 +144,7 @@ func (o Content) getManifest() []tag { var imageTags, pageTags, spaceTags []tag addTag := func(img epubimage.EPUBImage, withSpace bool) { imageTags = append(imageTags, - tag{"item", tagAttrs{"id": img.ImgKey(), "href": img.ImgPath(), "media-type": o.ImageOptions.MediaType()}, ""}, + tag{"item", tagAttrs{"id": img.ImgKey(), "href": img.ImgPath(), "media-type": img.MediaType()}, ""}, ) pageTags = append(pageTags, tag{"item", tagAttrs{"id": img.PageKey(), "href": img.PagePath(), "media-type": "application/xhtml+xml"}, ""}, diff --git a/pkg/epuboptions/image.go b/pkg/epuboptions/image.go index 1e78d1d..dc3f382 100644 --- a/pkg/epuboptions/image.go +++ b/pkg/epuboptions/image.go @@ -20,7 +20,3 @@ type Image struct { Format string `yaml:"format" json:"format"` AppleBookCompatibility bool `yaml:"apple_book_compatibility" json:"apple_book_compatibility"` } - -func (i Image) MediaType() string { - return "image/" + i.Format -} From 05dd8acc99d47bc945eaee373180889d56f8e224 Mon Sep 17 00:00:00 2001 From: celogeek <65178+celogeek@users.noreply.github.com> Date: Sun, 16 Feb 2025 12:49:37 +0100 Subject: [PATCH 04/12] no image is handled in loader --- pkg/epub/epub.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pkg/epub/epub.go b/pkg/epub/epub.go index ee22e05..cf01390 100644 --- a/pkg/epub/epub.go +++ b/pkg/epub/epub.go @@ -3,7 +3,6 @@ package epub import ( "archive/zip" - "errors" "fmt" "math" "path/filepath" @@ -215,11 +214,6 @@ func (e epub) getParts() (parts []epubPart, imgStorage epubzip.StorageImageReade return } - if len(images) == 0 { - err = errors.New("no image found") - return - } - // sort result by id and part sort.Slice(images, func(i, j int) bool { if images[i].Id == images[j].Id { From 2d21ced2fe10b4973b12054bbce32b0ecfd6da9b Mon Sep 17 00:00:00 2001 From: celogeek <65178+celogeek@users.noreply.github.com> Date: Sun, 16 Feb 2025 12:49:48 +0100 Subject: [PATCH 05/12] add compress raw data --- internal/pkg/epubzip/image.go | 36 +++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/internal/pkg/epubzip/image.go b/internal/pkg/epubzip/image.go index df87c4f..af479a6 100644 --- a/internal/pkg/epubzip/image.go +++ b/internal/pkg/epubzip/image.go @@ -66,3 +66,39 @@ func CompressImage(filename string, format string, img image.Image, quality int) cdata.Bytes(), }, nil } + +func CompressRaw(filename string, uncompressedData []byte) (Image, error) { + var ( + cdata bytes.Buffer + err error + ) + wcdata, err := flate.NewWriter(&cdata, flate.BestCompression) + if err != nil { + return Image{}, err + } + + _, err = wcdata.Write(uncompressedData) + if err != nil { + return Image{}, err + } + + err = wcdata.Close() + if err != nil { + return Image{}, err + } + + t := time.Now() + //goland:noinspection GoDeprecation + return Image{ + &zip.FileHeader{ + Name: filename, + CompressedSize64: uint64(cdata.Len()), + UncompressedSize64: uint64(len(uncompressedData)), + CRC32: crc32.Checksum(uncompressedData, crc32.IEEETable), + Method: zip.Deflate, + ModifiedTime: uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11), + ModifiedDate: uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9), + }, + cdata.Bytes(), + }, nil +} From 86fbb8cefe76ded59bd73194e9e976c03537fe93 Mon Sep 17 00:00:00 2001 From: celogeek <65178+celogeek@users.noreply.github.com> Date: Sun, 16 Feb 2025 12:50:01 +0100 Subject: [PATCH 06/12] add raw data to zip --- internal/pkg/epubzip/storage_image_writer.go | 21 ++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/internal/pkg/epubzip/storage_image_writer.go b/internal/pkg/epubzip/storage_image_writer.go index ccec171..e19b3f2 100644 --- a/internal/pkg/epubzip/storage_image_writer.go +++ b/internal/pkg/epubzip/storage_image_writer.go @@ -50,3 +50,24 @@ func (e StorageImageWriter) Add(filename string, img image.Image, quality int) e return nil } + +func (e StorageImageWriter) AddRaw(filename string, uncompressedData []byte) error { + zipImage, err := CompressRaw(filename, uncompressedData) + + if err != nil { + return err + } + + e.mut.Lock() + defer e.mut.Unlock() + fh, err := e.fz.CreateRaw(zipImage.Header) + if err != nil { + return err + } + _, err = fh.Write(zipImage.Data) + if err != nil { + return err + } + + return nil +} From ad614d09b4378496834ec9d1c7b4dacbc4152530 Mon Sep 17 00:00:00 2001 From: celogeek <65178+celogeek@users.noreply.github.com> Date: Sun, 16 Feb 2025 12:50:25 +0100 Subject: [PATCH 07/12] passthrough, support for dir --- .../pkg/epubimageprocessor/passthrough.go | 175 +++++++++++++++++- 1 file changed, 172 insertions(+), 3 deletions(-) diff --git a/internal/pkg/epubimageprocessor/passthrough.go b/internal/pkg/epubimageprocessor/passthrough.go index 672769a..21aea68 100644 --- a/internal/pkg/epubimageprocessor/passthrough.go +++ b/internal/pkg/epubimageprocessor/passthrough.go @@ -1,8 +1,177 @@ package epubimageprocessor -import "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubimage" +import ( + "bytes" + "fmt" + "image" + "image/jpeg" + "io" + "io/fs" + "os" + "path/filepath" + "slices" + "strings" + + "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubimage" + "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubprogress" + "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubzip" +) func (e EPUBImageProcessor) PassThrough() (images []epubimage.EPUBImage, err error) { - images = make([]epubimage.EPUBImage, 0) - return images, nil + fi, err := os.Stat(e.Input) + if err != nil { + return + } + + if fi.IsDir() { + return e.passThroughDir() + } else { + switch ext := strings.ToLower(filepath.Ext(e.Input)); ext { + case ".cbz", ".zip": + return e.passThroughCbz() + case ".cbr", ".rar": + return e.passThroughCbr() + default: + return nil, fmt.Errorf("unknown file format (%s): support .cbz, .zip, .cbr, .rar", ext) + } + } + +} + +func (e EPUBImageProcessor) passThroughDir() (images []epubimage.EPUBImage, err error) { + imagesPath := make([]string, 0) + + input := filepath.Clean(e.Input) + err = filepath.WalkDir(input, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + + // skip hidden files + if strings.HasPrefix(filepath.Base(path), ".") { + return nil + } + + if d.IsDir() { + return nil + } + + if slices.Contains([]string{".jpeg", ".jpg"}, strings.ToLower(filepath.Ext(path))) { + imagesPath = append(imagesPath, path) + } + return nil + }) + + if err != nil { + return + } + + if len(imagesPath) == 0 { + err = errNoImagesFound + return + } + + var imgStorage epubzip.StorageImageWriter + imgStorage, err = epubzip.NewStorageImageWriter(e.ImgStorage(), e.Image.Format) + if err != nil { + return + } + + // processing + bar := epubprogress.New(epubprogress.Options{ + Quiet: e.Quiet, + Json: e.Json, + Max: len(imagesPath), + Description: "Copying", + CurrentJob: 1, + TotalJob: 2, + }) + + for i, imgPath := range imagesPath { + var f *os.File + f, err = os.Open(imgPath) + if err != nil { + return + } + + var uncompressedData []byte + uncompressedData, err = io.ReadAll(f) + if err != nil { + return + } + + err = f.Close() + if err != nil { + return + } + + var config image.Config + config, err = jpeg.DecodeConfig(bytes.NewReader(uncompressedData)) + if err != nil { + return + } + + var rawImage image.Image + if i == 0 { + rawImage, err = jpeg.Decode(bytes.NewReader(uncompressedData)) + if err != nil { + return + } + } + + p, fn := filepath.Split(imgPath) + if p == input { + p = "" + } else { + p = p[len(input)+1:] + } + + img := epubimage.EPUBImage{ + Id: i, + Part: 0, + Raw: rawImage, + Width: config.Width, + Height: config.Height, + IsBlank: false, + DoublePage: config.Width > config.Height, + Path: p, + Name: fn, + Format: "jpeg", + OriginalAspectRatio: float64(config.Height) / float64(config.Width), + } + + err = imgStorage.AddRaw(img.EPUBImgPath(), uncompressedData) + if err != nil { + return + } + + images = append(images, img) + _ = bar.Add(1) + } + + err = imgStorage.Close() + if err != nil { + return + } + + _ = bar.Close() + + if len(images) == 0 { + err = errNoImagesFound + } + + return + +} + +func (e EPUBImageProcessor) passThroughCbz() (images []epubimage.EPUBImage, err error) { + images = make([]epubimage.EPUBImage, 0) + err = errNoImagesFound + return +} + +func (e EPUBImageProcessor) passThroughCbr() (images []epubimage.EPUBImage, err error) { + images = make([]epubimage.EPUBImage, 0) + err = errNoImagesFound + return } From af261d3f752d6e7fcc2340db3d1caf6e89e4121a Mon Sep 17 00:00:00 2001 From: celogeek <65178+celogeek@users.noreply.github.com> Date: Sun, 16 Feb 2025 16:55:57 +0100 Subject: [PATCH 08/12] passthrough, add png support for dir reading --- .../pkg/epubimageprocessor/passthrough.go | 40 ++++++++++++++----- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/internal/pkg/epubimageprocessor/passthrough.go b/internal/pkg/epubimageprocessor/passthrough.go index 21aea68..c842be6 100644 --- a/internal/pkg/epubimageprocessor/passthrough.go +++ b/internal/pkg/epubimageprocessor/passthrough.go @@ -5,6 +5,7 @@ import ( "fmt" "image" "image/jpeg" + "image/png" "io" "io/fs" "os" @@ -56,7 +57,7 @@ func (e EPUBImageProcessor) passThroughDir() (images []epubimage.EPUBImage, err return nil } - if slices.Contains([]string{".jpeg", ".jpg"}, strings.ToLower(filepath.Ext(path))) { + if slices.Contains([]string{".jpeg", ".jpg", ".png"}, strings.ToLower(filepath.Ext(path))) { imagesPath = append(imagesPath, path) } return nil @@ -105,27 +106,44 @@ func (e EPUBImageProcessor) passThroughDir() (images []epubimage.EPUBImage, err return } + p, fn := filepath.Split(imgPath) + if p == input { + p = "" + } else { + p = p[len(input)+1:] + } + + var ( + format string + decodeConfig func(r io.Reader) (image.Config, error) + decode func(r io.Reader) (image.Image, error) + ) + + switch filepath.Ext(fn) { + case ".png": + format = "png" + decodeConfig = png.DecodeConfig + decode = png.Decode + case ".jpg", ".jpeg": + format = "jpeg" + decodeConfig = jpeg.DecodeConfig + decode = jpeg.Decode + } + var config image.Config - config, err = jpeg.DecodeConfig(bytes.NewReader(uncompressedData)) + config, err = decodeConfig(bytes.NewReader(uncompressedData)) if err != nil { return } var rawImage image.Image if i == 0 { - rawImage, err = jpeg.Decode(bytes.NewReader(uncompressedData)) + rawImage, err = decode(bytes.NewReader(uncompressedData)) if err != nil { return } } - p, fn := filepath.Split(imgPath) - if p == input { - p = "" - } else { - p = p[len(input)+1:] - } - img := epubimage.EPUBImage{ Id: i, Part: 0, @@ -136,7 +154,7 @@ func (e EPUBImageProcessor) passThroughDir() (images []epubimage.EPUBImage, err DoublePage: config.Width > config.Height, Path: p, Name: fn, - Format: "jpeg", + Format: format, OriginalAspectRatio: float64(config.Height) / float64(config.Width), } From 277dff816180c9dafeece3bce7906e05f4fc7f9c Mon Sep 17 00:00:00 2001 From: celogeek <65178+celogeek@users.noreply.github.com> Date: Sun, 16 Feb 2025 17:42:13 +0100 Subject: [PATCH 09/12] passthrough, support zip/cbz, factor --- .../pkg/epubimageprocessor/passthrough.go | 256 ++++++++++++------ 1 file changed, 175 insertions(+), 81 deletions(-) diff --git a/internal/pkg/epubimageprocessor/passthrough.go b/internal/pkg/epubimageprocessor/passthrough.go index c842be6..421019f 100644 --- a/internal/pkg/epubimageprocessor/passthrough.go +++ b/internal/pkg/epubimageprocessor/passthrough.go @@ -1,6 +1,7 @@ package epubimageprocessor import ( + "archive/zip" "bytes" "fmt" "image" @@ -11,11 +12,13 @@ import ( "os" "path/filepath" "slices" + "sort" "strings" "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubimage" "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubprogress" "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubzip" + "github.com/celogeek/go-comic-converter/v3/internal/pkg/sortpath" ) func (e EPUBImageProcessor) PassThrough() (images []epubimage.EPUBImage, err error) { @@ -48,18 +51,10 @@ func (e EPUBImageProcessor) passThroughDir() (images []epubimage.EPUBImage, err return err } - // skip hidden files - if strings.HasPrefix(filepath.Base(path), ".") { - return nil - } - - if d.IsDir() { - return nil - } - - if slices.Contains([]string{".jpeg", ".jpg", ".png"}, strings.ToLower(filepath.Ext(path))) { + if filterCopyPath(d.IsDir(), path) { imagesPath = append(imagesPath, path) } + return nil }) @@ -72,11 +67,14 @@ func (e EPUBImageProcessor) passThroughDir() (images []epubimage.EPUBImage, err return } + sort.Sort(sortpath.By(imagesPath, e.SortPathMode)) + var imgStorage epubzip.StorageImageWriter imgStorage, err = epubzip.NewStorageImageWriter(e.ImgStorage(), e.Image.Format) if err != nil { return } + defer imgStorage.Close() // processing bar := epubprogress.New(epubprogress.Options{ @@ -87,6 +85,7 @@ func (e EPUBImageProcessor) passThroughDir() (images []epubimage.EPUBImage, err CurrentJob: 1, TotalJob: 2, }) + defer bar.Close() for i, imgPath := range imagesPath { var f *os.File @@ -95,70 +94,14 @@ func (e EPUBImageProcessor) passThroughDir() (images []epubimage.EPUBImage, err return } - var uncompressedData []byte - uncompressedData, err = io.ReadAll(f) - if err != nil { - return - } - - err = f.Close() - if err != nil { - return - } - - p, fn := filepath.Split(imgPath) - if p == input { - p = "" - } else { - p = p[len(input)+1:] - } - - var ( - format string - decodeConfig func(r io.Reader) (image.Config, error) - decode func(r io.Reader) (image.Image, error) + var img epubimage.EPUBImage + img, err = copyRawDataToStorage( + imgStorage, + f, + i, + input, + imgPath, ) - - switch filepath.Ext(fn) { - case ".png": - format = "png" - decodeConfig = png.DecodeConfig - decode = png.Decode - case ".jpg", ".jpeg": - format = "jpeg" - decodeConfig = jpeg.DecodeConfig - decode = jpeg.Decode - } - - var config image.Config - config, err = decodeConfig(bytes.NewReader(uncompressedData)) - if err != nil { - return - } - - var rawImage image.Image - if i == 0 { - rawImage, err = decode(bytes.NewReader(uncompressedData)) - if err != nil { - return - } - } - - img := epubimage.EPUBImage{ - Id: i, - Part: 0, - Raw: rawImage, - Width: config.Width, - Height: config.Height, - IsBlank: false, - DoublePage: config.Width > config.Height, - Path: p, - Name: fn, - Format: format, - OriginalAspectRatio: float64(config.Height) / float64(config.Width), - } - - err = imgStorage.AddRaw(img.EPUBImgPath(), uncompressedData) if err != nil { return } @@ -167,13 +110,6 @@ func (e EPUBImageProcessor) passThroughDir() (images []epubimage.EPUBImage, err _ = bar.Add(1) } - err = imgStorage.Close() - if err != nil { - return - } - - _ = bar.Close() - if len(images) == 0 { err = errNoImagesFound } @@ -184,7 +120,84 @@ func (e EPUBImageProcessor) passThroughDir() (images []epubimage.EPUBImage, err func (e EPUBImageProcessor) passThroughCbz() (images []epubimage.EPUBImage, err error) { images = make([]epubimage.EPUBImage, 0) - err = errNoImagesFound + + input := filepath.Clean(e.Input) + r, err := zip.OpenReader(input) + if err != nil { + return + } + defer r.Close() + + imagesZip := make([]*zip.File, 0) + for _, f := range r.File { + if filterCopyPath(f.FileInfo().IsDir(), f.Name) { + imagesZip = append(imagesZip, f) + } + } + + if len(imagesZip) == 0 { + err = errNoImagesFound + return + } + + var names []string + for _, img := range imagesZip { + names = append(names, img.Name) + } + + sort.Sort(sortpath.By(names, e.SortPathMode)) + + indexedNames := make(map[string]int) + for i, name := range names { + indexedNames[name] = i + } + + var imgStorage epubzip.StorageImageWriter + imgStorage, err = epubzip.NewStorageImageWriter(e.ImgStorage(), e.Image.Format) + if err != nil { + return + } + defer imgStorage.Close() + + // processing + bar := epubprogress.New(epubprogress.Options{ + Quiet: e.Quiet, + Json: e.Json, + Max: len(imagesZip), + Description: "Copying", + CurrentJob: 1, + TotalJob: 2, + }) + defer bar.Close() + + for _, imgZip := range imagesZip { + var f io.ReadCloser + f, err = imgZip.Open() + if err != nil { + return + } + + var img epubimage.EPUBImage + img, err = copyRawDataToStorage( + imgStorage, + f, + indexedNames[imgZip.Name], + "", + imgZip.Name, + ) + + if err != nil { + return + } + + images = append(images, img) + _ = bar.Add(1) + } + + if len(images) == 0 { + err = errNoImagesFound + } + return } @@ -193,3 +206,84 @@ func (e EPUBImageProcessor) passThroughCbr() (images []epubimage.EPUBImage, err err = errNoImagesFound return } + +func filterCopyPath(isDir bool, filename string) bool { + return !isDir && + !strings.HasPrefix(filepath.Base(filename), ".") && + slices.Contains([]string{".jpeg", ".jpg", ".png"}, strings.ToLower(filepath.Ext(filename))) +} + +func copyRawDataToStorage( + imgStorage epubzip.StorageImageWriter, + f io.ReadCloser, + id int, + dirname string, + filename string, +) (img epubimage.EPUBImage, err error) { + var uncompressedData []byte + uncompressedData, err = io.ReadAll(f) + if err != nil { + return + } + + err = f.Close() + if err != nil { + return + } + + p, fn := filepath.Split(filepath.Clean(filename)) + if p == dirname { + p = "" + } else { + p = p[len(dirname)+1:] + } + + var ( + format string + decodeConfig func(r io.Reader) (image.Config, error) + decode func(r io.Reader) (image.Image, error) + ) + + switch filepath.Ext(fn) { + case ".png": + format = "png" + decodeConfig = png.DecodeConfig + decode = png.Decode + case ".jpg", ".jpeg": + format = "jpeg" + decodeConfig = jpeg.DecodeConfig + decode = jpeg.Decode + } + + var config image.Config + config, err = decodeConfig(bytes.NewReader(uncompressedData)) + if err != nil { + return + } + + var rawImage image.Image + if id == 0 { + rawImage, err = decode(bytes.NewReader(uncompressedData)) + if err != nil { + return + } + } + + img = epubimage.EPUBImage{ + Id: id, + Part: 0, + Raw: rawImage, + Width: config.Width, + Height: config.Height, + IsBlank: false, + DoublePage: config.Width > config.Height, + Path: p, + Name: fn, + Format: format, + OriginalAspectRatio: float64(config.Height) / float64(config.Width), + } + + err = imgStorage.AddRaw(img.EPUBImgPath(), uncompressedData) + + return +} From 506cd1ad6cff001bf35a136d1f3341ba69eb4bbd Mon Sep 17 00:00:00 2001 From: celogeek <65178+celogeek@users.noreply.github.com> Date: Sun, 16 Feb 2025 18:14:37 +0100 Subject: [PATCH 10/12] passthrough, support rar/cbr --- .../pkg/epubimageprocessor/passthrough.go | 168 ++++++++++++++++-- 1 file changed, 153 insertions(+), 15 deletions(-) diff --git a/internal/pkg/epubimageprocessor/passthrough.go b/internal/pkg/epubimageprocessor/passthrough.go index 421019f..408bc9b 100644 --- a/internal/pkg/epubimageprocessor/passthrough.go +++ b/internal/pkg/epubimageprocessor/passthrough.go @@ -15,6 +15,8 @@ import ( "sort" "strings" + "github.com/nwaples/rardecode/v2" + "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubimage" "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubprogress" "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubzip" @@ -94,10 +96,17 @@ func (e EPUBImageProcessor) passThroughDir() (images []epubimage.EPUBImage, err return } + var uncompressedData []byte + uncompressedData, err = io.ReadAll(f) + if err != nil { + return + } + f.Close() + var img epubimage.EPUBImage img, err = copyRawDataToStorage( imgStorage, - f, + uncompressedData, i, input, imgPath, @@ -171,16 +180,27 @@ func (e EPUBImageProcessor) passThroughCbz() (images []epubimage.EPUBImage, err defer bar.Close() for _, imgZip := range imagesZip { + if _, ok := indexedNames[imgZip.Name]; !ok { + continue + } + var f io.ReadCloser f, err = imgZip.Open() if err != nil { return } + var uncompressedData []byte + uncompressedData, err = io.ReadAll(f) + if err != nil { + return + } + f.Close() + var img epubimage.EPUBImage img, err = copyRawDataToStorage( imgStorage, - f, + uncompressedData, indexedNames[imgZip.Name], "", imgZip.Name, @@ -203,7 +223,136 @@ func (e EPUBImageProcessor) passThroughCbz() (images []epubimage.EPUBImage, err func (e EPUBImageProcessor) passThroughCbr() (images []epubimage.EPUBImage, err error) { images = make([]epubimage.EPUBImage, 0) - err = errNoImagesFound + + var isSolid bool + files, err := rardecode.List(e.Input) + if err != nil { + return + } + + names := make([]string, 0) + for _, f := range files { + if filterCopyPath(f.IsDir, f.Name) { + if f.Solid { + isSolid = true + } + names = append(names, f.Name) + } + } + + if len(names) == 0 { + err = errNoImagesFound + return + } + + sort.Sort(sortpath.By(names, e.SortPathMode)) + + indexedNames := make(map[string]int) + for i, name := range names { + indexedNames[name] = i + } + + var imgStorage epubzip.StorageImageWriter + imgStorage, err = epubzip.NewStorageImageWriter(e.ImgStorage(), e.Image.Format) + if err != nil { + return + } + defer imgStorage.Close() + + // processing + bar := epubprogress.New(epubprogress.Options{ + Quiet: e.Quiet, + Json: e.Json, + Max: len(names), + Description: "Copying", + CurrentJob: 1, + TotalJob: 2, + }) + defer bar.Close() + + if isSolid { + var r *rardecode.ReadCloser + r, err = rardecode.OpenReader(e.Input) + if err != nil { + return + } + defer r.Close() + + for { + f, rerr := r.Next() + if rerr != nil { + if rerr == io.EOF { + break + } + err = rerr + return + } + + if _, ok := indexedNames[f.Name]; !ok { + continue + } + + var uncompressedData []byte + uncompressedData, err = io.ReadAll(r) + if err != nil { + return + } + + var img epubimage.EPUBImage + img, err = copyRawDataToStorage( + imgStorage, + uncompressedData, + indexedNames[f.Name], + "", + f.Name, + ) + + if err != nil { + return + } + + images = append(images, img) + _ = bar.Add(1) + } + } else { + for _, file := range files { + if i, ok := indexedNames[file.Name]; ok { + var f io.ReadCloser + f, err = file.Open() + if err != nil { + return + } + + var uncompressedData []byte + uncompressedData, err = io.ReadAll(f) + if err != nil { + return + } + f.Close() + + var img epubimage.EPUBImage + img, err = copyRawDataToStorage( + imgStorage, + uncompressedData, + i, + "", + file.Name, + ) + + if err != nil { + return + } + + images = append(images, img) + _ = bar.Add(1) + } + } + } + + if len(images) == 0 { + err = errNoImagesFound + } + return } @@ -215,22 +364,11 @@ func filterCopyPath(isDir bool, filename string) bool { func copyRawDataToStorage( imgStorage epubzip.StorageImageWriter, - f io.ReadCloser, + uncompressedData []byte, id int, dirname string, filename string, ) (img epubimage.EPUBImage, err error) { - var uncompressedData []byte - uncompressedData, err = io.ReadAll(f) - if err != nil { - return - } - - err = f.Close() - if err != nil { - return - } - p, fn := filepath.Split(filepath.Clean(filename)) if p == dirname { p = "" From dfb9a6fff34a7a608d82fb211b3289257eff081d Mon Sep 17 00:00:00 2001 From: celogeek <65178+celogeek@users.noreply.github.com> Date: Mon, 17 Feb 2025 09:22:42 +0100 Subject: [PATCH 11/12] separate passthrough from processor --- .../passthrough.go | 34 ++++++++++++++----- internal/pkg/epubimageprocessor/loader.go | 14 ++++---- internal/pkg/epubimageprocessor/processor.go | 21 +++++++----- pkg/epub/epub.go | 18 +++++----- 4 files changed, 55 insertions(+), 32 deletions(-) rename internal/pkg/{epubimageprocessor => epubimagepassthrough}/passthrough.go (88%) diff --git a/internal/pkg/epubimageprocessor/passthrough.go b/internal/pkg/epubimagepassthrough/passthrough.go similarity index 88% rename from internal/pkg/epubimageprocessor/passthrough.go rename to internal/pkg/epubimagepassthrough/passthrough.go index 408bc9b..02995ae 100644 --- a/internal/pkg/epubimageprocessor/passthrough.go +++ b/internal/pkg/epubimagepassthrough/passthrough.go @@ -1,8 +1,9 @@ -package epubimageprocessor +package epubimagepassthrough import ( "archive/zip" "bytes" + "errors" "fmt" "image" "image/jpeg" @@ -18,33 +19,48 @@ import ( "github.com/nwaples/rardecode/v2" "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubimage" + "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubimageprocessor" "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubprogress" "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubzip" "github.com/celogeek/go-comic-converter/v3/internal/pkg/sortpath" + "github.com/celogeek/go-comic-converter/v3/pkg/epuboptions" ) -func (e EPUBImageProcessor) PassThrough() (images []epubimage.EPUBImage, err error) { +type ePUBImagePassthrough struct { + epuboptions.EPUBOptions +} + +func (e ePUBImagePassthrough) Load() (images []epubimage.EPUBImage, err error) { fi, err := os.Stat(e.Input) if err != nil { return } if fi.IsDir() { - return e.passThroughDir() + return e.loadDir() } else { switch ext := strings.ToLower(filepath.Ext(e.Input)); ext { case ".cbz", ".zip": - return e.passThroughCbz() + return e.loadCbz() case ".cbr", ".rar": - return e.passThroughCbr() + return e.loadCbr() default: return nil, fmt.Errorf("unknown file format (%s): support .cbz, .zip, .cbr, .rar", ext) } } - } -func (e EPUBImageProcessor) passThroughDir() (images []epubimage.EPUBImage, err error) { +func (e ePUBImagePassthrough) CoverTitleData(o epubimageprocessor.CoverTitleDataOptions) (epubzip.Image, error) { + return epubimageprocessor.New(e.EPUBOptions).CoverTitleData(o) +} + +var errNoImagesFound = errors.New("no images found") + +func New(o epuboptions.EPUBOptions) epubimageprocessor.EPUBImageProcessor { + return ePUBImagePassthrough{o} +} + +func (e ePUBImagePassthrough) loadDir() (images []epubimage.EPUBImage, err error) { imagesPath := make([]string, 0) input := filepath.Clean(e.Input) @@ -127,7 +143,7 @@ func (e EPUBImageProcessor) passThroughDir() (images []epubimage.EPUBImage, err } -func (e EPUBImageProcessor) passThroughCbz() (images []epubimage.EPUBImage, err error) { +func (e ePUBImagePassthrough) loadCbz() (images []epubimage.EPUBImage, err error) { images = make([]epubimage.EPUBImage, 0) input := filepath.Clean(e.Input) @@ -221,7 +237,7 @@ func (e EPUBImageProcessor) passThroughCbz() (images []epubimage.EPUBImage, err return } -func (e EPUBImageProcessor) passThroughCbr() (images []epubimage.EPUBImage, err error) { +func (e ePUBImagePassthrough) loadCbr() (images []epubimage.EPUBImage, err error) { images = make([]epubimage.EPUBImage, 0) var isSolid bool diff --git a/internal/pkg/epubimageprocessor/loader.go b/internal/pkg/epubimageprocessor/loader.go index ec2761c..d6848fb 100644 --- a/internal/pkg/epubimageprocessor/loader.go +++ b/internal/pkg/epubimageprocessor/loader.go @@ -43,7 +43,7 @@ type task struct { var errNoImagesFound = errors.New("no images found") // only accept jpg, png and webp as source file -func (e EPUBImageProcessor) isSupportedImage(path string) bool { +func (e ePUBImageProcessor) isSupportedImage(path string) bool { switch strings.ToLower(filepath.Ext(path)) { case ".jpg", ".jpeg", ".png", ".webp", ".tiff": { @@ -54,7 +54,7 @@ func (e EPUBImageProcessor) isSupportedImage(path string) bool { } // load images from input -func (e EPUBImageProcessor) load() (totalImages int, output chan task, err error) { +func (e ePUBImageProcessor) load() (totalImages int, output chan task, err error) { fi, err := os.Stat(e.Input) if err != nil { return @@ -78,7 +78,7 @@ func (e EPUBImageProcessor) load() (totalImages int, output chan task, err error } } -func (e EPUBImageProcessor) corruptedImage(path, name string) image.Image { +func (e ePUBImageProcessor) corruptedImage(path, name string) image.Image { var w, h float64 = 1200, 1920 f, _ := truetype.Parse(gomonobold.TTF) face := truetype.NewFace(f, &truetype.Options{Size: 64, DPI: 72}) @@ -102,7 +102,7 @@ func (e EPUBImageProcessor) corruptedImage(path, name string) image.Image { } // load a directory of images -func (e EPUBImageProcessor) loadDir() (totalImages int, output chan task, err error) { +func (e ePUBImageProcessor) loadDir() (totalImages int, output chan task, err error) { images := make([]string, 0) input := filepath.Clean(e.Input) @@ -191,7 +191,7 @@ func (e EPUBImageProcessor) loadDir() (totalImages int, output chan task, err er } // load a zip file that include images -func (e EPUBImageProcessor) loadCbz() (totalImages int, output chan task, err error) { +func (e ePUBImageProcessor) loadCbz() (totalImages int, output chan task, err error) { r, err := zip.OpenReader(e.Input) if err != nil { return @@ -277,7 +277,7 @@ func (e EPUBImageProcessor) loadCbz() (totalImages int, output chan task, err er } // load a rar file that include images -func (e EPUBImageProcessor) loadCbr() (totalImages int, output chan task, err error) { +func (e ePUBImageProcessor) loadCbr() (totalImages int, output chan task, err error) { var isSolid bool files, err := rardecode.List(e.Input) if err != nil { @@ -393,7 +393,7 @@ func (e EPUBImageProcessor) loadCbr() (totalImages int, output chan task, err er } // extract image from a pdf -func (e EPUBImageProcessor) loadPdf() (totalImages int, output chan task, err error) { +func (e ePUBImageProcessor) loadPdf() (totalImages int, output chan task, err error) { pdf := pdfread.Load(e.Input) if pdf == nil { err = fmt.Errorf("can't read pdf") diff --git a/internal/pkg/epubimageprocessor/processor.go b/internal/pkg/epubimageprocessor/processor.go index 98df60c..ad2adf6 100644 --- a/internal/pkg/epubimageprocessor/processor.go +++ b/internal/pkg/epubimageprocessor/processor.go @@ -17,16 +17,21 @@ import ( "github.com/celogeek/go-comic-converter/v3/pkg/epuboptions" ) -type EPUBImageProcessor struct { +type EPUBImageProcessor interface { + Load() (images []epubimage.EPUBImage, err error) + CoverTitleData(o CoverTitleDataOptions) (epubzip.Image, error) +} + +type ePUBImageProcessor struct { epuboptions.EPUBOptions } func New(o epuboptions.EPUBOptions) EPUBImageProcessor { - return EPUBImageProcessor{o} + return ePUBImageProcessor{o} } // Load extract and convert images -func (e EPUBImageProcessor) Load() (images []epubimage.EPUBImage, err error) { +func (e ePUBImageProcessor) Load() (images []epubimage.EPUBImage, err error) { images = make([]epubimage.EPUBImage, 0) imageCount, imageInput, err := e.load() if err != nil { @@ -136,7 +141,7 @@ func (e EPUBImageProcessor) Load() (images []epubimage.EPUBImage, err error) { return images, nil } -func (e EPUBImageProcessor) createImage(src image.Image, r image.Rectangle) draw.Image { +func (e ePUBImageProcessor) createImage(src image.Image, r image.Rectangle) draw.Image { if e.EPUBOptions.Image.GrayScale { return image.NewGray(r) } @@ -169,7 +174,7 @@ func (e EPUBImageProcessor) createImage(src image.Image, r image.Rectangle) draw // transform image into 1 or 3 images // only doublepage with autosplit has 3 versions -func (e EPUBImageProcessor) transformImage(input task, part int, right bool) epubimage.EPUBImage { +func (e ePUBImageProcessor) transformImage(input task, part int, right bool) epubimage.EPUBImage { g := gift.New() src := input.Image srcBounds := src.Bounds() @@ -286,7 +291,7 @@ type CoverTitleDataOptions struct { BorderSize int } -func (e EPUBImageProcessor) Cover16LevelOfGray(bounds image.Rectangle) draw.Image { +func (e ePUBImageProcessor) cover16LevelOfGray(bounds image.Rectangle) draw.Image { return image.NewPaletted(bounds, color.Palette{ color.Gray{}, color.Gray{Y: 0x11}, @@ -308,12 +313,12 @@ func (e EPUBImageProcessor) Cover16LevelOfGray(bounds image.Rectangle) draw.Imag } // CoverTitleData create a title page with the cover -func (e EPUBImageProcessor) CoverTitleData(o CoverTitleDataOptions) (epubzip.Image, error) { +func (e ePUBImageProcessor) CoverTitleData(o CoverTitleDataOptions) (epubzip.Image, error) { // Create a blur version of the cover g := gift.New(epubimagefilters.CoverTitle(o.Text, o.Align, o.PctWidth, o.PctMargin, o.MaxFontSize, o.BorderSize)) var dst draw.Image if o.Name == "cover" && e.Image.GrayScale { - dst = e.Cover16LevelOfGray(o.Src.Bounds()) + dst = e.cover16LevelOfGray(o.Src.Bounds()) } else { dst = e.createImage(o.Src, g.Bounds(o.Src.Bounds())) } diff --git a/pkg/epub/epub.go b/pkg/epub/epub.go index cf01390..1861b1c 100644 --- a/pkg/epub/epub.go +++ b/pkg/epub/epub.go @@ -15,6 +15,7 @@ import ( "github.com/gofrs/uuid" "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubimage" + "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubimagepassthrough" "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubimageprocessor" "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubprogress" "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubtemplates" @@ -52,13 +53,20 @@ func New(options epuboptions.EPUBOptions) EPUB { "zoom": func(s int, z float32) int { return int(float32(s) * z) }, }) + var imageProcessor epubimageprocessor.EPUBImageProcessor + if options.Image.Format == "copy" { + imageProcessor = epubimagepassthrough.New(options) + } else { + imageProcessor = epubimageprocessor.New(options) + } + return epub{ EPUBOptions: options, UID: uid.String(), Publisher: "GO Comic Converter", UpdatedAt: time.Now().UTC().Format("2006-01-02T15:04:05Z"), templateProcessor: tmpl, - imageProcessor: epubimageprocessor.New(options), + imageProcessor: imageProcessor, } } @@ -202,13 +210,7 @@ func (e epub) writeTitleImage(wz epubzip.EPUBZip, img epubimage.EPUBImage, title // extract image and split it into part func (e epub) getParts() (parts []epubPart, imgStorage epubzip.StorageImageReader, err error) { - images, err := func() ([]epubimage.EPUBImage, error) { - if e.EPUBOptions.Image.Format == "copy" { - return e.imageProcessor.PassThrough() - } else { - return e.imageProcessor.Load() - } - }() + images, err := e.imageProcessor.Load() if err != nil { return From c0bc8ea01acd7d5fd7935332ac4d15e3cee3870a Mon Sep 17 00:00:00 2001 From: celogeek <65178+celogeek@users.noreply.github.com> Date: Fri, 21 Feb 2025 10:40:51 +0100 Subject: [PATCH 12/12] improve code --- .../pkg/epubimagepassthrough/passthrough.go | 113 ++++++++---------- 1 file changed, 51 insertions(+), 62 deletions(-) diff --git a/internal/pkg/epubimagepassthrough/passthrough.go b/internal/pkg/epubimagepassthrough/passthrough.go index 02995ae..93a5297 100644 --- a/internal/pkg/epubimagepassthrough/passthrough.go +++ b/internal/pkg/epubimagepassthrough/passthrough.go @@ -12,7 +12,6 @@ import ( "io/fs" "os" "path/filepath" - "slices" "sort" "strings" @@ -69,7 +68,7 @@ func (e ePUBImagePassthrough) loadDir() (images []epubimage.EPUBImage, err error return err } - if filterCopyPath(d.IsDir(), path) { + if !d.IsDir() && e.isSupportedImage(path) { imagesPath = append(imagesPath, path) } @@ -106,23 +105,18 @@ func (e ePUBImagePassthrough) loadDir() (images []epubimage.EPUBImage, err error defer bar.Close() for i, imgPath := range imagesPath { - var f *os.File - f, err = os.Open(imgPath) - if err != nil { - return - } - - var uncompressedData []byte - uncompressedData, err = io.ReadAll(f) - if err != nil { - return - } - f.Close() var img epubimage.EPUBImage - img, err = copyRawDataToStorage( + img, err = e.copyRawDataToStorage( imgStorage, - uncompressedData, + func() ([]byte, error) { + f, err := os.Open(imgPath) + if err != nil { + return nil, err + } + defer f.Close() + return io.ReadAll(f) + }, i, input, imgPath, @@ -155,7 +149,7 @@ func (e ePUBImagePassthrough) loadCbz() (images []epubimage.EPUBImage, err error imagesZip := make([]*zip.File, 0) for _, f := range r.File { - if filterCopyPath(f.FileInfo().IsDir(), f.Name) { + if !f.FileInfo().IsDir() && e.isSupportedImage(f.Name) { imagesZip = append(imagesZip, f) } } @@ -200,23 +194,18 @@ func (e ePUBImagePassthrough) loadCbz() (images []epubimage.EPUBImage, err error continue } - var f io.ReadCloser - f, err = imgZip.Open() - if err != nil { - return - } - - var uncompressedData []byte - uncompressedData, err = io.ReadAll(f) - if err != nil { - return - } - f.Close() - var img epubimage.EPUBImage - img, err = copyRawDataToStorage( + img, err = e.copyRawDataToStorage( imgStorage, - uncompressedData, + func() ([]byte, error) { + f, err := imgZip.Open() + if err != nil { + return nil, err + } + defer f.Close() + + return io.ReadAll(f) + }, indexedNames[imgZip.Name], "", imgZip.Name, @@ -248,7 +237,7 @@ func (e ePUBImagePassthrough) loadCbr() (images []epubimage.EPUBImage, err error names := make([]string, 0) for _, f := range files { - if filterCopyPath(f.IsDir, f.Name) { + if !f.IsDir && e.isSupportedImage(f.Name) { if f.Solid { isSolid = true } @@ -308,16 +297,12 @@ func (e ePUBImagePassthrough) loadCbr() (images []epubimage.EPUBImage, err error continue } - var uncompressedData []byte - uncompressedData, err = io.ReadAll(r) - if err != nil { - return - } - var img epubimage.EPUBImage - img, err = copyRawDataToStorage( + img, err = e.copyRawDataToStorage( imgStorage, - uncompressedData, + func() ([]byte, error) { + return io.ReadAll(r) + }, indexedNames[f.Name], "", f.Name, @@ -333,23 +318,17 @@ func (e ePUBImagePassthrough) loadCbr() (images []epubimage.EPUBImage, err error } else { for _, file := range files { if i, ok := indexedNames[file.Name]; ok { - var f io.ReadCloser - f, err = file.Open() - if err != nil { - return - } - - var uncompressedData []byte - uncompressedData, err = io.ReadAll(f) - if err != nil { - return - } - f.Close() - var img epubimage.EPUBImage - img, err = copyRawDataToStorage( + img, err = e.copyRawDataToStorage( imgStorage, - uncompressedData, + func() ([]byte, error) { + f, err := file.Open() + if err != nil { + return nil, err + } + defer f.Close() + return io.ReadAll(f) + }, i, "", file.Name, @@ -372,19 +351,29 @@ func (e ePUBImagePassthrough) loadCbr() (images []epubimage.EPUBImage, err error return } -func filterCopyPath(isDir bool, filename string) bool { - return !isDir && - !strings.HasPrefix(filepath.Base(filename), ".") && - slices.Contains([]string{".jpeg", ".jpg", ".png"}, strings.ToLower(filepath.Ext(filename))) +func (e ePUBImagePassthrough) isSupportedImage(path string) bool { + switch strings.ToLower(filepath.Ext(path)) { + case ".jpg", ".jpeg", ".png": + { + return !strings.HasPrefix(filepath.Base(path), ".") + } + } + return false } -func copyRawDataToStorage( +func (e ePUBImagePassthrough) copyRawDataToStorage( imgStorage epubzip.StorageImageWriter, - uncompressedData []byte, + getData func() ([]byte, error), id int, dirname string, filename string, ) (img epubimage.EPUBImage, err error) { + var uncompressedData []byte + uncompressedData, err = getData() + if err != nil { + return + } + p, fn := filepath.Split(filepath.Clean(filename)) if p == dirname { p = ""