simplify, make only necessary public method

This commit is contained in:
Celogeek 2022-12-29 21:18:02 +01:00
parent 222e02f377
commit 7a27473cb3
Signed by: celogeek
GPG Key ID: E6B7BDCFC446233A
5 changed files with 116 additions and 159 deletions

View File

@ -19,40 +19,42 @@ import (
imageconverter "go-comic-converter/internal/image-converter"
)
type Image struct {
type image struct {
Id int
Data *ImageData
Data *imageData
Width int
Height int
}
type EPub struct {
Path string
UID string
type EpubOptions struct {
Input string
Output string
Title string
Author string
Publisher string
UpdatedAt string
ViewWidth int
ViewHeight int
Quality int
Crop bool
LimitMb int
Error error
ImagesCount int
ProcessingImages func() chan *Image
TemplateProcessor *template.Template
}
type EpubPart struct {
Cover *Image
Images []*Image
type ePub struct {
*EpubOptions
UID string
Publisher string
UpdatedAt string
imagesCount int
processingImages func() chan *image
templateProcessor *template.Template
}
func NewEpub(path string) *EPub {
type epubPart struct {
Cover *image
Images []*image
}
func NewEpub(options *EpubOptions) *ePub {
uid, err := uuid.NewV4()
if err != nil {
panic(err)
@ -64,55 +66,17 @@ func NewEpub(path string) *EPub {
"zoom": func(s int, z float32) int { return int(float32(s) * z) },
})
return &EPub{
Path: path,
return &ePub{
EpubOptions: options,
UID: uid.String(),
Title: "Unknown title",
Author: "Unknown author",
Publisher: "GO Comic Converter",
UpdatedAt: time.Now().UTC().Format("2006-01-02T15:04:05Z"),
ViewWidth: 0,
ViewHeight: 0,
Quality: 75,
TemplateProcessor: tmpl,
templateProcessor: tmpl,
}
}
func (e *EPub) SetTitle(title string) *EPub {
e.Title = title
return e
}
func (e *EPub) SetAuthor(author string) *EPub {
e.Author = author
return e
}
func (e *EPub) SetSize(w, h int) *EPub {
e.ViewWidth = w
e.ViewHeight = h
return e
}
func (e *EPub) SetQuality(q int) *EPub {
e.Quality = q
return e
}
func (e *EPub) SetCrop(c bool) *EPub {
e.Crop = c
return e
}
func (e *EPub) SetLimitMb(l int) *EPub {
e.LimitMb = l
return e
}
func (e *EPub) Render(templateString string, data any) string {
tmpl, err := e.TemplateProcessor.Parse(templateString)
func (e *ePub) render(templateString string, data any) string {
tmpl, err := e.templateProcessor.Parse(templateString)
if err != nil {
panic(err)
}
@ -124,34 +88,32 @@ func (e *EPub) Render(templateString string, data any) string {
return result.String()
}
func (e *EPub) Load(path string) *EPub {
fi, err := os.Stat(path)
func (e *ePub) load() error {
fi, err := os.Stat(e.Input)
if err != nil {
e.Error = err
return e
return err
}
if fi.IsDir() {
return e.LoadDir(path)
return e.loadDir()
}
switch ext := strings.ToLower(filepath.Ext(path)); ext {
switch ext := strings.ToLower(filepath.Ext(e.Input)); ext {
case ".cbz":
e.LoadCBZ(path)
return e.loadCBZ()
case ".cbr":
e.LoadCBR(path)
return e.loadCBR()
case ".pdf":
e.LoadPDF(path)
return e.loadPDF()
default:
e.Error = fmt.Errorf("unknown file format (%s): support .cbz, .cbr, .pdf", ext)
return fmt.Errorf("unknown file format (%s): support .cbz, .cbr, .pdf", ext)
}
return e
}
func (e *EPub) LoadCBZ(path string) *EPub {
r, err := zip.OpenReader(path)
func (e *ePub) loadCBZ() error {
r, err := zip.OpenReader(e.Input)
if err != nil {
e.Error = err
return err
}
images := make([]*zip.File, 0)
@ -165,16 +127,15 @@ func (e *EPub) LoadCBZ(path string) *EPub {
images = append(images, f)
}
if len(images) == 0 {
e.Error = fmt.Errorf("no images found")
r.Close()
return e
return fmt.Errorf("no images found")
}
sort.SliceStable(images, func(i, j int) bool {
return strings.Compare(images[i].Name, images[j].Name) < 0
})
e.ImagesCount = len(images)
e.imagesCount = len(images)
type Todo struct {
Id int
@ -183,10 +144,10 @@ func (e *EPub) LoadCBZ(path string) *EPub {
todo := make(chan *Todo)
e.ProcessingImages = func() chan *Image {
e.processingImages = func() chan *image {
// defer r.Close()
wg := &sync.WaitGroup{}
results := make(chan *Image)
results := make(chan *image)
for i := 0; i < runtime.NumCPU(); i++ {
wg.Add(1)
go func() {
@ -207,9 +168,9 @@ func (e *EPub) LoadCBZ(path string) *EPub {
if task.Id == 0 {
name = "OEBPS/Images/cover.jpg"
}
results <- &Image{
results <- &image{
task.Id,
NewImageData(name, data),
newImageData(name, data),
w,
h,
}
@ -229,22 +190,20 @@ func (e *EPub) LoadCBZ(path string) *EPub {
return results
}
return e
return nil
}
func (e *EPub) LoadCBR(path string) *EPub {
e.Error = fmt.Errorf("no implemented")
return e
func (e *ePub) loadCBR() error {
return fmt.Errorf("no implemented")
}
func (e *EPub) LoadPDF(path string) *EPub {
e.Error = fmt.Errorf("no implemented")
return e
func (e *ePub) loadPDF() error {
return fmt.Errorf("no implemented")
}
func (e *EPub) LoadDir(dirname string) *EPub {
func (e *ePub) loadDir() error {
images := make([]string, 0)
err := filepath.WalkDir(dirname, func(path string, d fs.DirEntry, err error) error {
err := filepath.WalkDir(e.Input, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
@ -260,16 +219,14 @@ func (e *EPub) LoadDir(dirname string) *EPub {
return nil
})
if err != nil {
e.Error = err
return e
return err
}
if len(images) == 0 {
e.Error = fmt.Errorf("no images found")
return e
return fmt.Errorf("no images found")
}
sort.Strings(images)
e.ImagesCount = len(images)
e.imagesCount = len(images)
type Todo struct {
Id int
@ -278,9 +235,9 @@ func (e *EPub) LoadDir(dirname string) *EPub {
todo := make(chan *Todo)
e.ProcessingImages = func() chan *Image {
e.processingImages = func() chan *image {
wg := &sync.WaitGroup{}
results := make(chan *Image)
results := make(chan *image)
for i := 0; i < runtime.NumCPU(); i++ {
wg.Add(1)
go func() {
@ -301,9 +258,9 @@ func (e *EPub) LoadDir(dirname string) *EPub {
if task.Id == 0 {
name = "OEBPS/Images/cover.jpg"
}
results <- &Image{
results <- &image{
task.Id,
NewImageData(name, data),
newImageData(name, data),
w,
h,
}
@ -321,27 +278,27 @@ func (e *EPub) LoadDir(dirname string) *EPub {
return results
}
return e
return nil
}
func (e *EPub) GetParts() []*EpubPart {
images := make([]*Image, e.ImagesCount)
bar := progressbar.Default(int64(e.ImagesCount), "Processing")
for img := range e.ProcessingImages() {
func (e *ePub) getParts() []*epubPart {
images := make([]*image, e.imagesCount)
bar := progressbar.Default(int64(e.imagesCount), "Processing")
for img := range e.processingImages() {
images[img.Id] = img
bar.Add(1)
}
bar.Close()
epubPart := make([]*EpubPart, 0)
parts := make([]*epubPart, 0)
cover := images[0]
images = images[1:]
if e.LimitMb == 0 {
epubPart = append(epubPart, &EpubPart{
parts = append(parts, &epubPart{
Cover: cover,
Images: images,
})
return epubPart
return parts
}
maxSize := uint64(e.LimitMb * 1024 * 1024)
@ -351,36 +308,36 @@ func (e *EPub) GetParts() []*EpubPart {
baseSize := uint64(16*1024) + cover.Data.CompressedSize()
currentSize := baseSize
currentImages := make([]*Image, 0)
currentImages := make([]*image, 0)
part := 1
for _, img := range images {
imgSize := img.Data.CompressedSize() + xhtmlSize
if len(currentImages) > 0 && currentSize+imgSize > maxSize {
epubPart = append(epubPart, &EpubPart{
parts = append(parts, &epubPart{
Cover: cover,
Images: currentImages,
})
part += 1
currentSize = baseSize
currentImages = make([]*Image, 0)
currentImages = make([]*image, 0)
}
currentSize += imgSize
currentImages = append(currentImages, img)
}
if len(currentImages) > 0 {
epubPart = append(epubPart, &EpubPart{
parts = append(parts, &epubPart{
Cover: cover,
Images: currentImages,
})
}
return epubPart
return parts
}
func (e *EPub) Write() error {
if e.Error != nil {
return e.Error
func (e *ePub) Write() error {
if err := e.load(); err != nil {
return err
}
type ZipContent struct {
@ -388,31 +345,31 @@ func (e *EPub) Write() error {
Content any
}
epubParts := e.GetParts()
epubParts := e.getParts()
totalParts := len(epubParts)
bar := progressbar.Default(int64(totalParts), "Writing Part")
for i, part := range epubParts {
ext := filepath.Ext(e.Path)
ext := filepath.Ext(e.Output)
suffix := ""
if totalParts > 1 {
suffix = fmt.Sprintf(" PART_%02d", i+1)
}
path := fmt.Sprintf("%s%s%s", e.Path[0:len(e.Path)-len(ext)], suffix, ext)
wz, err := NewEpubZip(path)
path := fmt.Sprintf("%s%s%s", e.Output[0:len(e.Output)-len(ext)], suffix, ext)
wz, err := newEpubZip(path)
if err != nil {
return err
}
defer wz.Close()
zipContent := []ZipContent{
{"META-INF/container.xml", TEMPLATE_CONTAINER},
{"OEBPS/content.opf", e.Render(TEMPLATE_CONTENT, map[string]any{"Info": e, "Images": part.Images})},
{"OEBPS/toc.ncx", e.Render(TEMPLATE_TOC, map[string]any{"Info": e, "Image": part.Images[0]})},
{"OEBPS/nav.xhtml", e.Render(TEMPLATE_NAV, map[string]any{"Info": e, "Image": part.Images[0]})},
{"OEBPS/Text/style.css", TEMPLATE_STYLE},
{"OEBPS/Text/part.xhtml", e.Render(TEMPLATE_PART, map[string]any{
{"META-INF/container.xml", containerTmpl},
{"OEBPS/content.opf", e.render(contentTmpl, map[string]any{"Info": e, "Images": part.Images})},
{"OEBPS/toc.ncx", e.render(tocTmpl, map[string]any{"Info": e, "Image": part.Images[0]})},
{"OEBPS/nav.xhtml", e.render(navTmpl, map[string]any{"Info": e, "Image": part.Images[0]})},
{"OEBPS/Text/style.css", styleTmpl},
{"OEBPS/Text/part.xhtml", e.render(partTmpl, map[string]any{
"Info": e,
"Part": i + 1,
"Total": totalParts,
@ -431,7 +388,7 @@ func (e *EPub) Write() error {
for _, img := range part.Images {
text := fmt.Sprintf("OEBPS/Text/%d.xhtml", img.Id)
if err := wz.WriteFile(text, e.Render(TEMPLATE_TEXT, img)); err != nil {
if err := wz.WriteFile(text, e.render(textTmpl, img)); err != nil {
return err
}
if err := wz.WriteImage(img.Data); err != nil {

View File

@ -8,16 +8,16 @@ import (
"time"
)
type ImageData struct {
type imageData struct {
Header *zip.FileHeader
Data []byte
}
func (img *ImageData) CompressedSize() uint64 {
func (img *imageData) CompressedSize() uint64 {
return img.Header.CompressedSize64 + 30 + uint64(len(img.Header.Name))
}
func NewImageData(name string, data []byte) *ImageData {
func newImageData(name string, data []byte) *imageData {
cdata := bytes.NewBuffer([]byte{})
wcdata, err := flate.NewWriter(cdata, flate.BestCompression)
if err != nil {
@ -29,7 +29,7 @@ func NewImageData(name string, data []byte) *ImageData {
panic(err)
}
t := time.Now()
return &ImageData{
return &imageData{
&zip.FileHeader{
Name: name,
CompressedSize64: uint64(cdata.Len()),

View File

@ -3,22 +3,22 @@ package epub
import _ "embed"
//go:embed "templates/container.xml.tmpl"
var TEMPLATE_CONTAINER string
var containerTmpl string
//go:embed "templates/content.opf.tmpl"
var TEMPLATE_CONTENT string
var contentTmpl string
//go:embed "templates/toc.ncx.tmpl"
var TEMPLATE_TOC string
var tocTmpl string
//go:embed "templates/nav.xhtml.tmpl"
var TEMPLATE_NAV string
var navTmpl string
//go:embed "templates/style.css.tmpl"
var TEMPLATE_STYLE string
var styleTmpl string
//go:embed "templates/part.xhtml.tmpl"
var TEMPLATE_PART string
var partTmpl string
//go:embed "templates/text.xhtml.tmpl"
var TEMPLATE_TEXT string
var textTmpl string

View File

@ -7,28 +7,28 @@ import (
"time"
)
type EpubZip struct {
type epubZip struct {
w *os.File
wz *zip.Writer
}
func NewEpubZip(path string) (*EpubZip, error) {
func newEpubZip(path string) (*epubZip, error) {
w, err := os.Create(path)
if err != nil {
return nil, err
}
wz := zip.NewWriter(w)
return &EpubZip{w, wz}, nil
return &epubZip{w, wz}, nil
}
func (e *EpubZip) Close() error {
func (e *epubZip) Close() error {
if err := e.wz.Close(); err != nil {
return err
}
return e.w.Close()
}
func (e *EpubZip) WriteMagic() error {
func (e *epubZip) WriteMagic() error {
t := time.Now()
fh := &zip.FileHeader{
Name: "mimetype",
@ -50,7 +50,7 @@ func (e *EpubZip) WriteMagic() error {
return err
}
func (e *EpubZip) WriteImage(image *ImageData) error {
func (e *epubZip) WriteImage(image *imageData) error {
m, err := e.wz.CreateRaw(image.Header)
if err != nil {
return err
@ -59,7 +59,7 @@ func (e *EpubZip) WriteImage(image *ImageData) error {
return err
}
func (e *EpubZip) WriteFile(file string, data any) error {
func (e *epubZip) WriteFile(file string, data any) error {
var content []byte
switch b := data.(type) {
case string:

22
main.go
View File

@ -124,17 +124,17 @@ func main() {
fmt.Println(opt)
err := epub.NewEpub(opt.Output).
SetSize(profile.Width, profile.Height).
SetQuality(opt.Quality).
SetCrop(!opt.NoCrop).
SetLimitMb(opt.LimitMb).
SetTitle(opt.Title).
SetAuthor(opt.Author).
Load(opt.Input).
Write()
if err != nil {
if err := epub.NewEpub(&epub.EpubOptions{
Input: opt.Input,
Output: opt.Output,
ViewWidth: profile.Width,
ViewHeight: profile.Height,
Quality: opt.Quality,
Crop: !opt.NoCrop,
LimitMb: opt.LimitMb,
Title: opt.Title,
Author: opt.Author,
}).Write(); err != nil {
fmt.Printf("Error: %v\n", err)
os.Exit(1)
}