revamp command line

This commit is contained in:
Celogeek 2023-04-02 19:43:30 +02:00
parent db4ba5ad5f
commit 49056e71d9
Signed by: celogeek
SSH Key Fingerprint: SHA256:njNJLzoLQdbV9PC6ehcruRb0QnEgxABoCYZ+0+aUIYc
4 changed files with 537 additions and 319 deletions

267
internal/converter/core.go Normal file
View File

@ -0,0 +1,267 @@
package converter
import (
"errors"
"flag"
"fmt"
"os"
"path/filepath"
"reflect"
"runtime"
"strings"
)
type converterOrder struct {
name string
is_string bool
section string
is_section bool
}
type Converter struct {
Options *Options
Cmd *flag.FlagSet
order []converterOrder
isZeroValueErrs []error
}
func New() *Converter {
options := NewOptions()
cmd := flag.NewFlagSet("go-comic-converter", flag.ExitOnError)
conv := &Converter{
Options: options,
Cmd: cmd,
order: make([]converterOrder, 0),
}
cmd.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", filepath.Base(os.Args[0]))
for _, o := range conv.order {
if o.is_section {
fmt.Fprintf(os.Stderr, "\n%s:\n", o.section)
} else {
fmt.Fprintln(os.Stderr, conv.Usage(o.is_string, cmd.Lookup(o.name)))
}
}
}
return conv
}
func (c *Converter) LoadConfig() error {
return c.Options.LoadDefault()
}
func (c *Converter) AddSection(section string) {
c.order = append(c.order, converterOrder{section: section, is_section: true})
}
func (c *Converter) AddStringParam(p *string, name string, value string, usage string) {
c.Cmd.StringVar(p, name, value, usage)
c.order = append(c.order, converterOrder{name: name, is_string: true})
}
func (c *Converter) AddIntParam(p *int, name string, value int, usage string) {
c.Cmd.IntVar(p, name, value, usage)
c.order = append(c.order, converterOrder{name: name})
}
func (c *Converter) AddBoolParam(p *bool, name string, value bool, usage string) {
c.Cmd.BoolVar(p, name, value, usage)
c.order = append(c.order, converterOrder{name: name})
}
func (c *Converter) InitParse() {
c.AddSection("Output")
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.Author, "author", "GO Comic Converter", "Author of the epub")
c.AddStringParam(&c.Options.Title, "title", "", "Title of the epub")
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.AddSection("Config")
c.AddStringParam(&c.Options.Profile, "profile", c.Options.Profile, fmt.Sprintf("Profile to use: \n%s", c.Options.profiles))
c.AddIntParam(&c.Options.Quality, "quality", c.Options.Quality, "Quality of the image")
c.AddBoolParam(&c.Options.Crop, "crop", c.Options.Crop, "Crop images")
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.AddBoolParam(&c.Options.AutoRotate, "autorotate", c.Options.AutoRotate, "Auto Rotate page when width > height")
c.AddBoolParam(&c.Options.Auto, "auto", false, "Activate all automatic options")
c.AddBoolParam(&c.Options.AutoSplitDoublePage, "autosplitdoublepage", c.Options.AutoSplitDoublePage, "Auto Split double page when width > height")
c.AddBoolParam(&c.Options.NoBlankPage, "noblankpage", c.Options.NoBlankPage, "Remove blank pages")
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.AddPanelView, "addpanelview", c.Options.AddPanelView, "Add an embeded panel view. On kindle you may not need this option as it is handled by the kindle.")
c.AddIntParam(&c.Options.LimitMb, "limitmb", c.Options.LimitMb, "Limit size of the ePub: Default nolimit (0), Minimum 20")
c.AddSection("Default config")
c.AddBoolParam(&c.Options.Show, "show", false, "Show your default parameters")
c.AddBoolParam(&c.Options.Save, "save", false, "Save your parameters as default")
c.AddSection("Other")
c.AddBoolParam(&c.Options.Help, "help", false, "Show this help message")
}
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()
}
// 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
}
func (c *Converter) Parse() {
c.Cmd.Parse(os.Args[1:])
if c.Options.Help {
c.Cmd.Usage()
os.Exit(0)
}
if c.Options.Auto {
c.Options.AutoRotate = true
c.Options.AutoSplitDoublePage = true
}
}
func (c *Converter) Validate() error {
// Check input
if c.Options.Input == "" {
return errors.New("missing input")
}
fi, err := os.Stat(c.Options.Input)
if err != nil {
return err
}
// Check Output
var defaultOutput string
inputBase := filepath.Clean(c.Options.Input)
if fi.IsDir() {
defaultOutput = fmt.Sprintf("%s.epub", inputBase)
} else {
ext := filepath.Ext(inputBase)
defaultOutput = fmt.Sprintf("%s.epub", inputBase[0:len(inputBase)-len(ext)])
}
if c.Options.Output == "" {
c.Options.Output = defaultOutput
}
c.Options.Output = filepath.Clean(c.Options.Output)
if filepath.Ext(c.Options.Output) == ".epub" {
fo, err := os.Stat(filepath.Dir(c.Options.Output))
if err != nil {
return err
}
if !fo.IsDir() {
return errors.New("parent of the output is not a directory")
}
} else {
fo, err := os.Stat(c.Options.Output)
if err != nil {
return err
}
if !fo.IsDir() {
return errors.New("output must be an existing dir or end with .epub")
}
c.Options.Output = filepath.Join(
c.Options.Output,
filepath.Base(defaultOutput),
)
}
// Title
if c.Options.Title == "" {
ext := filepath.Ext(defaultOutput)
c.Options.Title = filepath.Base(defaultOutput[0 : len(defaultOutput)-len(ext)])
}
// Profile
if c.Options.Profile == "" {
return errors.New("profile missing")
}
if _, ok := c.Options.profiles[c.Options.Profile]; !ok {
return fmt.Errorf("profile %q doesn't exists", c.Options.Profile)
}
// LimitMb
if c.Options.LimitMb < 20 && c.Options.LimitMb != 0 {
return errors.New("limitmb should be 0 or >= 20")
}
// Brightness
if c.Options.Brightness < -100 || c.Options.Brightness > 100 {
return errors.New("brightness should be between -100 and 100")
}
// Contrast
if c.Options.Contrast < -100 || c.Options.Contrast > 100 {
return errors.New("contrast should be between -100 and 100")
}
return nil
}
func (c Converter) Fatal(err error) {
c.Cmd.Usage()
fmt.Fprintf(os.Stderr, "\nError: %s\n", err)
os.Exit(1)
}

