mirror of
https://github.com/Belphemur/CBZOptimizer.git
synced 2026-07-22 03:15:41 +02:00
feat(converter): add --keep-filenames flag to preserve original page filenames
Add a new `--keep-filenames` boolean flag (no shorthand, default false) on both `optimize` and `watch` commands. When enabled, page entries in the output CBZ preserve their original base filenames instead of being renumbered to `%04d` sequential indices. Extension is swapped on format conversion (e.g. `001.png` → `001.webp`), split pages get `-NN` suffixes on the original stem, and duplicate base names fall back to `<stem>_<index>.<ext>`. Watch mode supports the flag via the `keep-filenames` viper config key. Design: data-driven `OriginalName` field on `manga.PageFile`, set at extraction in `cbz_loader.ExtractChapter`. The `Converter` interface is unchanged. Flag off → behavior identical to before. Closes #216
This commit is contained in:
@@ -5,6 +5,8 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/belphemur/CBZOptimizer/v2/internal/manga"
|
||||
@@ -12,6 +14,49 @@ import (
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// resolvePageName picks the final archive entry name for a page.
|
||||
//
|
||||
// Order of resolution:
|
||||
// 1. When page.OriginalName is set (keep-filenames mode), use its stem with
|
||||
// the current page.Extension. Split pages append the -NN suffix so all
|
||||
// parts of the same source still land in the archive.
|
||||
// 2. If that final name has already been used in this archive, fall back to
|
||||
// the indexed form (stem + "_%04d" + extension) so the zip stays valid.
|
||||
// 3. Otherwise (OriginalName is empty), use the historical %04d / %04d-NN
|
||||
// sequential naming.
|
||||
//
|
||||
// usedNames tracks every name already chosen for this archive and is mutated
|
||||
// in place so the caller can keep a single map across the whole chapter.
|
||||
func resolvePageName(page *manga.PageFile, usedNames map[string]struct{}) string {
|
||||
if page.OriginalName != "" {
|
||||
stem := strings.TrimSuffix(page.OriginalName, filepath.Ext(page.OriginalName))
|
||||
var candidate string
|
||||
if page.IsSplitted {
|
||||
candidate = fmt.Sprintf("%s-%02d%s", stem, page.SplitPartIndex, page.Extension)
|
||||
} else {
|
||||
candidate = stem + page.Extension
|
||||
}
|
||||
if _, taken := usedNames[candidate]; !taken {
|
||||
usedNames[candidate] = struct{}{}
|
||||
return candidate
|
||||
}
|
||||
// Collision: fall back to the indexed form so the entry still gets
|
||||
// written, but make it visibly distinct from the preserved one.
|
||||
fallback := fmt.Sprintf("%s_%04d%s", stem, page.Index, page.Extension)
|
||||
usedNames[fallback] = struct{}{}
|
||||
return fallback
|
||||
}
|
||||
|
||||
if page.IsSplitted {
|
||||
name := fmt.Sprintf("%04d-%02d%s", page.Index, page.SplitPartIndex, page.Extension)
|
||||
usedNames[name] = struct{}{}
|
||||
return name
|
||||
}
|
||||
name := fmt.Sprintf("%04d%s", page.Index, page.Extension)
|
||||
usedNames[name] = struct{}{}
|
||||
return name
|
||||
}
|
||||
|
||||
// WriteChapterToCBZ creates a CBZ file from a Chapter by streaming page files
|
||||
// from disk directly into the zip archive. No image data is held in memory.
|
||||
func WriteChapterToCBZ(chapter *manga.Chapter, outputFilePath string) (err error) {
|
||||
@@ -34,14 +79,14 @@ func WriteChapterToCBZ(chapter *manga.Chapter, outputFilePath string) (err error
|
||||
zipWriter := zip.NewWriter(zipFile)
|
||||
defer errs.Capture(&err, zipWriter.Close, "failed to close .cbz writer")
|
||||
|
||||
// Write each page to the archive by streaming from disk
|
||||
// Write each page to the archive by streaming from disk.
|
||||
// Final name resolution: when a page carries an OriginalName (recorded by
|
||||
// ExtractChapter when --keep-filenames is on), preserve its stem and only
|
||||
// swap the extension to the current page.Extension. Duplicates in the
|
||||
// archive fall back to the indexed naming so the output stays a valid zip.
|
||||
usedNames := make(map[string]struct{}, len(chapter.Pages))
|
||||
for _, page := range chapter.Pages {
|
||||
var fileName string
|
||||
if page.IsSplitted {
|
||||
fileName = fmt.Sprintf("%04d-%02d%s", page.Index, page.SplitPartIndex, page.Extension)
|
||||
} else {
|
||||
fileName = fmt.Sprintf("%04d%s", page.Index, page.Extension)
|
||||
}
|
||||
fileName := resolvePageName(page, usedNames)
|
||||
|
||||
log.Debug().
|
||||
Str("output_path", outputFilePath).
|
||||
|
||||
Reference in New Issue
Block a user