package cbconvert import ( "fmt" "io" "math" "os" "path/filepath" "strings" ) // imagesFromPath returns list of found image files for given directory. func imagesFromPath(path string) ([]string, error) { var images []string walkFiles := func(fp string, f os.FileInfo, err error) error { if !f.IsDir() && f.Mode()&os.ModeType == 0 { if f.Size() > 0 && (isImage(fp)) { images = append(images, fp) } } return nil } f, err := filepath.Abs(path) if err != nil { return images, fmt.Errorf("imagesFromPath: %w", err) } stat, err := os.Stat(f) if err != nil { return images, fmt.Errorf("imagesFromPath: %w", err) } if !stat.IsDir() && stat.Mode()&os.ModeType == 0 { if isImage(f) { images = append(images, f) } } else { err = filepath.Walk(f, walkFiles) if err != nil { return images, fmt.Errorf("imagesFromPath: %w", err) } } return images, nil } // imagesFromSlice returns list of found image files for given slice of files. func imagesFromSlice(files []string) []string { var images []string for _, f := range files { if isImage(f) { images = append(images, f) } } return images } // filesFromPath returns list of all found files for given directory. func filesFromPath(path string) ([]string, error) { var files []string walkFiles := func(fp string, f os.FileInfo, err error) error { if !f.IsDir() && f.Mode()&os.ModeType == 0 && f.Size() > 0 { files = append(files, fp) } return nil } f, err := filepath.Abs(path) if err != nil { return files, fmt.Errorf("filesFromPath: %w", err) } stat, err := os.Stat(f) if err != nil { return files, fmt.Errorf("filesFromPath: %w", err) } if !stat.IsDir() && stat.Mode()&os.ModeType == 0 { files = append(files, f) } else if err = filepath.Walk(f, walkFiles); err != nil { return files, fmt.Errorf("filesFromPath: %w", err) } return files, nil } // sizeHuman formats a size in IEC units. func sizeHuman(s uint64) string { if s < 10 { return fmt.Sprintf("%d B", s) } sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"} e := math.Floor(math.Log(float64(s)) / math.Log(1024)) val := math.Floor(float64(s)/math.Pow(1024, e)*10+0.5) / 10 f := "%.0f %s" if val < 10 { f = "%.1f %s" } return fmt.Sprintf(f, val, sizes[int(e)]) } // isArchive checks if a file is archive. func isArchive(f string) bool { var types = []string{".rar", ".zip", ".7z", ".tar", ".cbr", ".cbz", ".cb7", ".cbt"} for _, t := range types { if strings.ToLower(filepath.Ext(f)) == t { return true } } return false } // isDocument checks if file is document. func isDocument(f string) bool { var types = []string{".pdf", ".epub", ".mobi", ".chm", ".fb2", ".docx", ".pptx", ".xlsx"} for _, t := range types { if strings.ToLower(filepath.Ext(f)) == t { return true } } return false } // isImage checks if file is image. func isImage(f string) bool { var types = []string{".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tiff", ".tif", ".webp", ".avif", ".jxl"} for _, t := range types { if strings.ToLower(filepath.Ext(f)) == t { return true } } return false } // isNonImage checks for allowed files in archive. func isNonImage(f string) bool { var types = []string{".nfo", ".xml", ".txt"} for _, t := range types { if strings.ToLower(filepath.Ext(f)) == t { return true } } return false } // isSize checks size of file. func isSize(a, b int64) bool { if a > 0 { if b < int64(a)*(1024*1024) { return false } } return true } // baseNoExt returns base name without extension. func baseNoExt(filename string) string { return strings.TrimSuffix(filepath.Base(filename), filepath.Ext(filename)) } // flatName flattens a path into a single collision-free name by replacing separators. func flatName(name string) string { name = strings.ReplaceAll(name, "\\", "/") return strings.ReplaceAll(name, "/", "_") } // copyFile copies reader to file. func copyFile(reader io.Reader, filename string) error { err := os.MkdirAll(filepath.Dir(filename), 0755) if err != nil { return fmt.Errorf("copyFile: %w", err) } file, err := os.Create(filename) if err != nil { return fmt.Errorf("copyFile: %w", err) } defer file.Close() _, err = io.Copy(file, reader) if err != nil { return fmt.Errorf("copyFile: %w", err) } return nil }