View File

@ -0,0 +1,160 @@
package converter
import (
"fmt"
"os"
"path/filepath"
"gopkg.in/yaml.v3"
)
type Options struct {
Input string `yaml:"-"`
Output string `yaml:"-"`
Author string `yaml:"-"`
Title string `yaml:"-"`
Auto bool `yaml:"-"`
Workers int `yaml:"-"`
Dry bool `yaml:"-"`
Show bool `yaml:"-"`
Save bool `yaml:"-"`
Help bool `yaml:"-"`
Profile string `yaml:"profile"`
Quality int `yaml:"quality"`
Crop bool `yaml:"crop"`
Brightness int `yaml:"brightness"`
Contrast int `yaml:"contrast"`
AutoRotate bool `yaml:"auto_rotate"`
AutoSplitDoublePage bool `yaml:"auto_split_double_page"`
NoBlankPage bool `yaml:"no_blank_page"`
Manga bool `yaml:"manga"`
HasCover bool `yaml:"has_cover"`
AddPanelView bool `yaml:"add_panel_view"`
LimitMb int `yaml:"limit_mb"`
profiles Profiles
}
func NewOptions() *Options {
return &Options{
Profile: "",
Quality: 85,
Crop: true,
Brightness: 0,
Contrast: 0,
AutoRotate: false,
AutoSplitDoublePage: false,
NoBlankPage: false,
Manga: false,
HasCover: true,
AddPanelView: false,
LimitMb: 0,
profiles: NewProfile(),
}
}
func (o *Options) Header() string {
return `Go Comic Converter
Options:`
}
func (o *Options) String() string {
return fmt.Sprintf(`%s
Input : %s
Output : %s
Author : %s
Title : %s
Workers : %d%s
`,
o.Header(),
o.Input,
o.Output,
o.Author,
o.Title,
o.Workers,
o.ShowDefault(),
)
}
func (o *Options) FileName() string {
home, _ := os.UserHomeDir()
return filepath.Join(home, ".go-comic-converter.yaml")
}
func (o *Options) LoadDefault() error {
f, err := os.Open(o.FileName())
if err != nil {
return nil
}
defer f.Close()
err = yaml.NewDecoder(f).Decode(o)
if err != nil && err.Error() != "EOF" {
return err
}
return nil
}
func (o *Options) GetProfile() *Profile {
if profile, ok := o.profiles[o.Profile]; ok {
return &profile
} else {
return nil
}
}
func (o *Options) ShowDefault() string {
var profileDesc string
profile := o.GetProfile()
if profile != nil {
profileDesc = fmt.Sprintf(
"%s - %s - %dx%d - %d levels of gray",
o.Profile,
profile.Description,
profile.Width,
profile.Height,
len(profile.Palette),
)
}
limitmb := "nolimit"
if o.LimitMb > 0 {
limitmb = fmt.Sprintf("%d Mb", o.LimitMb)
}
return fmt.Sprintf(`
Profile : %s
Quality : %d
Crop : %v
Brightness : %d
Contrast : %d
AutoRotate : %v
AutoSplitDoublePage: %v
NoBlankPage : %v
Manga : %v
HasCover : %v
AddPanelView : %v
LimitMb : %s`,
profileDesc,
o.Quality,
o.Crop,
o.Brightness,
o.Contrast,
o.AutoRotate,
o.AutoSplitDoublePage,
o.NoBlankPage,
o.Manga,
o.HasCover,
o.AddPanelView,
limitmb,
)
}
func (o *Options) SaveDefault() error {
f, err := os.Create(o.FileName())
if err != nil {
return err
}
defer f.Close()
return yaml.NewEncoder(f).Encode(o)
}

