mirror of
https://github.com/celogeek/go-comic-converter.git
synced 2025-05-25 08:12:36 +02:00
simplify converter struct
This commit is contained in:
parent
6c92597d8c
commit
1250deb53f
@ -14,17 +14,14 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/celogeek/go-comic-converter/v2/internal/converter/options"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Converter struct {
|
type converter struct {
|
||||||
Options *options.Options
|
Options *converterOptions
|
||||||
Cmd *flag.FlagSet
|
Cmd *flag.FlagSet
|
||||||
|
|
||||||
order []converterOrder
|
order []converterOrder
|
||||||
@ -33,10 +30,10 @@ type Converter struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a new parser
|
// Create a new parser
|
||||||
func New() *Converter {
|
func New() *converter {
|
||||||
options := options.New()
|
options := newOptions()
|
||||||
cmd := flag.NewFlagSet("go-comic-converter", flag.ExitOnError)
|
cmd := flag.NewFlagSet("go-comic-converter", flag.ExitOnError)
|
||||||
conv := &Converter{
|
conv := &converter{
|
||||||
Options: options,
|
Options: options,
|
||||||
Cmd: cmd,
|
Cmd: cmd,
|
||||||
order: make([]converterOrder, 0),
|
order: make([]converterOrder, 0),
|
||||||
@ -64,169 +61,76 @@ func New() *Converter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load default options (config + default)
|
// Load default options (config + default)
|
||||||
func (c *Converter) LoadConfig() error {
|
func (c *converter) LoadConfig() error {
|
||||||
return c.Options.LoadConfig()
|
return c.Options.LoadConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new section of config
|
|
||||||
func (c *Converter) AddSection(section string) {
|
|
||||||
c.order = append(c.order, converterOrderSection{value: section})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a string parameter
|
|
||||||
func (c *Converter) AddStringParam(p *string, name string, value string, usage string) {
|
|
||||||
c.Cmd.StringVar(p, name, value, usage)
|
|
||||||
c.order = append(c.order, converterOrderName{value: name, isString: true})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add an integer parameter
|
|
||||||
func (c *Converter) AddIntParam(p *int, name string, value int, usage string) {
|
|
||||||
c.Cmd.IntVar(p, name, value, usage)
|
|
||||||
c.order = append(c.order, converterOrderName{value: name})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add an float parameter
|
|
||||||
func (c *Converter) AddFloatParam(p *float64, name string, value float64, usage string) {
|
|
||||||
c.Cmd.Float64Var(p, name, value, usage)
|
|
||||||
c.order = append(c.order, converterOrderName{value: name})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a boolean parameter
|
|
||||||
func (c *Converter) AddBoolParam(p *bool, name string, value bool, usage string) {
|
|
||||||
c.Cmd.BoolVar(p, name, value, usage)
|
|
||||||
c.order = append(c.order, converterOrderName{value: name})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the parser with all section and parameter.
|
// Initialize the parser with all section and parameter.
|
||||||
func (c *Converter) InitParse() {
|
func (c *converter) InitParse() {
|
||||||
c.AddSection("Output")
|
c.addSection("Output")
|
||||||
c.AddStringParam(&c.Options.Input, "input", "", "Source of comic to convert: directory, cbz, zip, cbr, rar, pdf")
|
c.addStringParam(&c.Options.Input, "input", "", "Source of comic to convert: directory, cbz, zip, cbr, rar, pdf")
|
||||||
c.AddStringParam(&c.Options.Output, "output", "", "Output of the EPUB (directory or EPUB): (default [INPUT].epub)")
|
c.addStringParam(&c.Options.Output, "output", "", "Output of the EPUB (directory or EPUB): (default [INPUT].epub)")
|
||||||
c.AddStringParam(&c.Options.Author, "author", "GO Comic Converter", "Author of the EPUB")
|
c.addStringParam(&c.Options.Author, "author", "GO Comic Converter", "Author of the EPUB")
|
||||||
c.AddStringParam(&c.Options.Title, "title", "", "Title of the EPUB")
|
c.addStringParam(&c.Options.Title, "title", "", "Title of the EPUB")
|
||||||
|
|
||||||
c.AddSection("Config")
|
c.addSection("Config")
|
||||||
c.AddStringParam(&c.Options.Profile, "profile", c.Options.Profile, fmt.Sprintf("Profile to use: \n%s", c.Options.AvailableProfiles()))
|
c.addStringParam(&c.Options.Profile, "profile", c.Options.Profile, fmt.Sprintf("Profile to use: \n%s", c.Options.AvailableProfiles()))
|
||||||
c.AddIntParam(&c.Options.Quality, "quality", c.Options.Quality, "Quality of the image")
|
c.addIntParam(&c.Options.Quality, "quality", c.Options.Quality, "Quality of the image")
|
||||||
c.AddBoolParam(&c.Options.Grayscale, "grayscale", c.Options.Grayscale, "Grayscale image. Ideal for eInk devices.")
|
c.addBoolParam(&c.Options.Grayscale, "grayscale", c.Options.Grayscale, "Grayscale image. Ideal for eInk devices.")
|
||||||
c.AddIntParam(&c.Options.GrayscaleMode, "grayscale-mode", c.Options.GrayscaleMode, "Grayscale Mode\n0 = normal\n1 = average\n2 = luminance")
|
c.addIntParam(&c.Options.GrayscaleMode, "grayscale-mode", c.Options.GrayscaleMode, "Grayscale Mode\n0 = normal\n1 = average\n2 = luminance")
|
||||||
c.AddBoolParam(&c.Options.Crop, "crop", c.Options.Crop, "Crop images")
|
c.addBoolParam(&c.Options.Crop, "crop", c.Options.Crop, "Crop images")
|
||||||
c.AddIntParam(&c.Options.CropRatioLeft, "crop-ratio-left", c.Options.CropRatioLeft, "Crop ratio left: ratio of pixels allow to be non blank while cutting on the left.")
|
c.addIntParam(&c.Options.CropRatioLeft, "crop-ratio-left", c.Options.CropRatioLeft, "Crop ratio left: ratio of pixels allow to be non blank while cutting on the left.")
|
||||||
c.AddIntParam(&c.Options.CropRatioUp, "crop-ratio-up", c.Options.CropRatioUp, "Crop ratio up: ratio of pixels allow to be non blank while cutting on the top.")
|
c.addIntParam(&c.Options.CropRatioUp, "crop-ratio-up", c.Options.CropRatioUp, "Crop ratio up: ratio of pixels allow to be non blank while cutting on the top.")
|
||||||
c.AddIntParam(&c.Options.CropRatioRight, "crop-ratio-right", c.Options.CropRatioRight, "Crop ratio right: ratio of pixels allow to be non blank while cutting on the right.")
|
c.addIntParam(&c.Options.CropRatioRight, "crop-ratio-right", c.Options.CropRatioRight, "Crop ratio right: ratio of pixels allow to be non blank while cutting on the right.")
|
||||||
c.AddIntParam(&c.Options.CropRatioBottom, "crop-ratio-bottom", c.Options.CropRatioBottom, "Crop ratio bottom: ratio of pixels allow to be non blank while cutting on the bottom.")
|
c.addIntParam(&c.Options.CropRatioBottom, "crop-ratio-bottom", c.Options.CropRatioBottom, "Crop ratio bottom: ratio of pixels allow to be non blank while cutting on the bottom.")
|
||||||
c.AddIntParam(&c.Options.Brightness, "brightness", c.Options.Brightness, "Brightness readjustement: between -100 and 100, > 0 lighter, < 0 darker")
|
c.addIntParam(&c.Options.Brightness, "brightness", c.Options.Brightness, "Brightness readjustement: between -100 and 100, > 0 lighter, < 0 darker")
|
||||||
c.AddIntParam(&c.Options.Contrast, "contrast", c.Options.Contrast, "Contrast readjustement: between -100 and 100, > 0 more contrast, < 0 less contrast")
|
c.addIntParam(&c.Options.Contrast, "contrast", c.Options.Contrast, "Contrast readjustement: between -100 and 100, > 0 more contrast, < 0 less contrast")
|
||||||
c.AddBoolParam(&c.Options.AutoContrast, "autocontrast", c.Options.AutoContrast, "Improve contrast automatically")
|
c.addBoolParam(&c.Options.AutoContrast, "autocontrast", c.Options.AutoContrast, "Improve contrast automatically")
|
||||||
c.AddBoolParam(&c.Options.AutoRotate, "autorotate", c.Options.AutoRotate, "Auto Rotate page when width > height")
|
c.addBoolParam(&c.Options.AutoRotate, "autorotate", c.Options.AutoRotate, "Auto Rotate page when width > height")
|
||||||
c.AddBoolParam(&c.Options.AutoSplitDoublePage, "autosplitdoublepage", c.Options.AutoSplitDoublePage, "Auto Split double page when width > height")
|
c.addBoolParam(&c.Options.AutoSplitDoublePage, "autosplitdoublepage", c.Options.AutoSplitDoublePage, "Auto Split double page when width > height")
|
||||||
c.AddBoolParam(&c.Options.KeepDoublePageIfSplitted, "keepdoublepageifsplitted", c.Options.KeepDoublePageIfSplitted, "Keep the double page if splitted")
|
c.addBoolParam(&c.Options.KeepDoublePageIfSplitted, "keepdoublepageifsplitted", c.Options.KeepDoublePageIfSplitted, "Keep the double page if splitted")
|
||||||
c.AddBoolParam(&c.Options.NoBlankImage, "noblankimage", c.Options.NoBlankImage, "Remove blank image")
|
c.addBoolParam(&c.Options.NoBlankImage, "noblankimage", c.Options.NoBlankImage, "Remove blank image")
|
||||||
c.AddBoolParam(&c.Options.Manga, "manga", c.Options.Manga, "Manga mode (right to left)")
|
c.addBoolParam(&c.Options.Manga, "manga", c.Options.Manga, "Manga mode (right to left)")
|
||||||
c.AddBoolParam(&c.Options.HasCover, "hascover", c.Options.HasCover, "Has cover. Indicate if your comic have a cover. The first page will be used as a cover and include after the title.")
|
c.addBoolParam(&c.Options.HasCover, "hascover", c.Options.HasCover, "Has cover. Indicate if your comic have a cover. The first page will be used as a cover and include after the title.")
|
||||||
c.AddIntParam(&c.Options.LimitMb, "limitmb", c.Options.LimitMb, "Limit size of the EPUB: Default nolimit (0), Minimum 20")
|
c.addIntParam(&c.Options.LimitMb, "limitmb", c.Options.LimitMb, "Limit size of the EPUB: Default nolimit (0), Minimum 20")
|
||||||
c.AddBoolParam(&c.Options.StripFirstDirectoryFromToc, "strip", c.Options.StripFirstDirectoryFromToc, "Strip first directory from the TOC if only 1")
|
c.addBoolParam(&c.Options.StripFirstDirectoryFromToc, "strip", c.Options.StripFirstDirectoryFromToc, "Strip first directory from the TOC if only 1")
|
||||||
c.AddIntParam(&c.Options.SortPathMode, "sort", c.Options.SortPathMode, "Sort path mode\n0 = alpha for path and file\n1 = alphanum for path and alpha for file\n2 = alphanum for path and file")
|
c.addIntParam(&c.Options.SortPathMode, "sort", c.Options.SortPathMode, "Sort path mode\n0 = alpha for path and file\n1 = alphanum for path and alpha for file\n2 = alphanum for path and file")
|
||||||
c.AddStringParam(&c.Options.ForegroundColor, "foreground-color", c.Options.ForegroundColor, "Foreground color in hexa format RGB. Black=000, White=FFF")
|
c.addStringParam(&c.Options.ForegroundColor, "foreground-color", c.Options.ForegroundColor, "Foreground color in hexa format RGB. Black=000, White=FFF")
|
||||||
c.AddStringParam(&c.Options.BackgroundColor, "background-color", c.Options.BackgroundColor, "Background color in hexa format RGB. Black=000, White=FFF, Light Gray=DDD, Dark Gray=777")
|
c.addStringParam(&c.Options.BackgroundColor, "background-color", c.Options.BackgroundColor, "Background color in hexa format RGB. Black=000, White=FFF, Light Gray=DDD, Dark Gray=777")
|
||||||
c.AddBoolParam(&c.Options.NoResize, "noresize", c.Options.NoResize, "Do not reduce image size if exceed device size")
|
c.addBoolParam(&c.Options.NoResize, "noresize", c.Options.NoResize, "Do not reduce image size if exceed device size")
|
||||||
c.AddStringParam(&c.Options.Format, "format", c.Options.Format, "Format of output images: jpeg (lossy), png (lossless)")
|
c.addStringParam(&c.Options.Format, "format", c.Options.Format, "Format of output images: jpeg (lossy), png (lossless)")
|
||||||
c.AddFloatParam(&c.Options.AspectRatio, "aspect-ratio", c.Options.AspectRatio, "Aspect ratio (height/width) of the output\n -1 = same as device\n 0 = same as source\n1.6 = amazon advice for kindle")
|
c.addFloatParam(&c.Options.AspectRatio, "aspect-ratio", c.Options.AspectRatio, "Aspect ratio (height/width) of the output\n -1 = same as device\n 0 = same as source\n1.6 = amazon advice for kindle")
|
||||||
c.AddBoolParam(&c.Options.PortraitOnly, "portrait-only", c.Options.PortraitOnly, "Portrait only: force orientation to portrait only.")
|
c.addBoolParam(&c.Options.PortraitOnly, "portrait-only", c.Options.PortraitOnly, "Portrait only: force orientation to portrait only.")
|
||||||
c.AddIntParam(&c.Options.TitlePage, "titlepage", c.Options.TitlePage, "Title page\n0 = never\n1 = always\n2 = only if epub is splitted")
|
c.addIntParam(&c.Options.TitlePage, "titlepage", c.Options.TitlePage, "Title page\n0 = never\n1 = always\n2 = only if epub is splitted")
|
||||||
|
|
||||||
c.AddSection("Default config")
|
c.addSection("Default config")
|
||||||
c.AddBoolParam(&c.Options.Show, "show", false, "Show your default parameters")
|
c.addBoolParam(&c.Options.Show, "show", false, "Show your default parameters")
|
||||||
c.AddBoolParam(&c.Options.Save, "save", false, "Save your parameters as default")
|
c.addBoolParam(&c.Options.Save, "save", false, "Save your parameters as default")
|
||||||
c.AddBoolParam(&c.Options.Reset, "reset", false, "Reset your parameters to default")
|
c.addBoolParam(&c.Options.Reset, "reset", false, "Reset your parameters to default")
|
||||||
|
|
||||||
c.AddSection("Shortcut")
|
c.addSection("Shortcut")
|
||||||
c.AddBoolParam(&c.Options.Auto, "auto", false, "Activate all automatic options")
|
c.addBoolParam(&c.Options.Auto, "auto", false, "Activate all automatic options")
|
||||||
c.AddBoolParam(&c.Options.NoFilter, "nofilter", false, "Deactivate all filters")
|
c.addBoolParam(&c.Options.NoFilter, "nofilter", false, "Deactivate all filters")
|
||||||
c.AddBoolParam(&c.Options.MaxQuality, "maxquality", false, "Max quality: color png + noresize")
|
c.addBoolParam(&c.Options.MaxQuality, "maxquality", false, "Max quality: color png + noresize")
|
||||||
c.AddBoolParam(&c.Options.BestQuality, "bestquality", false, "Max quality: color jpg q100 + noresize")
|
c.addBoolParam(&c.Options.BestQuality, "bestquality", false, "Max quality: color jpg q100 + noresize")
|
||||||
c.AddBoolParam(&c.Options.GreatQuality, "greatquality", false, "Max quality: grayscale jpg q90 + noresize")
|
c.addBoolParam(&c.Options.GreatQuality, "greatquality", false, "Max quality: grayscale jpg q90 + noresize")
|
||||||
c.AddBoolParam(&c.Options.GoodQuality, "goodquality", false, "Max quality: grayscale jpg q90")
|
c.addBoolParam(&c.Options.GoodQuality, "goodquality", false, "Max quality: grayscale jpg q90")
|
||||||
|
|
||||||
c.AddSection("Compatibility")
|
c.addSection("Compatibility")
|
||||||
c.AddBoolParam(&c.Options.AppleBookCompatibility, "applebookcompatibility", c.Options.AppleBookCompatibility, "Apple book compatibility")
|
c.addBoolParam(&c.Options.AppleBookCompatibility, "applebookcompatibility", c.Options.AppleBookCompatibility, "Apple book compatibility")
|
||||||
|
|
||||||
c.AddSection("Other")
|
c.addSection("Other")
|
||||||
c.AddIntParam(&c.Options.Workers, "workers", runtime.NumCPU(), "Number of workers")
|
c.addIntParam(&c.Options.Workers, "workers", runtime.NumCPU(), "Number of workers")
|
||||||
c.AddBoolParam(&c.Options.Dry, "dry", false, "Dry run to show all options")
|
c.addBoolParam(&c.Options.Dry, "dry", false, "Dry run to show all options")
|
||||||
c.AddBoolParam(&c.Options.DryVerbose, "dry-verbose", false, "Display also sorted files after the TOC")
|
c.addBoolParam(&c.Options.DryVerbose, "dry-verbose", false, "Display also sorted files after the TOC")
|
||||||
c.AddBoolParam(&c.Options.Quiet, "quiet", false, "Disable progress bar")
|
c.addBoolParam(&c.Options.Quiet, "quiet", false, "Disable progress bar")
|
||||||
c.AddBoolParam(&c.Options.Json, "json", false, "Output progression and information in Json format")
|
c.addBoolParam(&c.Options.Json, "json", false, "Output progression and information in Json format")
|
||||||
c.AddBoolParam(&c.Options.Version, "version", false, "Show current and available version")
|
c.addBoolParam(&c.Options.Version, "version", false, "Show current and available version")
|
||||||
c.AddBoolParam(&c.Options.Help, "help", false, "Show this help message")
|
c.addBoolParam(&c.Options.Help, "help", false, "Show this help message")
|
||||||
}
|
|
||||||
|
|
||||||
// Customize version of FlagSet.PrintDefaults
|
|
||||||
func (c *Converter) Usage(isString bool, f *flag.Flag) string {
|
|
||||||
var b strings.Builder
|
|
||||||
fmt.Fprintf(&b, " -%s", f.Name) // Two spaces before -; see next two comments.
|
|
||||||
name, usage := flag.UnquoteUsage(f)
|
|
||||||
if len(name) > 0 {
|
|
||||||
b.WriteString(" ")
|
|
||||||
b.WriteString(name)
|
|
||||||
}
|
|
||||||
// Print the default value only if it differs to the zero value
|
|
||||||
// for this flag type.
|
|
||||||
if isZero, err := c.isZeroValue(f, f.DefValue); err != nil {
|
|
||||||
c.isZeroValueErrs = append(c.isZeroValueErrs, err)
|
|
||||||
} else if !isZero {
|
|
||||||
if isString {
|
|
||||||
fmt.Fprintf(&b, " (default %q)", f.DefValue)
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(&b, " (default %v)", f.DefValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Boolean flags of one ASCII letter are so common we
|
|
||||||
// treat them specially, putting their usage on the same line.
|
|
||||||
if b.Len() <= 4 { // space, space, '-', 'x'.
|
|
||||||
b.WriteString("\t")
|
|
||||||
} else {
|
|
||||||
// Four spaces before the tab triggers good alignment
|
|
||||||
// for both 4- and 8-space tab stops.
|
|
||||||
b.WriteString("\n \t")
|
|
||||||
}
|
|
||||||
b.WriteString(strings.ReplaceAll(usage, "\n", "\n \t"))
|
|
||||||
|
|
||||||
return b.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Taken from flag package as it is private and needed for usage.
|
|
||||||
//
|
|
||||||
// isZeroValue determines whether the string represents the zero
|
|
||||||
// value for a flag.
|
|
||||||
func (c *Converter) isZeroValue(f *flag.Flag, value string) (ok bool, err error) {
|
|
||||||
// Build a zero value of the flag's Value type, and see if the
|
|
||||||
// result of calling its String method equals the value passed in.
|
|
||||||
// This works unless the Value type is itself an interface type.
|
|
||||||
typ := reflect.TypeOf(f.Value)
|
|
||||||
var z reflect.Value
|
|
||||||
if typ.Kind() == reflect.Pointer {
|
|
||||||
z = reflect.New(typ.Elem())
|
|
||||||
} else {
|
|
||||||
z = reflect.Zero(typ)
|
|
||||||
}
|
|
||||||
// Catch panics calling the String method, which shouldn't prevent the
|
|
||||||
// usage message from being printed, but that we should report to the
|
|
||||||
// user so that they know to fix their code.
|
|
||||||
defer func() {
|
|
||||||
if e := recover(); e != nil {
|
|
||||||
if typ.Kind() == reflect.Pointer {
|
|
||||||
typ = typ.Elem()
|
|
||||||
}
|
|
||||||
err = fmt.Errorf("panic calling String method on zero %v for flag %s: %v", typ, f.Name, e)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return value == z.Interface().(flag.Value).String(), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse all parameters
|
// Parse all parameters
|
||||||
func (c *Converter) Parse() {
|
func (c *converter) Parse() {
|
||||||
c.Cmd.Parse(os.Args[1:])
|
c.Cmd.Parse(os.Args[1:])
|
||||||
if c.Options.Help {
|
if c.Options.Help {
|
||||||
c.Cmd.Usage()
|
c.Cmd.Usage()
|
||||||
@ -277,7 +181,7 @@ func (c *Converter) Parse() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check parameters
|
// Check parameters
|
||||||
func (c *Converter) Validate() error {
|
func (c *converter) Validate() error {
|
||||||
// Check input
|
// Check input
|
||||||
if c.Options.Input == "" {
|
if c.Options.Input == "" {
|
||||||
return errors.New("missing input")
|
return errors.New("missing input")
|
||||||
@ -393,14 +297,49 @@ func (c *Converter) Validate() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Customize version of FlagSet.PrintDefaults
|
||||||
|
func (c *converter) Usage(isString bool, f *flag.Flag) string {
|
||||||
|
var b strings.Builder
|
||||||
|
fmt.Fprintf(&b, " -%s", f.Name) // Two spaces before -; see next two comments.
|
||||||
|
name, usage := flag.UnquoteUsage(f)
|
||||||
|
if len(name) > 0 {
|
||||||
|
b.WriteString(" ")
|
||||||
|
b.WriteString(name)
|
||||||
|
}
|
||||||
|
// Print the default value only if it differs to the zero value
|
||||||
|
// for this flag type.
|
||||||
|
if isZero, err := c.isZeroValue(f, f.DefValue); err != nil {
|
||||||
|
c.isZeroValueErrs = append(c.isZeroValueErrs, err)
|
||||||
|
} else if !isZero {
|
||||||
|
if isString {
|
||||||
|
fmt.Fprintf(&b, " (default %q)", f.DefValue)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(&b, " (default %v)", f.DefValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Boolean flags of one ASCII letter are so common we
|
||||||
|
// treat them specially, putting their usage on the same line.
|
||||||
|
if b.Len() <= 4 { // space, space, '-', 'x'.
|
||||||
|
b.WriteString("\t")
|
||||||
|
} else {
|
||||||
|
// Four spaces before the tab triggers good alignment
|
||||||
|
// for both 4- and 8-space tab stops.
|
||||||
|
b.WriteString("\n \t")
|
||||||
|
}
|
||||||
|
b.WriteString(strings.ReplaceAll(usage, "\n", "\n \t"))
|
||||||
|
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
// Helper to show usage, err and exit 1
|
// Helper to show usage, err and exit 1
|
||||||
func (c *Converter) Fatal(err error) {
|
func (c *converter) Fatal(err error) {
|
||||||
c.Cmd.Usage()
|
c.Cmd.Usage()
|
||||||
fmt.Fprintf(os.Stderr, "\nError: %s\n", err)
|
fmt.Fprintf(os.Stderr, "\nError: %s\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Converter) Stats() {
|
func (c *converter) Stats() {
|
||||||
// Display elapse time and memory usage
|
// Display elapse time and memory usage
|
||||||
elapse := time.Since(c.startAt).Round(time.Millisecond)
|
elapse := time.Since(c.startAt).Round(time.Millisecond)
|
||||||
var mem runtime.MemStats
|
var mem runtime.MemStats
|
||||||
|
65
internal/converter/converter_helper.go
Normal file
65
internal/converter/converter_helper.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package converter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create a new section of config
|
||||||
|
func (c *converter) addSection(section string) {
|
||||||
|
c.order = append(c.order, converterOrderSection{value: section})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a string parameter
|
||||||
|
func (c *converter) addStringParam(p *string, name string, value string, usage string) {
|
||||||
|
c.Cmd.StringVar(p, name, value, usage)
|
||||||
|
c.order = append(c.order, converterOrderName{value: name, isString: true})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add an integer parameter
|
||||||
|
func (c *converter) addIntParam(p *int, name string, value int, usage string) {
|
||||||
|
c.Cmd.IntVar(p, name, value, usage)
|
||||||
|
c.order = append(c.order, converterOrderName{value: name})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add an float parameter
|
||||||
|
func (c *converter) addFloatParam(p *float64, name string, value float64, usage string) {
|
||||||
|
c.Cmd.Float64Var(p, name, value, usage)
|
||||||
|
c.order = append(c.order, converterOrderName{value: name})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a boolean parameter
|
||||||
|
func (c *converter) addBoolParam(p *bool, name string, value bool, usage string) {
|
||||||
|
c.Cmd.BoolVar(p, name, value, usage)
|
||||||
|
c.order = append(c.order, converterOrderName{value: name})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Taken from flag package as it is private and needed for usage.
|
||||||
|
//
|
||||||
|
// isZeroValue determines whether the string represents the zero
|
||||||
|
// value for a flag.
|
||||||
|
func (c *converter) isZeroValue(f *flag.Flag, value string) (ok bool, err error) {
|
||||||
|
// Build a zero value of the flag's Value type, and see if the
|
||||||
|
// result of calling its String method equals the value passed in.
|
||||||
|
// This works unless the Value type is itself an interface type.
|
||||||
|
typ := reflect.TypeOf(f.Value)
|
||||||
|
var z reflect.Value
|
||||||
|
if typ.Kind() == reflect.Pointer {
|
||||||
|
z = reflect.New(typ.Elem())
|
||||||
|
} else {
|
||||||
|
z = reflect.Zero(typ)
|
||||||
|
}
|
||||||
|
// Catch panics calling the String method, which shouldn't prevent the
|
||||||
|
// usage message from being printed, but that we should report to the
|
||||||
|
// user so that they know to fix their code.
|
||||||
|
defer func() {
|
||||||
|
if e := recover(); e != nil {
|
||||||
|
if typ.Kind() == reflect.Pointer {
|
||||||
|
typ = typ.Elem()
|
||||||
|
}
|
||||||
|
err = fmt.Errorf("panic calling String method on zero %v for flag %s: %v", typ, f.Name, e)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return value == z.Interface().(flag.Value).String(), nil
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
Manage options with default value from config.
|
Manage options with default value from config.
|
||||||
*/
|
*/
|
||||||
package options
|
package converter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@ -10,11 +10,10 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/celogeek/go-comic-converter/v2/internal/converter/profiles"
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Options struct {
|
type converterOptions struct {
|
||||||
// Output
|
// Output
|
||||||
Input string `yaml:"-"`
|
Input string `yaml:"-"`
|
||||||
Output string `yaml:"-"`
|
Output string `yaml:"-"`
|
||||||
@ -75,12 +74,12 @@ type Options struct {
|
|||||||
Help bool `yaml:"-"`
|
Help bool `yaml:"-"`
|
||||||
|
|
||||||
// Internal
|
// Internal
|
||||||
profiles profiles.Profiles
|
profiles converterProfiles
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize default options.
|
// Initialize default options.
|
||||||
func New() *Options {
|
func newOptions() *converterOptions {
|
||||||
return &Options{
|
return &converterOptions{
|
||||||
Profile: "SR",
|
Profile: "SR",
|
||||||
Quality: 85,
|
Quality: 85,
|
||||||
Grayscale: true,
|
Grayscale: true,
|
||||||
@ -97,15 +96,15 @@ func New() *Options {
|
|||||||
BackgroundColor: "FFF",
|
BackgroundColor: "FFF",
|
||||||
Format: "jpeg",
|
Format: "jpeg",
|
||||||
TitlePage: 1,
|
TitlePage: 1,
|
||||||
profiles: profiles.New(),
|
profiles: newProfiles(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Options) Header() string {
|
func (o *converterOptions) Header() string {
|
||||||
return "Go Comic Converter\n\nOptions:"
|
return "Go Comic Converter\n\nOptions:"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Options) String() string {
|
func (o *converterOptions) String() string {
|
||||||
var b strings.Builder
|
var b strings.Builder
|
||||||
b.WriteString(o.Header())
|
b.WriteString(o.Header())
|
||||||
for _, v := range []struct {
|
for _, v := range []struct {
|
||||||
@ -125,7 +124,7 @@ func (o *Options) String() string {
|
|||||||
return b.String()
|
return b.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Options) MarshalJSON() ([]byte, error) {
|
func (o *converterOptions) MarshalJSON() ([]byte, error) {
|
||||||
out := map[string]any{
|
out := map[string]any{
|
||||||
"input": o.Input,
|
"input": o.Input,
|
||||||
"output": o.Output,
|
"output": o.Output,
|
||||||
@ -186,13 +185,13 @@ func (o *Options) MarshalJSON() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Config file: ~/.go-comic-converter.yaml
|
// Config file: ~/.go-comic-converter.yaml
|
||||||
func (o *Options) FileName() string {
|
func (o *converterOptions) FileName() string {
|
||||||
home, _ := os.UserHomeDir()
|
home, _ := os.UserHomeDir()
|
||||||
return filepath.Join(home, ".go-comic-converter.yaml")
|
return filepath.Join(home, ".go-comic-converter.yaml")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load config files
|
// Load config files
|
||||||
func (o *Options) LoadConfig() error {
|
func (o *converterOptions) LoadConfig() error {
|
||||||
f, err := os.Open(o.FileName())
|
f, err := os.Open(o.FileName())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
@ -207,7 +206,7 @@ func (o *Options) LoadConfig() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get current settings for fields that can be saved
|
// Get current settings for fields that can be saved
|
||||||
func (o *Options) ShowConfig() string {
|
func (o *converterOptions) ShowConfig() string {
|
||||||
var profileDesc string
|
var profileDesc string
|
||||||
profile := o.GetProfile()
|
profile := o.GetProfile()
|
||||||
if profile != nil {
|
if profile != nil {
|
||||||
@ -296,13 +295,13 @@ func (o *Options) ShowConfig() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// reset all settings to default value
|
// reset all settings to default value
|
||||||
func (o *Options) ResetConfig() error {
|
func (o *converterOptions) ResetConfig() error {
|
||||||
New().SaveConfig()
|
newOptions().SaveConfig()
|
||||||
return o.LoadConfig()
|
return o.LoadConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
// save all current settings as futur default value
|
// save all current settings as futur default value
|
||||||
func (o *Options) SaveConfig() error {
|
func (o *converterOptions) SaveConfig() error {
|
||||||
f, err := os.Create(o.FileName())
|
f, err := os.Create(o.FileName())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -312,11 +311,11 @@ func (o *Options) SaveConfig() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// shortcut to get current profile
|
// shortcut to get current profile
|
||||||
func (o *Options) GetProfile() *profiles.Profile {
|
func (o *converterOptions) GetProfile() *converterProfile {
|
||||||
return o.profiles.Get(o.Profile)
|
return o.profiles.Get(o.Profile)
|
||||||
}
|
}
|
||||||
|
|
||||||
// all available profiles
|
// all available profiles
|
||||||
func (o *Options) AvailableProfiles() string {
|
func (o *converterOptions) AvailableProfiles() string {
|
||||||
return o.profiles.String()
|
return o.profiles.String()
|
||||||
}
|
}
|
@ -1,25 +1,25 @@
|
|||||||
/*
|
/*
|
||||||
Manage supported profiles for go-comic-converter.
|
Manage supported profiles for go-comic-converter.
|
||||||
*/
|
*/
|
||||||
package profiles
|
package converter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Profile struct {
|
type converterProfile struct {
|
||||||
Code string `json:"code"`
|
Code string `json:"code"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Width int `json:"width"`
|
Width int `json:"width"`
|
||||||
Height int `json:"height"`
|
Height int `json:"height"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Profiles []Profile
|
type converterProfiles []*converterProfile
|
||||||
|
|
||||||
// Initialize list of all supported profiles.
|
// Initialize list of all supported profiles.
|
||||||
func New() Profiles {
|
func newProfiles() converterProfiles {
|
||||||
return []Profile{
|
return []*converterProfile{
|
||||||
// High Resolution for Tablette
|
// High Resolution for Tablette
|
||||||
{"HR", "High Resolution", 2400, 3840},
|
{"HR", "High Resolution", 2400, 3840},
|
||||||
{"SR", "Standard Resolution", 1200, 1920},
|
{"SR", "Standard Resolution", 1200, 1920},
|
||||||
@ -55,7 +55,7 @@ func New() Profiles {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Profiles) String() string {
|
func (p converterProfiles) String() string {
|
||||||
s := make([]string, 0)
|
s := make([]string, 0)
|
||||||
for _, v := range p {
|
for _, v := range p {
|
||||||
s = append(s, fmt.Sprintf(
|
s = append(s, fmt.Sprintf(
|
||||||
@ -69,10 +69,10 @@ func (p Profiles) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Lookup profile by code
|
// Lookup profile by code
|
||||||
func (p Profiles) Get(name string) *Profile {
|
func (p converterProfiles) Get(name string) *converterProfile {
|
||||||
for _, profile := range p {
|
for _, profile := range p {
|
||||||
if profile.Code == name {
|
if profile.Code == name {
|
||||||
return &profile
|
return profile
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
Loading…
x
Reference in New Issue
Block a user