Compare commits

...

5 Commits

Author SHA1 Message Date
ad614d09b4
passthrough, support for dir 2025-02-16 12:50:25 +01:00
86fbb8cefe
add raw data to zip 2025-02-16 12:50:01 +01:00
2d21ced2fe
add compress raw data 2025-02-16 12:49:48 +01:00
05dd8acc99
no image is handled in loader 2025-02-16 12:49:37 +01:00
87a127d04a
use epubimage format instead of global params 2025-02-16 12:49:08 +01:00
7 changed files with 235 additions and 14 deletions

View File

@ -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.

View File

@ -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
}

View File

@ -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"}, ""},

View File

@ -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
}

View File

@ -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
}

View File

@ -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 {

View File

@ -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
}