View File

@ -0,0 +1,69 @@
package converter
import (
"fmt"
"image/color"
"strings"
"github.com/celogeek/go-comic-converter/internal/epub"
)
type Profile struct {
Code string
Description string
Width int
Height int
Palette color.Palette
}
type Profiles map[string]Profile
var profiles = []Profile{
{"KS", "Kindle 1", 600, 670, epub.PALETTE_4},
{"K11", "Kindle 11", 1072, 1448, epub.PALETTE_16},
{"K2", "Kindle 2", 600, 670, epub.PALETTE_15},
{"K34", "Kindle Keyboard/Touch", 600, 800, epub.PALETTE_16},
{"K578", "Kindle", 600, 800, epub.PALETTE_16},
{"KDX", "Kindle DX/DXG", 824, 1000, epub.PALETTE_16},
{"KPW", "Kindle Paperwhite 1/2", 758, 1024, epub.PALETTE_16},
{"KV", "Kindle Paperwhite 3/4/Voyage/Oasis", 1072, 1448, epub.PALETTE_16},
{"KPW5", "Kindle Paperwhite 5/Signature Edition", 1236, 1648, epub.PALETTE_16},
{"KO", "Kindle Oasis 2/3", 1264, 1680, epub.PALETTE_16},
{"KS", "Kindle Scribe", 1860, 2480, epub.PALETTE_16},
// Kobo
{"KoMT", "Kobo Mini/Touch", 600, 800, epub.PALETTE_16},
{"KoG", "Kobo Glo", 768, 1024, epub.PALETTE_16},
{"KoGHD", "Kobo Glo HD", 1072, 1448, epub.PALETTE_16},
{"KoA", "Kobo Aura", 758, 1024, epub.PALETTE_16},
{"KoAHD", "Kobo Aura HD", 1080, 1440, epub.PALETTE_16},
{"KoAH2O", "Kobo Aura H2O", 1080, 1430, epub.PALETTE_16},
{"KoAO", "Kobo Aura ONE", 1404, 1872, epub.PALETTE_16},
{"KoN", "Kobo Nia", 758, 1024, epub.PALETTE_16},
{"KoC", "Kobo Clara HD/Kobo Clara 2E", 1072, 1448, epub.PALETTE_16},
{"KoL", "Kobo Libra H2O/Kobo Libra 2", 1264, 1680, epub.PALETTE_16},
{"KoF", "Kobo Forma", 1440, 1920, epub.PALETTE_16},
{"KoS", "Kobo Sage", 1440, 1920, epub.PALETTE_16},
{"KoE", "Kobo Elipsa", 1404, 1872, epub.PALETTE_16},
}
func NewProfile() Profiles {
r := Profiles{}
for _, profile := range profiles {
r[profile.Code] = profile
}
return r
}
func (p Profiles) String() string {
s := make([]string, 0)
for _, v := range profiles {
s = append(s, fmt.Sprintf(
" - %-7s ( %9s ) - %2d levels of gray - %s",
v.Code,
fmt.Sprintf("%dx%d", v.Width, v.Height),
len(v.Palette),
v.Description,
))
}
return strings.Join(s, "\n")
}

