support limit and split

This commit is contained in:
Celogeek 2022-12-27 19:59:17 +01:00
parent 6dcaf4b7f3
commit ba7291eb65
Signed by: celogeek
GPG Key ID: E6B7BDCFC446233A
2 changed files with 111 additions and 48 deletions

View File

@ -20,7 +20,7 @@ import (
imageconverter "go-comic-converter/internal/image-converter" imageconverter "go-comic-converter/internal/image-converter"
) )
type Images struct { type Image struct {
Id int Id int
Data []byte Data []byte
Width int Width int
@ -44,10 +44,17 @@ type EPub struct {
Error error Error error
ImagesCount int ImagesCount int
ProcessingImages func() chan *Images ProcessingImages func() chan *Image
TemplateProcessor *template.Template TemplateProcessor *template.Template
} }
type EpubPart struct {
Idx int
Cover *Image
Images []*Image
Suffix string
}
func NewEpub(path string) *EPub { func NewEpub(path string) *EPub {
uid, err := uuid.NewV4() uid, err := uuid.NewV4()
if err != nil { if err != nil {
@ -178,16 +185,16 @@ func (e *EPub) LoadDir(dirname string) *EPub {
todo := make(chan *Todo) todo := make(chan *Todo)
e.ProcessingImages = func() chan *Images { e.ProcessingImages = func() chan *Image {
wg := &sync.WaitGroup{} wg := &sync.WaitGroup{}
results := make(chan *Images) results := make(chan *Image)
for i := 0; i < runtime.NumCPU(); i++ { for i := 0; i < runtime.NumCPU(); i++ {
wg.Add(1) wg.Add(1)
go func() { go func() {
defer wg.Done() defer wg.Done()
for task := range todo { for task := range todo {
data, w, h := imageconverter.Convert(task.Path, e.Crop, e.ViewWidth, e.ViewHeight, e.Quality) data, w, h := imageconverter.Convert(task.Path, e.Crop, e.ViewWidth, e.ViewHeight, e.Quality)
results <- &Images{ results <- &Image{
task.Id, task.Id,
data, data,
w, w,
@ -214,17 +221,8 @@ func (e *EPub) LoadDir(dirname string) *EPub {
return e return e
} }
func (e *EPub) Write() error { func (e *EPub) GetParts() <-chan *EpubPart {
if e.Error != nil { images := make([]*Image, e.ImagesCount)
return e.Error
}
w, err := os.Create(e.Path)
if err != nil {
return err
}
images := make([]*Images, e.ImagesCount)
totalSize := 0 totalSize := 0
bar := progressbar.Default(int64(e.ImagesCount), "Processing") bar := progressbar.Default(int64(e.ImagesCount), "Processing")
for img := range e.ProcessingImages() { for img := range e.ProcessingImages() {
@ -234,49 +232,107 @@ func (e *EPub) Write() error {
} }
bar.Close() bar.Close()
cover := images[0] epubPart := make(chan *EpubPart)
images = images[1:] go func() {
defer close(epubPart)
cover := images[0]
images = images[1:]
fmt.Println("Limit: ", e.LimitMb, e.LimitMb == 0)
if e.LimitMb == 0 {
epubPart <- &EpubPart{
Idx: 1,
Cover: cover,
Images: images,
Suffix: "",
}
return
}
fmt.Println(len(images)) maxSize := e.LimitMb * 1024 * 1024
fmt.Println("Total Size:", totalSize) currentSize := 512*1024 + len(cover.Data)
currentImages := make([]*Image, 0)
part := 1
for _, img := range images {
if len(currentImages) > 0 && currentSize+len(img.Data) > maxSize {
epubPart <- &EpubPart{
Idx: part,
Cover: cover,
Images: currentImages,
Suffix: fmt.Sprintf(" PART_%03d", part),
}
part += 1
currentSize = 512*1024 + len(cover.Data)
currentImages = make([]*Image, 0)
}
currentSize += len(img.Data)
currentImages = append(currentImages, img)
}
if len(currentImages) > 0 {
epubPart <- &EpubPart{
Idx: part,
Cover: cover,
Images: currentImages,
Suffix: fmt.Sprintf(" PART_%03d", part),
}
}
}()
return epubPart
}
func (e *EPub) Write() error {
if e.Error != nil {
return e.Error
}
type ZipContent struct { type ZipContent struct {
Name string Name string
Content any Content any
} }
zipContent := []ZipContent{ for part := range e.GetParts() {
{"mimetype", TEMPLATE_MIME_TYPE}, fmt.Printf("Writing part %d...\n", part.Idx)
{"META-INF/container.xml", gohtml.Format(TEMPLATE_CONTAINER)}, ext := filepath.Ext(e.Path)
{"OEBPS/content.opf", e.Render(TEMPLATE_CONTENT, map[string]any{"Info": e, "Images": images})}, path := fmt.Sprintf("%s%s%s", e.Path[0:len(e.Path)-len(ext)], part.Suffix, ext)
{"OEBPS/toc.ncx", e.Render(TEMPLATE_TOC, map[string]any{"Info": e, "Images": images})}, w, err := os.Create(path)
{"OEBPS/nav.xhtml", e.Render(TEMPLATE_NAV, map[string]any{"Info": e, "Images": images})}, if err != nil {
{"OEBPS/Text/style.css", TEMPLATE_STYLE}, return err
{"OEBPS/Text/cover.xhtml", e.Render(TEMPLATE_TEXT, map[string]any{ }
"Id": "cover",
"Width": cover.Width,
"Height": cover.Height,
})},
{"OEBPS/Images/cover.jpg", cover.Data},
}
wz := zip.NewWriter(w) zipContent := []ZipContent{
defer wz.Close() {"mimetype", TEMPLATE_MIME_TYPE},
for _, content := range zipContent { {"META-INF/container.xml", gohtml.Format(TEMPLATE_CONTAINER)},
if err := e.WriteFile(wz, content.Name, content.Content); err != nil { {"OEBPS/content.opf", e.Render(TEMPLATE_CONTENT, map[string]any{"Info": e, "Images": part.Images})},
return err {"OEBPS/toc.ncx", e.Render(TEMPLATE_TOC, map[string]any{"Info": e, "Images": part.Images})},
{"OEBPS/nav.xhtml", e.Render(TEMPLATE_NAV, map[string]any{"Info": e, "Images": part.Images})},
{"OEBPS/Text/style.css", TEMPLATE_STYLE},
{"OEBPS/Text/cover.xhtml", e.Render(TEMPLATE_TEXT, map[string]any{
"Id": "cover",
"Width": part.Cover.Width,
"Height": part.Cover.Height,
})},
{"OEBPS/Images/cover.jpg", part.Cover.Data},
} }
}
for _, img := range images { wz := zip.NewWriter(w)
text := fmt.Sprintf("OEBPS/Text/%d.xhtml", img.Id) for _, content := range zipContent {
image := fmt.Sprintf("OEBPS/Images/%d.jpg", img.Id) if err := e.WriteFile(wz, content.Name, content.Content); err != nil {
if err := e.WriteFile(wz, text, e.Render(TEMPLATE_TEXT, img)); err != nil { return err
return err }
} }
if err := e.WriteFile(wz, image, img.Data); err != nil {
return err for _, img := range part.Images {
text := fmt.Sprintf("OEBPS/Text/%d.xhtml", img.Id)
image := fmt.Sprintf("OEBPS/Images/%d.jpg", img.Id)
if err := e.WriteFile(wz, text, e.Render(TEMPLATE_TEXT, img)); err != nil {
return err
}
if err := e.WriteFile(wz, image, img.Data); err != nil {
return err
}
} }
wz.Close()
} }
return nil return nil

View File

@ -80,7 +80,7 @@ func main() {
flag.StringVar(&opt.Title, "title", "", "Title of the epub") flag.StringVar(&opt.Title, "title", "", "Title of the epub")
flag.IntVar(&opt.Quality, "quality", 85, "Quality of the image: Default 75") flag.IntVar(&opt.Quality, "quality", 85, "Quality of the image: Default 75")
flag.BoolVar(&opt.NoCrop, "nocrop", false, "Disable cropping: Default false") flag.BoolVar(&opt.NoCrop, "nocrop", false, "Disable cropping: Default false")
flag.IntVar(&opt.LimitMb, "limitmb", 0, "Limit size of the ePub: Default nolimit") flag.IntVar(&opt.LimitMb, "limitmb", 0, "Limit size of the ePub: Default nolimit, Minimum 20")
flag.Parse() flag.Parse()
if opt.Input == "" || opt.Output == "" { if opt.Input == "" || opt.Output == "" {
@ -95,6 +95,12 @@ func main() {
return return
} }
if opt.LimitMb > 0 && opt.LimitMb < 20 {
fmt.Println("LimitMb should be 0 or >= 20")
flag.Usage()
return
}
if opt.Title == "" { if opt.Title == "" {
opt.Title = filepath.Base(opt.Input) opt.Title = filepath.Base(opt.Input)
} }
@ -105,6 +111,7 @@ func main() {
SetSize(profile.Width, profile.Height). SetSize(profile.Width, profile.Height).
SetQuality(opt.Quality). SetQuality(opt.Quality).
SetCrop(!opt.NoCrop). SetCrop(!opt.NoCrop).
SetLimitMb(opt.LimitMb).
SetTitle(opt.Title). SetTitle(opt.Title).
SetAuthor(opt.Author). SetAuthor(opt.Author).
LoadDir(opt.Input). LoadDir(opt.Input).