diff --git a/.gitignore b/.gitignore index b79018b..f97b547 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ *.jpg *.jpeg *.pdf +*.cbz .DS_Store webtoon-dl opt diff --git a/main.go b/main.go index f7e971a..1717cf3 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "archive/zip" "bytes" "encoding/json" "flag" @@ -31,6 +32,94 @@ type EpisodeBatch struct { maxEp int } +type Comic interface { + addImage([]byte) error + save(outputPath string) error +} + +type PDFComic struct { + pdf *gopdf.GoPdf +} + +// validate PDFComic implements Comic +var _ Comic = &PDFComic{} + +func newPDFComic() *PDFComic { + pdf := gopdf.GoPdf{} + pdf.Start(gopdf.Config{Unit: gopdf.UnitPT, PageSize: *gopdf.PageSizeA4}) + return &PDFComic{pdf: &pdf} +} + +func (c *PDFComic) addImage(img []byte) error { + holder, err := gopdf.ImageHolderByBytes(img) + if err != nil { + return err + } + + d, _, err := image.DecodeConfig(bytes.NewReader(img)) + if err != nil { + return err + } + + // gopdf assumes dpi 128 https://github.com/signintech/gopdf/issues/168 + // W and H are in points, 1 point = 1/72 inch + // convert pixels (Width and Height) to points + // subtract 1 point to account for margins + c.pdf.AddPageWithOption(gopdf.PageOption{PageSize: &gopdf.Rect{ + W: float64(d.Width)*72/128 - 1, + H: float64(d.Height)*72/128 - 1, + }}) + return c.pdf.ImageByHolder(holder, 0, 0, nil) +} + +func (c *PDFComic) save(outputPath string) error { + return c.pdf.WritePdf(outputPath) +} + +type CBZComic struct { + zipWriter *zip.Writer + outFile *os.File + numFiles int +} + +// validate CBZComic implements Comic +var _ Comic = &CBZComic{} + +func newCBZComic() (*CBZComic, error) { + out, err := os.CreateTemp("", "output.tmp.cbz") + if err != nil { + return nil, err + } + zipWriter := zip.NewWriter(out) + return &CBZComic{zipWriter: zipWriter, outFile: out, numFiles: 0}, nil +} + +func (c *CBZComic) addImage(img []byte) error { + f, err := c.zipWriter.Create(fmt.Sprintf("%d.jpg", c.numFiles)) + if err != nil { + return err + } + _, err = f.Write(img) + if err != nil { + return err + } + c.numFiles++ + return nil +} + +func (c *CBZComic) save(outputPath string) error { + err := c.zipWriter.Close() + if err != nil { + return err + } + err = c.outFile.Close() + if err != nil { + return err + } + err = os.Rename(c.outFile.Name(), outputPath) + return err +} + func getOzPageImgLinks(doc soup.Root) []string { // regex find the documentURL, e.g: // viewerOptions: { @@ -310,21 +399,7 @@ func main() { fmt.Println(fmt.Sprintf("saving into %d files with max of %d episodes per file", len(episodeBatches), *epsPerFile)) for _, episodeBatch := range episodeBatches { - pdf := gopdf.GoPdf{} - pdf.Start(gopdf.Config{Unit: gopdf.UnitPT, PageSize: *gopdf.PageSizeA4}) - for idx, imgLink := range episodeBatch.imgLinks { - if strings.Contains(imgLink, ".gif") { - fmt.Println(fmt.Sprintf("WARNING: skipping gif %s", imgLink)) - continue - } - err := addImgToPdf(&pdf, imgLink) - if err != nil { - fmt.Println(err.Error()) - os.Exit(1) - } - fmt.Println(fmt.Sprintf("saving episodes %d through %d: added page %d/%d", episodeBatch.minEp, episodeBatch.maxEp, idx+1, len(episodeBatch.imgLinks))) - } - + var err error outURL := strings.ReplaceAll(url, "http://", "") outURL = strings.ReplaceAll(outURL, "https://", "") outURL = strings.ReplaceAll(outURL, "www.", "") @@ -337,12 +412,79 @@ func main() { } else { outURL = fmt.Sprintf("%s-epNo%d", outURL, episodeBatch.minEp) } - outPath := outURL + ".pdf" - err := pdf.WritePdf(outPath) + //comic := newPDFComic() + //outPath := outURL + ".pdf" + comic, err := newCBZComic() + if err != nil { + fmt.Println(err.Error()) + os.Exit(1) + } + outPath := outURL + ".cbz" + for idx, imgLink := range episodeBatch.imgLinks { + if strings.Contains(imgLink, ".gif") { + fmt.Println(fmt.Sprintf("WARNING: skipping gif %s", imgLink)) + continue + } + err := comic.addImage(fetchImage(imgLink)) + if err != nil { + fmt.Println(err.Error()) + os.Exit(1) + } + fmt.Println(fmt.Sprintf("saving episodes %d through %d: added page %d/%d", episodeBatch.minEp, episodeBatch.maxEp, idx+1, len(episodeBatch.imgLinks))) + } + + err = comic.save(outPath) if err != nil { fmt.Println(err.Error()) os.Exit(1) } fmt.Println(fmt.Sprintf("saved to %s", outPath)) } + + //createCbz := func(episodeBatches []EpisodeBatch, outPath string) error { + // out, err := os.Create(outPath) + // if err != nil { + // return err + // } + // defer func(out *os.File) { + // err := out.Close() + // if err != nil { + // fmt.Println(err.Error()) + // os.Exit(1) + // } + // }(out) + // + // zipWriter := zip.NewWriter(out) + // for _, episodeBatch := range episodeBatches { + // for idx, imgLink := range episodeBatch.imgLinks { + // if strings.Contains(imgLink, ".gif") { + // fmt.Println(fmt.Sprintf("WARNING: skipping gif %s", imgLink)) + // continue + // } + // img := fetchImage(imgLink) + // f, err := zipWriter.Create(fmt.Sprintf("%d.jpg", idx)) + // if err != nil { + // return err + // } + // _, err = f.Write(img) + // if err != nil { + // return err + // } + // fmt.Println(fmt.Sprintf("saving episodes %d through %d: added page %d/%d", episodeBatch.minEp, episodeBatch.maxEp, idx+1, len(episodeBatch.imgLinks))) + // } + // } + // err = zipWriter.Close() + // if err != nil { + // return err + // } + // return nil + //} + //// create cbz output from image links + //outPath := "output.cbz" + //err := createCbz(episodeBatches, outPath) + //if err != nil { + // fmt.Println(err.Error()) + // os.Exit(1) + //} + //fmt.Println(fmt.Sprintf("saved to %s", outPath)) }