Fix outdir and recursive, issue #41

This commit is contained in:
Milan Nikolic
2026-06-24 06:42:48 +02:00
parent b8d82e920c
commit 1284b9ded7
5 changed files with 129 additions and 37 deletions
+39 -11
View File
@@ -93,6 +93,8 @@ type Converter struct {
Workdir string
// Page name prefix, set per input when combining
prefix string
// Input root for the current file, used to build recursive output paths
root string
// Number of files
Nfiles int
// Index of the current file
@@ -115,6 +117,7 @@ type Converter struct {
type File struct {
Name string
Path string
Root string
Stat os.FileInfo
SizeHuman string
}
@@ -157,11 +160,13 @@ func (c *Converter) Cancel() {
// Files returns list of found comic files.
func (c *Converter) Files(args []string) ([]File, error) {
var files []File
var root string
toFile := func(fp string, f os.FileInfo) File {
var file File
file.Name = filepath.Base(fp)
file.Path = fp
file.Root = root
file.Stat = f
file.SizeHuman = humanize.IBytes(uint64(f.Size()))
return file
@@ -214,12 +219,14 @@ func (c *Converter) Files(args []string) ([]File, error) {
}
if !stat.IsDir() {
root = filepath.Dir(path)
if isArchive(path) || isDocument(path) {
if isSize(int64(c.Opts.Size), stat.Size()) {
files = append(files, toFile(path, stat))
}
}
} else {
root = path
if c.Opts.Recursive {
if err := filepath.Walk(path, walkFiles); err != nil {
return files, fmt.Errorf("%s: %w", arg, err)
@@ -261,9 +268,26 @@ func (c *Converter) Files(args []string) ([]File, error) {
return files, nil
}
// recursiveDir mirrors the source path under OutDir, relative to the input root.
func (c *Converter) recursiveDir(fileName string) string {
dir := filepath.Dir(fileName)
if c.root != "" {
if rel, err := filepath.Rel(c.root, dir); err == nil {
return filepath.Join(c.Opts.OutDir, rel)
}
}
dir = strings.TrimPrefix(dir[len(filepath.VolumeName(dir)):], string(os.PathSeparator))
return filepath.Join(c.Opts.OutDir, dir)
}
// Cover extracts cover.
func (c *Converter) Cover(fileName string, fileInfo os.FileInfo) error {
func (c *Converter) Cover(file File) error {
c.CurrFile++
c.root = file.Root
fileName, fileInfo := file.Path, file.Stat
cover, err := c.coverImage(fileName, fileInfo)
if err != nil {
@@ -285,13 +309,12 @@ func (c *Converter) Cover(fileName string, fileInfo os.FileInfo) error {
var fName string
if c.Opts.Recursive {
fDir := strings.Split(filepath.Dir(fileName), string(os.PathSeparator))[1:]
err := os.MkdirAll(filepath.Join(c.Opts.OutDir, filepath.Join(fDir...)), 0755)
if err != nil {
outDir := c.recursiveDir(fileName)
if err := os.MkdirAll(outDir, 0755); err != nil {
return fmt.Errorf("%s: %w", fileName, err)
}
fName = filepath.Join(c.Opts.OutDir, filepath.Join(fDir...), fmt.Sprintf("%s.%s", baseNoExt(fileName), ext))
fName = filepath.Join(outDir, fmt.Sprintf("%s.%s", baseNoExt(fileName), ext))
} else {
fName = filepath.Join(c.Opts.OutDir, fmt.Sprintf("%s.%s", baseNoExt(fileName), ext))
}
@@ -310,8 +333,10 @@ func (c *Converter) Cover(fileName string, fileInfo os.FileInfo) error {
}
// Thumbnail extracts thumbnail.
func (c *Converter) Thumbnail(fileName string, fileInfo os.FileInfo) error {
func (c *Converter) Thumbnail(file File) error {
c.CurrFile++
c.root = file.Root
fileName, fileInfo := file.Path, file.Stat
cover, err := c.coverImage(fileName, fileInfo)
if err != nil {
@@ -352,13 +377,12 @@ func (c *Converter) Thumbnail(fileName string, fileInfo os.FileInfo) error {
fURI = "file://" + fileName
if c.Opts.Recursive {
fDir := strings.Split(filepath.Dir(fileName), string(os.PathSeparator))[1:]
err := os.MkdirAll(filepath.Join(c.Opts.OutDir, filepath.Join(fDir...)), 0755)
if err != nil {
outDir := c.recursiveDir(fileName)
if err := os.MkdirAll(outDir, 0755); err != nil {
return fmt.Errorf("%s: %w", fileName, err)
}
fName = filepath.Join(c.Opts.OutDir, filepath.Join(fDir...), fmt.Sprintf("%x.png", md5.Sum([]byte(fURI))))
fName = filepath.Join(outDir, fmt.Sprintf("%x.png", md5.Sum([]byte(fURI))))
} else {
fName = filepath.Join(c.Opts.OutDir, fmt.Sprintf("%x.png", md5.Sum([]byte(fURI))))
}
@@ -496,8 +520,10 @@ func (c *Converter) Preview(fileName string, fileInfo os.FileInfo, width, height
}
// Convert converts a comic book.
func (c *Converter) Convert(fileName string, fileInfo os.FileInfo) error {
func (c *Converter) Convert(file File) error {
c.CurrFile++
c.root = file.Root
fileName, fileInfo := file.Path, file.Stat
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
@@ -541,6 +567,8 @@ func (c *Converter) Combine(files []File) error {
return nil
}
c.root = ""
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
+6 -8
View File
@@ -33,13 +33,12 @@ func (c *Converter) archiveSaveZip(fileName string) error {
var zipName string
if c.Opts.Recursive {
fDir := strings.Split(filepath.Dir(fileName), string(os.PathSeparator))[1:]
err := os.MkdirAll(filepath.Join(c.Opts.OutDir, filepath.Join(fDir...)), 0755)
if err != nil {
outDir := c.recursiveDir(fileName)
if err := os.MkdirAll(outDir, 0755); err != nil {
return fmt.Errorf("archiveSaveZip: %w", err)
}
zipName = filepath.Join(c.Opts.OutDir, filepath.Join(fDir...), fmt.Sprintf("%s%s.cbz", baseNoExt(fileName), c.Opts.Suffix))
zipName = filepath.Join(outDir, fmt.Sprintf("%s%s.cbz", baseNoExt(fileName), c.Opts.Suffix))
} else {
zipName = filepath.Join(c.Opts.OutDir, fmt.Sprintf("%s%s.cbz", baseNoExt(fileName), c.Opts.Suffix))
}
@@ -108,13 +107,12 @@ func (c *Converter) archiveSaveTar(fileName string) error {
var tarName string
if c.Opts.Recursive {
fDir := strings.Split(filepath.Dir(fileName), string(os.PathSeparator))[1:]
err := os.MkdirAll(filepath.Join(c.Opts.OutDir, filepath.Join(fDir...)), 0755)
if err != nil {
outDir := c.recursiveDir(fileName)
if err := os.MkdirAll(outDir, 0755); err != nil {
return fmt.Errorf("archiveSaveTar: %w", err)
}
tarName = filepath.Join(c.Opts.OutDir, filepath.Join(fDir...), fmt.Sprintf("%s%s.cbt", baseNoExt(fileName), c.Opts.Suffix))
tarName = filepath.Join(outDir, fmt.Sprintf("%s%s.cbt", baseNoExt(fileName), c.Opts.Suffix))
} else {
tarName = filepath.Join(c.Opts.OutDir, fmt.Sprintf("%s%s.cbt", baseNoExt(fileName), c.Opts.Suffix))
}
+56 -3
View File
@@ -29,7 +29,7 @@ func TestConvert(t *testing.T) {
for _, file := range files {
conv.Opts.Suffix = fmt.Sprintf("_%s%s", format, filepath.Ext(file.Path))
err = conv.Convert(file.Path, file.Stat)
err = conv.Convert(file)
if err != nil {
t.Errorf("format %s: file %s: %v", format, file.Name, err)
}
@@ -59,7 +59,7 @@ func TestCover(t *testing.T) {
}
for _, file := range files {
err = conv.Cover(file.Path, file.Stat)
err = conv.Cover(file)
if err != nil {
t.Error(err)
}
@@ -88,7 +88,7 @@ func TestThumbnail(t *testing.T) {
}
for _, file := range files {
err = conv.Thumbnail(file.Path, file.Stat)
err = conv.Thumbnail(file)
if err != nil {
t.Error(err)
}
@@ -99,3 +99,56 @@ func TestThumbnail(t *testing.T) {
t.Error(err)
}
}
func TestRecursive(t *testing.T) {
inDir, err := os.MkdirTemp(os.TempDir(), "cbc-in")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(inDir)
sub := filepath.Join(inDir, "chapter1")
if err := os.MkdirAll(sub, 0755); err != nil {
t.Fatal(err)
}
src, err := os.ReadFile("testdata/test.cbz")
if err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(sub, "test.cbz"), src, 0644); err != nil {
t.Fatal(err)
}
outDir, err := os.MkdirTemp(os.TempDir(), "cbc-out")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(outDir)
opts := NewOptions()
opts.OutDir = outDir
opts.Recursive = true
conv := New(opts)
files, err := conv.Files([]string{inDir})
if err != nil {
t.Fatal(err)
}
if len(files) != 1 {
t.Fatalf("expected 1 file, got %d", len(files))
}
for _, file := range files {
if err := conv.Convert(file); err != nil {
t.Error(err)
}
}
// output must mirror the input subtree relative to the input root, not the absolute path
want := filepath.Join(outDir, "chapter1", "test.cbz")
if _, err := os.Stat(want); err != nil {
t.Errorf("expected output relative to input root at %s: %v", want, err)
}
}
+3 -3
View File
@@ -1024,7 +1024,7 @@ func onThumbnail(ih iup.Ihandle) int {
break
}
if err := c.Thumbnail(file.Path, file.Stat); err != nil {
if err := c.Thumbnail(file); err != nil {
iup.PostMessage(iup.GetHandle("dlg"), err.Error(), 0, 0)
fmt.Println(err)
@@ -1067,7 +1067,7 @@ func onCover(ih iup.Ihandle) int {
break
}
if err := c.Cover(file.Path, file.Stat); err != nil {
if err := c.Cover(file); err != nil {
iup.PostMessage(iup.GetHandle("dlg"), err.Error(), 0, 0)
fmt.Println(err)
@@ -1131,7 +1131,7 @@ func onConvert(ih iup.Ihandle) int {
}
} else {
for _, file := range files {
if err := c.Convert(file.Path, file.Stat); err != nil {
if err := c.Convert(file); err != nil {
convertErr(err)
if errors.Is(err, context.Canceled) {
break
+25 -12
View File
@@ -68,9 +68,9 @@ func main() {
if _, err := os.Stat(opts.OutDir); err != nil {
if err := os.MkdirAll(opts.OutDir, 0775); err != nil {
fmt.Println(err)
}
os.Exit(1)
}
}
files, err := conv.Files(args)
if err != nil {
@@ -142,14 +142,14 @@ func main() {
continue
case opts.Cover:
if err := conv.Cover(file.Path, file.Stat); err != nil {
if err := conv.Cover(file); err != nil {
fmt.Println(err)
os.Exit(1)
}
continue
case opts.Thumbnail:
if err = conv.Thumbnail(file.Path, file.Stat); err != nil {
if err = conv.Thumbnail(file); err != nil {
fmt.Println(err)
os.Exit(1)
}
@@ -157,7 +157,7 @@ func main() {
continue
}
if err := conv.Convert(file.Path, file.Stat); err != nil {
if err := conv.Convert(file); err != nil {
fmt.Println(err)
os.Exit(1)
}
@@ -279,27 +279,27 @@ func parseFlags() (cbconvert.Options, []string) {
switch os.Args[1] {
case "convert":
_ = convert.Parse(os.Args[2:])
operands := parseArgs(convert, os.Args[2:])
if !pipe {
args = convert.Args()
args = operands
}
case "cover":
opts.Cover = true
_ = cover.Parse(os.Args[2:])
operands := parseArgs(cover, os.Args[2:])
if !pipe {
args = cover.Args()
args = operands
}
case "thumbnail":
opts.Thumbnail = true
_ = thumbnail.Parse(os.Args[2:])
operands := parseArgs(thumbnail, os.Args[2:])
if !pipe {
args = thumbnail.Args()
args = operands
}
case "meta":
opts.Meta = true
_ = meta.Parse(os.Args[2:])
operands := parseArgs(meta, os.Args[2:])
if !pipe {
args = meta.Args()
args = operands
}
case "version":
opts.Version = true
@@ -314,6 +314,19 @@ func parseFlags() (cbconvert.Options, []string) {
return opts, args
}
// parseArgs parses flags interspersed with file/dir operands.
func parseArgs(fs *flag.FlagSet, args []string) []string {
var operands []string
_ = fs.Parse(args)
for fs.NArg() > 0 {
operands = append(operands, fs.Arg(0))
_ = fs.Parse(fs.Args()[1:])
}
return operands
}
// piped checks if we have piped stdin.
func piped() bool {
f, err := os.Stdin.Stat()