360
main.go
View File

@ -1,348 +1,70 @@
package main
import (
"flag"
"fmt"
"image/color"
"os"
"path/filepath"
"runtime"
"strings"
"github.com/celogeek/go-comic-converter/internal/converter"
"github.com/celogeek/go-comic-converter/internal/epub"
"gopkg.in/yaml.v3"
)
type Profile struct {
Code string
Description string
Width int
Height int
Palette color.Palette
}
var Profiles = []Profile{
// Kindle
{"K1", "Kindle 1", 600, 670, epub.PALETTE_4},
{"K11", "Kindle 11", 1072, 1448, epub.PALETTE_16},
{"K2", "Kindle 2", 600, 670, epub.PALETTE_15},
{"K34", "Kindle Keyboard/Touch", 600, 800, epub.PALETTE_16},
{"K578", "Kindle", 600, 800, epub.PALETTE_16},
{"KDX", "Kindle DX/DXG", 824, 1000, epub.PALETTE_16},
{"KPW", "Kindle Paperwhite 1/2", 758, 1024, epub.PALETTE_16},
{"KV", "Kindle Paperwhite 3/4/Voyage/Oasis", 1072, 1448, epub.PALETTE_16},
{"KPW5", "Kindle Paperwhite 5/Signature Edition", 1236, 1648, epub.PALETTE_16},
{"KO", "Kindle Oasis 2/3", 1264, 1680, epub.PALETTE_16},
{"KS", "Kindle Scribe", 1860, 2480, epub.PALETTE_16},
// Kobo
{"KoMT", "Kobo Mini/Touch", 600, 800, epub.PALETTE_16},
{"KoG", "Kobo Glo", 768, 1024, epub.PALETTE_16},
{"KoGHD", "Kobo Glo HD", 1072, 1448, epub.PALETTE_16},
{"KoA", "Kobo Aura", 758, 1024, epub.PALETTE_16},
{"KoAHD", "Kobo Aura HD", 1080, 1440, epub.PALETTE_16},
{"KoAH2O", "Kobo Aura H2O", 1080, 1430, epub.PALETTE_16},
{"KoAO", "Kobo Aura ONE", 1404, 1872, epub.PALETTE_16},
{"KoN", "Kobo Nia", 758, 1024, epub.PALETTE_16},
{"KoC", "Kobo Clara HD/Kobo Clara 2E", 1072, 1448, epub.PALETTE_16},
{"KoL", "Kobo Libra H2O/Kobo Libra 2", 1264, 1680, epub.PALETTE_16},
{"KoF", "Kobo Forma", 1440, 1920, epub.PALETTE_16},
{"KoS", "Kobo Sage", 1440, 1920, epub.PALETTE_16},
{"KoE", "Kobo Elipsa", 1404, 1872, epub.PALETTE_16},
}
var ProfilesIdx = map[string]int{}
func init() {
for i, p := range Profiles {
ProfilesIdx[p.Code] = i
}
}
var Home, _ = os.UserHomeDir()
var ConfigFile = filepath.Join(Home, ".go-comic-converter.yaml")
type Config struct {
Profile string `yaml:"profile"`
Quality int `yaml:"quality"`
Crop bool `yaml:"crop"`
Brightness int `yaml:"brightness"`
Contrast int `yaml:"contrast"`
AutoRotate bool `yaml:"auto_rotate"`
AutoSplitDoublePage bool `yaml:"auto_split_double_page"`
NoBlankPage bool `yaml:"no_blank_page"`
Manga bool `yaml:"manga"`
HasCover bool `yaml:"has_cover"`
AddPanelView bool `yaml:"add_panel_view"`
LimitMb int `yaml:"limit_mb"`
}
type Option struct {
Input string
Output string
Profile string
Author string
Title string
Quality int
Crop bool
Brightness int
Contrast int
Auto bool
AutoRotate bool
AutoSplitDoublePage bool
NoBlankPage bool
Manga bool
HasCover bool
AddPanelView bool
Workers int
LimitMb int
Save bool
}
func (o *Option) String() string {
var desc string
var width, height, level int
if i, ok := ProfilesIdx[o.Profile]; ok {
profile := Profiles[i]
desc = profile.Description
width = profile.Width
height = profile.Height
level = len(profile.Palette)
}
limitmb := "nolimit"
if o.LimitMb > 0 {
limitmb = fmt.Sprintf("%d Mb", o.LimitMb)
}
return fmt.Sprintf(`Go Comic Converter
Options:
Input : %s
Output : %s
Profile : %s - %s - %dx%d - %d levels of gray
Author : %s
Title : %s
Quality : %d
Crop : %v
Brightness : %d
Contrast : %d
AutoRotate : %v
AutoSplitDoublePage: %v
NoBlankPage : %v
Manga : %v
HasCover : %v
AddPanelView : %v
LimitMb : %s
Workers : %d
`,
o.Input,
o.Output,
o.Profile, desc, width, height, level,
o.Author,
o.Title,
o.Quality,
o.Crop,
o.Brightness,
o.Contrast,
o.AutoRotate,
o.AutoSplitDoublePage,
o.NoBlankPage,
o.Manga,
o.HasCover,
o.AddPanelView,
limitmb,
o.Workers,
)
}
func main() {
availableProfiles := make([]string, 0)
for _, p := range Profiles {
availableProfiles = append(availableProfiles, fmt.Sprintf(
" - %-7s ( %9s ) - %2d levels of gray - %s",
p.Code,
fmt.Sprintf("%dx%d", p.Width, p.Height),
len(p.Palette),
p.Description,
))
cmd := converter.New()
if err := cmd.LoadConfig(); err != nil {
cmd.Fatal(err)
}
cmd.InitParse()
cmd.Parse()
defaultOpt := &Config{
Profile: "",
Quality: 85,
Crop: true,
Brightness: 0,
Contrast: 0,
AutoRotate: false,
AutoSplitDoublePage: false,
NoBlankPage: false,
Manga: false,
HasCover: true,
AddPanelView: false,
LimitMb: 0,
}
configHandler, err := os.Open(ConfigFile)
if err == nil {
defer configHandler.Close()
err = yaml.NewDecoder(configHandler).Decode(defaultOpt)
if err != nil && err.Error() != "EOF" {
fmt.Fprintf(os.Stderr, "Error detected in your config %q\n", ConfigFile)
fmt.Fprint(os.Stderr, err)
os.Exit(1)
}
}
opt := &Option{}
flag.StringVar(&opt.Input, "input", "", "Source of comic to convert: directory, cbz, zip, cbr, rar, pdf")
flag.StringVar(&opt.Output, "output", "", "Output of the epub (directory or epub): (default [INPUT].epub)")
flag.StringVar(&opt.Profile, "profile", defaultOpt.Profile, fmt.Sprintf("Profile to use: \n%s\n", strings.Join(availableProfiles, "\n")))
flag.StringVar(&opt.Author, "author", "GO Comic Converter", "Author of the epub")
flag.StringVar(&opt.Title, "title", "", "Title of the epub")
flag.IntVar(&opt.Quality, "quality", defaultOpt.Quality, "Quality of the image")
flag.BoolVar(&opt.Crop, "crop", defaultOpt.Crop, "Crop images")
flag.IntVar(&opt.Brightness, "brightness", defaultOpt.Brightness, "Brightness readjustement: between -100 and 100, > 0 lighter, < 0 darker")
flag.IntVar(&opt.Contrast, "contrast", defaultOpt.Contrast, "Contrast readjustement: between -100 and 100, > 0 more contrast, < 0 less contrast")
flag.BoolVar(&opt.Auto, "auto", false, "Activate all automatic options")
flag.BoolVar(&opt.AutoRotate, "autorotate", defaultOpt.AutoRotate, "Auto Rotate page when width > height")
flag.BoolVar(&opt.AutoSplitDoublePage, "autosplitdoublepage", defaultOpt.AutoSplitDoublePage, "Auto Split double page when width > height")
flag.BoolVar(&opt.NoBlankPage, "noblankpage", defaultOpt.NoBlankPage, "Remove blank pages")
flag.BoolVar(&opt.Manga, "manga", defaultOpt.Manga, "Manga mode (right to left)")
flag.BoolVar(&opt.HasCover, "hascover", defaultOpt.HasCover, "Has cover. Indicate if your comic have a cover. The first page will be used as a cover and include after the title.")
flag.BoolVar(&opt.AddPanelView, "addpanelview", defaultOpt.AddPanelView, "Add an embeded panel view. On kindle you may not need this option as it is handled by the kindle.")
flag.IntVar(&opt.LimitMb, "limitmb", defaultOpt.LimitMb, "Limit size of the ePub: Default nolimit (0), Minimum 20")
flag.IntVar(&opt.Workers, "workers", runtime.NumCPU(), "Number of workers")
flag.BoolVar(&opt.Save, "save", false, "Save your parameters as default.")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", filepath.Base(os.Args[0]))
flag.PrintDefaults()
}
flag.Parse()
if opt.Auto {
opt.AutoRotate = true
opt.AutoSplitDoublePage = true
}
if opt.Save {
f, err := os.Create(ConfigFile)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
defer f.Close()
yaml.NewEncoder(f).Encode(&Config{
Profile: opt.Profile,
Quality: opt.Quality,
Crop: opt.Crop,
Brightness: opt.Brightness,
Contrast: opt.Contrast,
AutoRotate: opt.AutoRotate,
AutoSplitDoublePage: opt.AutoSplitDoublePage,
NoBlankPage: opt.NoBlankPage,
Manga: opt.Manga,
HasCover: opt.HasCover,
AddPanelView: opt.AddPanelView,
LimitMb: opt.LimitMb,
})
fmt.Fprintf(os.Stderr, "Default settings saved in %q\n", ConfigFile)
os.Exit(0)
}
if opt.Input == "" {
fmt.Fprintln(os.Stderr, "Missing input or output!")
flag.Usage()
os.Exit(1)
}
var defaultOutput string
fi, err := os.Stat(opt.Input)
if err != nil {
fmt.Fprintln(os.Stderr, err)
flag.Usage()
os.Exit(1)
}
inputBase := filepath.Clean(opt.Input)
if fi.IsDir() {
defaultOutput = fmt.Sprintf("%s.epub", inputBase)
} else {
ext := filepath.Ext(inputBase)
defaultOutput = fmt.Sprintf("%s.epub", inputBase[0:len(inputBase)-len(ext)])
}
if opt.Output == "" {
opt.Output = defaultOutput
}
if filepath.Ext(opt.Output) != ".epub" {
fo, err := os.Stat(opt.Output)
if err != nil {
fmt.Fprintln(os.Stderr, err)
flag.Usage()
os.Exit(1)
}
if !fo.IsDir() {
fmt.Fprintln(os.Stderr, "output must be an existing dir or end with .epub")
flag.Usage()
os.Exit(1)
}
opt.Output = filepath.Join(
opt.Output,
filepath.Base(defaultOutput),
if cmd.Options.Save {
cmd.Options.SaveDefault()
fmt.Fprintf(
os.Stderr,
"%s%s\n\nSaving to %s.\n",
cmd.Options.Header(),
cmd.Options.ShowDefault(),
cmd.Options.FileName(),
)
return
}
profileIdx, profileMatch := ProfilesIdx[opt.Profile]
if !profileMatch {
fmt.Fprintf(os.Stderr, "Profile %q doesn't exists!\n", opt.Profile)
flag.Usage()
os.Exit(1)
}
profile := Profiles[profileIdx]
if opt.LimitMb > 0 && opt.LimitMb < 20 {
fmt.Fprintln(os.Stderr, "LimitMb should be 0 or >= 20")
flag.Usage()
os.Exit(1)
if cmd.Options.Show {
fmt.Fprintln(os.Stderr, cmd.Options.Header(), cmd.Options.ShowDefault())
return
}
if opt.Brightness < -100 || opt.Brightness > 100 {
fmt.Fprintln(os.Stderr, "Brightness should be between -100 and 100")
flag.Usage()
os.Exit(1)
if err := cmd.Validate(); err != nil {
cmd.Fatal(err)
}
if opt.Contrast < -100 || opt.Contrast > 100 {
fmt.Fprintln(os.Stderr, "Contrast should be between -100 and 100")
flag.Usage()
os.Exit(1)
fmt.Fprintln(os.Stderr, cmd.Options)
if cmd.Options.Dry {
return
}
if opt.Title == "" {
ext := filepath.Ext(defaultOutput)
opt.Title = filepath.Base(defaultOutput[0 : len(defaultOutput)-len(ext)])
}
fmt.Fprintln(os.Stderr, opt)
profile := cmd.Options.GetProfile()
if err := epub.NewEpub(&epub.EpubOptions{
Input: opt.Input,
Output: opt.Output,
LimitMb: opt.LimitMb,
Title: opt.Title,
Author: opt.Author,
Input: cmd.Options.Input,
Output: cmd.Options.Output,
LimitMb: cmd.Options.LimitMb,
Title: cmd.Options.Title,
Author: cmd.Options.Author,
ImageOptions: &epub.ImageOptions{
ViewWidth: profile.Width,
ViewHeight: profile.Height,
Quality: opt.Quality,
Crop: opt.Crop,
Quality: cmd.Options.Quality,
Crop: cmd.Options.Crop,
Palette: profile.Palette,
Brightness: opt.Brightness,
Contrast: opt.Contrast,
AutoRotate: opt.AutoRotate,
AutoSplitDoublePage: opt.AutoSplitDoublePage,
NoBlankPage: opt.NoBlankPage,
Manga: opt.Manga,
HasCover: opt.HasCover,
AddPanelView: opt.AddPanelView,
Workers: opt.Workers,
Brightness: cmd.Options.Brightness,
Contrast: cmd.Options.Contrast,
AutoRotate: cmd.Options.AutoRotate,
AutoSplitDoublePage: cmd.Options.AutoSplitDoublePage,
NoBlankPage: cmd.Options.NoBlankPage,
Manga: cmd.Options.Manga,
HasCover: cmd.Options.HasCover,
AddPanelView: cmd.Options.AddPanelView,
Workers: cmd.Options.Workers,
},
}).Write(); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)