From fdfa80875e2b4aa5599b21551bd1301541f57dec Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Tue, 29 Aug 2023 20:07:35 +0200 Subject: [PATCH] Add File type --- README.md | 2 +- cbconvert.go | 96 +++++++++++++++++++++++++------------------ cmd/cbconvert/main.go | 40 +++++++++--------- go.mod | 1 + go.sum | 2 + 5 files changed, 81 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index 4602965..5b3b978 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ It can convert comics to different formats to fit your various devices. ### Features -* reads RAR, ZIP, 7Z, CBR, CBZ, CB7, CBT, PDF, EPUB, MOBI and plain directory +* reads RAR, ZIP, 7Z, TAR, CBR, CBZ, CB7, CBT, PDF, EPUB, MOBI and plain directory * saves processed comics in CBZ (ZIP) archive format or CBT (TAR) * images can be converted to JPEG, PNG, TIFF, WEBP, AVIF, or 4-Bit BMP (16 colors) file format * rotate, flip, adjust brightness/contrast, adjust levels (Photoshop-like) or grayscale images diff --git a/cbconvert.go b/cbconvert.go index 1101f17..d1ffa06 100644 --- a/cbconvert.go +++ b/cbconvert.go @@ -30,6 +30,7 @@ import ( "golang.org/x/image/tiff" "github.com/disintegration/imaging" + "github.com/dustin/go-humanize" "github.com/fvbommel/sortorder" "github.com/gen2brain/go-fitz" "github.com/gen2brain/go-unarr" @@ -108,9 +109,9 @@ type Options struct { // Remove file FileRemove string // Output file - Outfile string + OutFile string // Output directory - Outdir string + OutDir string // Convert images to grayscale (monochromatic) Grayscale bool // Rotate images, valid values are 0, 90, 180, 270 @@ -118,25 +119,25 @@ type Options struct { // Flip images, valid values are none, horizontal, vertical Flip string // Adjust the brightness of the images, must be in the range (-100, 100) - Brightness float64 + Brightness int // Adjust the contrast of the images, must be in the range (-100, 100) - Contrast float64 + Contrast int // Process subdirectories recursively Recursive bool // Process only files larger than size (in MB) - Size int64 + Size int // Hide console output Quiet bool // Shadow input value - LevelsInMin float64 + LevelsInMin int // Highlight input value - LevelsInMax float64 + LevelsInMax int // Midpoint/gamma LevelsGamma float64 // Shadow output value - LevelsOutMin float64 + LevelsOutMin int // Highlight output value - LevelsOutMax float64 + LevelsOutMax int } // Convertor type. @@ -161,6 +162,14 @@ type Convertor struct { OnCompress func() } +// File type. +type File struct { + Name string + Path string + Size int64 + SizeHuman string +} + // New returns new convertor. func New(o Options) *Convertor { c := &Convertor{} @@ -501,11 +510,11 @@ func (c *Convertor) imageTransform(img image.Image) image.Image { } if c.Opts.Brightness != 0 { - i = imaging.AdjustBrightness(i, c.Opts.Brightness) + i = imaging.AdjustBrightness(i, float64(c.Opts.Brightness)) } if c.Opts.Contrast != 0 { - i = imaging.AdjustContrast(i, c.Opts.Contrast) + i = imaging.AdjustContrast(i, float64(c.Opts.Contrast)) } return i @@ -525,16 +534,16 @@ func (c *Convertor) imageLevel(img image.Image) (image.Image, error) { _, qrange := imagick.GetQuantumRange() quantumRange := float64(qrange) - inmin := (quantumRange * c.Opts.LevelsInMin) / 255 - inmax := (quantumRange * c.Opts.LevelsInMax) / 255 - outmin := (quantumRange * c.Opts.LevelsOutMin) / 255 - outmax := (quantumRange * c.Opts.LevelsOutMax) / 255 + inMin := (quantumRange * float64(c.Opts.LevelsInMin)) / 255 + inMax := (quantumRange * float64(c.Opts.LevelsInMax)) / 255 + outMin := (quantumRange * float64(c.Opts.LevelsOutMin)) / 255 + outMax := (quantumRange * float64(c.Opts.LevelsOutMax)) / 255 - if err := mw.LevelImage(inmin, c.Opts.LevelsGamma, inmax); err != nil { + if err := mw.LevelImage(inMin, c.Opts.LevelsGamma, inMax); err != nil { return img, fmt.Errorf("imageLevel: %w", err) } - if err := mw.LevelImage(-outmin, 1.0, quantumRange+(quantumRange-outmax)); err != nil { + if err := mw.LevelImage(-outMin, 1.0, quantumRange+(quantumRange-outMax)); err != nil { return img, fmt.Errorf("imageLevel: %w", err) } @@ -752,14 +761,14 @@ func (c *Convertor) archiveSaveZip(fileName string) error { var zipName string if c.Opts.Recursive { - err := os.MkdirAll(filepath.Join(c.Opts.Outdir, filepath.Dir(fileName)), 0755) + err := os.MkdirAll(filepath.Join(c.Opts.OutDir, filepath.Dir(fileName)), 0755) if err != nil { return fmt.Errorf("archiveSaveZip: %w", err) } - zipName = filepath.Join(c.Opts.Outdir, filepath.Dir(fileName), fmt.Sprintf("%s%s.cbz", c.baseNoExt(fileName), c.Opts.Suffix)) + zipName = filepath.Join(c.Opts.OutDir, filepath.Dir(fileName), fmt.Sprintf("%s%s.cbz", c.baseNoExt(fileName), c.Opts.Suffix)) } else { - zipName = filepath.Join(c.Opts.Outdir, fmt.Sprintf("%s%s.cbz", c.baseNoExt(fileName), c.Opts.Suffix)) + zipName = filepath.Join(c.Opts.OutDir, fmt.Sprintf("%s%s.cbz", c.baseNoExt(fileName), c.Opts.Suffix)) } zipFile, err := os.Create(zipName) @@ -826,14 +835,14 @@ func (c *Convertor) archiveSaveTar(fileName string) error { var tarName string if c.Opts.Recursive { - err := os.MkdirAll(filepath.Join(c.Opts.Outdir, filepath.Dir(fileName)), 0755) + err := os.MkdirAll(filepath.Join(c.Opts.OutDir, filepath.Dir(fileName)), 0755) if err != nil { return fmt.Errorf("archiveSaveTar: %w", err) } - tarName = filepath.Join(c.Opts.Outdir, filepath.Dir(fileName), fmt.Sprintf("%s%s.cbt", c.baseNoExt(fileName), c.Opts.Suffix)) + tarName = filepath.Join(c.Opts.OutDir, filepath.Dir(fileName), fmt.Sprintf("%s%s.cbt", c.baseNoExt(fileName), c.Opts.Suffix)) } else { - tarName = filepath.Join(c.Opts.Outdir, fmt.Sprintf("%s%s.cbt", c.baseNoExt(fileName), c.Opts.Suffix)) + tarName = filepath.Join(c.Opts.OutDir, fmt.Sprintf("%s%s.cbt", c.baseNoExt(fileName), c.Opts.Suffix)) } tarFile, err := os.Create(tarName) @@ -1416,7 +1425,7 @@ func (c *Convertor) isNonImage(f string) bool { // isSize checks size of file. func (c *Convertor) isSize(size int64) bool { if c.Opts.Size > 0 { - if size < c.Opts.Size*(1024*1024) { + if size < int64(c.Opts.Size)*(1024*1024) { return false } } @@ -1471,8 +1480,17 @@ func (c *Convertor) Terminate() { } // Files returns list of found comic files. -func (c *Convertor) Files(args []string) ([]string, error) { - var files []string +func (c *Convertor) Files(args []string) ([]File, error) { + var files []File + + toFile := func(fp string, f os.FileInfo) File { + var file File + file.Name = filepath.Base(fp) + file.Path = fp + file.Size = f.Size() + file.SizeHuman = humanize.IBytes(uint64(file.Size)) + return file + } walkFiles := func(fp string, f os.FileInfo, err error) error { if f.IsDir() { @@ -1480,7 +1498,7 @@ func (c *Convertor) Files(args []string) ([]string, error) { } if c.isArchive(fp) || c.isDocument(fp) { if c.isSize(f.Size()) { - files = append(files, fp) + files = append(files, toFile(fp, f)) } } @@ -1502,7 +1520,7 @@ func (c *Convertor) Files(args []string) ([]string, error) { } if count > 1 { - files = append(files, fp) + files = append(files, toFile(fp, f)) } } @@ -1523,7 +1541,7 @@ func (c *Convertor) Files(args []string) ([]string, error) { if !stat.IsDir() { if c.isArchive(path) || c.isDocument(path) { if c.isSize(stat.Size()) { - files = append(files, path) + files = append(files, toFile(path, stat)) } } } else { @@ -1544,7 +1562,7 @@ func (c *Convertor) Files(args []string) ([]string, error) { return files, fmt.Errorf("%s: %w", arg, err) } if c.isSize(info.Size()) { - files = append(files, filepath.Join(path, f.Name())) + files = append(files, toFile(filepath.Join(path, f.Name()), info)) } } } @@ -1557,7 +1575,7 @@ func (c *Convertor) Files(args []string) ([]string, error) { return files, fmt.Errorf("%s: %w", arg, err) } } else { - files = append(files, path) + files = append(files, toFile(path, stat)) } } } @@ -1587,14 +1605,14 @@ func (c *Convertor) Cover(fileName string, fileInfo os.FileInfo) error { var fName string if c.Opts.Recursive { - err := os.MkdirAll(filepath.Join(c.Opts.Outdir, filepath.Dir(fileName)), 0755) + err := os.MkdirAll(filepath.Join(c.Opts.OutDir, filepath.Dir(fileName)), 0755) if err != nil { return fmt.Errorf("%s: %w", fileName, err) } - fName = filepath.Join(c.Opts.Outdir, filepath.Dir(fileName), fmt.Sprintf("%s.%s", c.baseNoExt(fileName), c.Opts.Format)) + fName = filepath.Join(c.Opts.OutDir, filepath.Dir(fileName), fmt.Sprintf("%s.%s", c.baseNoExt(fileName), c.Opts.Format)) } else { - fName = filepath.Join(c.Opts.Outdir, fmt.Sprintf("%s.%s", c.baseNoExt(fileName), c.Opts.Format)) + fName = filepath.Join(c.Opts.OutDir, fmt.Sprintf("%s.%s", c.baseNoExt(fileName), c.Opts.Format)) } switch c.Opts.Format { @@ -1642,21 +1660,21 @@ func (c *Convertor) Thumbnail(fileName string, info os.FileInfo) error { var fName string var fURI string - if c.Opts.Outfile == "" { + if c.Opts.OutFile == "" { fURI = "file://" + fileName if c.Opts.Recursive { - err := os.MkdirAll(filepath.Join(c.Opts.Outdir, filepath.Dir(fileName)), 0755) + err := os.MkdirAll(filepath.Join(c.Opts.OutDir, filepath.Dir(fileName)), 0755) if err != nil { return fmt.Errorf("%s: %w", fileName, err) } - fName = filepath.Join(c.Opts.Outdir, filepath.Dir(fileName), fmt.Sprintf("%x.png", md5.Sum([]byte(fURI)))) + fName = filepath.Join(c.Opts.OutDir, filepath.Dir(fileName), fmt.Sprintf("%x.png", md5.Sum([]byte(fURI)))) } else { - fName = filepath.Join(c.Opts.Outdir, fmt.Sprintf("%x.png", md5.Sum([]byte(fURI)))) + fName = filepath.Join(c.Opts.OutDir, fmt.Sprintf("%x.png", md5.Sum([]byte(fURI)))) } } else { - abs, _ := filepath.Abs(c.Opts.Outfile) + abs, _ := filepath.Abs(c.Opts.OutFile) fURI = "file://" + abs fName = abs } diff --git a/cmd/cbconvert/main.go b/cmd/cbconvert/main.go index e95e5f1..8722b57 100644 --- a/cmd/cbconvert/main.go +++ b/cmd/cbconvert/main.go @@ -30,8 +30,8 @@ func main() { } }() - if _, err := os.Stat(opts.Outdir); err != nil { - if err := os.MkdirAll(opts.Outdir, 0775); err != nil { + if _, err := os.Stat(opts.OutDir); err != nil { + if err := os.MkdirAll(opts.OutDir, 0775); err != nil { fmt.Println(err) } os.Exit(1) @@ -83,7 +83,7 @@ func main() { } for _, file := range files { - stat, err := os.Stat(file) + stat, err := os.Stat(file.Path) if err != nil { fmt.Println(err) os.Exit(1) @@ -91,7 +91,7 @@ func main() { switch { case opts.Meta: - ret, err := conv.Meta(file) + ret, err := conv.Meta(file.Path) if err != nil { fmt.Println(err) os.Exit(1) @@ -105,14 +105,14 @@ func main() { continue case opts.Cover: - if err := conv.Cover(file, stat); err != nil { + if err := conv.Cover(file.Path, stat); err != nil { fmt.Println(err) os.Exit(1) } continue case opts.Thumbnail: - if err = conv.Thumbnail(file, stat); err != nil { + if err = conv.Thumbnail(file.Path, stat); err != nil { fmt.Println(err) os.Exit(1) } @@ -120,7 +120,7 @@ func main() { continue } - if err := conv.Convert(file, stat); err != nil { + if err := conv.Convert(file.Path, stat); err != nil { fmt.Println(err) os.Exit(1) } @@ -151,16 +151,16 @@ func parseFlags() (cbconvert.Options, []string) { convert.BoolVar(&opts.Grayscale, "grayscale", false, "Convert images to grayscale (monochromatic)") convert.IntVar(&opts.Rotate, "rotate", 0, "Rotate images, valid values are 0, 90, 180, 270") convert.StringVar(&opts.Flip, "flip", "none", "Flip images, valid values are none, horizontal, vertical") - convert.Float64Var(&opts.Brightness, "brightness", 0, "Adjust the brightness of the images, must be in the range (-100, 100)") - convert.Float64Var(&opts.Contrast, "contrast", 0, "Adjust the contrast of the images, must be in the range (-100, 100)") + convert.IntVar(&opts.Brightness, "brightness", 0, "Adjust the brightness of the images, must be in the range (-100, 100)") + convert.IntVar(&opts.Contrast, "contrast", 0, "Adjust the contrast of the images, must be in the range (-100, 100)") convert.StringVar(&opts.Suffix, "suffix", "", "Add suffix to file basename") - convert.Float64Var(&opts.LevelsInMin, "levels-inmin", 0, "Shadow input value") + convert.IntVar(&opts.LevelsInMin, "levels-inmin", 0, "Shadow input value") convert.Float64Var(&opts.LevelsGamma, "levels-gamma", 1.0, "Midpoint/Gamma") - convert.Float64Var(&opts.LevelsInMax, "levels-inmax", 255, "Highlight input value") - convert.Float64Var(&opts.LevelsOutMin, "levels-outmin", 0, "Shadow output value") - convert.Float64Var(&opts.LevelsOutMax, "levels-outmax", 255, "Highlight output value") - convert.StringVar(&opts.Outdir, "outdir", ".", "Output directory") - convert.Int64Var(&opts.Size, "size", 0, "Process only files larger than size (in MB)") + convert.IntVar(&opts.LevelsInMax, "levels-inmax", 255, "Highlight input value") + convert.IntVar(&opts.LevelsOutMin, "levels-outmin", 0, "Shadow output value") + convert.IntVar(&opts.LevelsOutMax, "levels-outmax", 255, "Highlight output value") + convert.StringVar(&opts.OutDir, "outdir", ".", "Output directory") + convert.IntVar(&opts.Size, "size", 0, "Process only files larger than size (in MB)") convert.BoolVar(&opts.Recursive, "recursive", false, "Process subdirectories recursively") convert.BoolVar(&opts.Quiet, "quiet", false, "Hide console output") @@ -172,8 +172,8 @@ func parseFlags() (cbconvert.Options, []string) { cover.StringVar(&opts.Format, "format", "jpeg", "Image format, valid values are jpeg, png, tiff, bmp, webp, avif") cover.IntVar(&opts.Quality, "quality", 75, "Image quality") cover.IntVar(&opts.Filter, "filter", 2, "0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos") - cover.StringVar(&opts.Outdir, "outdir", ".", "Output directory") - cover.Int64Var(&opts.Size, "size", 0, "Process only files larger than size (in MB)") + cover.StringVar(&opts.OutDir, "outdir", ".", "Output directory") + cover.IntVar(&opts.Size, "size", 0, "Process only files larger than size (in MB)") cover.BoolVar(&opts.Recursive, "recursive", false, "Process subdirectories recursively") cover.BoolVar(&opts.Quiet, "quiet", false, "Hide console output") @@ -183,9 +183,9 @@ func parseFlags() (cbconvert.Options, []string) { thumbnail.IntVar(&opts.Height, "height", 0, "Image height") thumbnail.BoolVar(&opts.Fit, "fit", false, "Best fit for required width and height") thumbnail.IntVar(&opts.Filter, "filter", 2, "0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos") - thumbnail.StringVar(&opts.Outdir, "outdir", ".", "Output directory") - thumbnail.StringVar(&opts.Outfile, "outfile", "", "Output file") - thumbnail.Int64Var(&opts.Size, "size", 0, "Process only files larger than size (in MB)") + thumbnail.StringVar(&opts.OutDir, "outdir", ".", "Output directory") + thumbnail.StringVar(&opts.OutFile, "outfile", "", "Output file") + thumbnail.IntVar(&opts.Size, "size", 0, "Process only files larger than size (in MB)") thumbnail.BoolVar(&opts.Recursive, "recursive", false, "Process subdirectories recursively") thumbnail.BoolVar(&opts.Quiet, "quiet", false, "Hide console output") diff --git a/go.mod b/go.mod index a736549..dc3da5f 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.21 require ( github.com/chai2010/webp v1.1.1 github.com/disintegration/imaging v1.6.2 + github.com/dustin/go-humanize v1.0.1 github.com/fvbommel/sortorder v1.1.0 github.com/gen2brain/go-fitz v1.23.1 github.com/gen2brain/go-unarr v0.1.7 diff --git a/go.sum b/go.sum index 45af9d1..2906e76 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ github.com/chai2010/webp v1.1.1 h1:jTRmEccAJ4MGrhFOrPMpNGIJ/eybIgwKpcACsrTEapk= github.com/chai2010/webp v1.1.1/go.mod h1:0XVwvZWdjjdxpUEIf7b9g9VkHFnInUSYujwqTLEuldU= github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw= github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= github.com/gen2brain/go-fitz v1.23.1 h1:x69/szWZXpI3jZ57mMqCg7WqqvtYnQG0lXts3L6M1Fc=