celogeek 393bba0266
remove panelview by default, add an option addpanelview
On kindle it's better to not add manually the panelview as it will
unlock several features:
- Animation
- Adjust to screen size
- Panel View
It means, you can still have a panel view, but it's handled by kindle
directly.
2023-03-05 00:01:19 +01:00

283 lines
8.2 KiB
Go

package main
import (
"flag"
"fmt"
"image/color"
"os"
"path/filepath"
"runtime"
"strings"
"github.com/celogeek/go-comic-converter/internal/epub"
)
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
}
}
type Option struct {
Input string
Output string
Profile string
Author string
Title string
Quality int
NoCrop bool
Brightness int
Contrast int
Auto bool
AutoRotate bool
AutoSplitDoublePage bool
NoBlankPage bool
Manga bool
NoCover bool
AddPanelView bool
Workers int
LimitMb int
}
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.NoCrop,
o.Brightness,
o.Contrast,
o.AutoRotate,
o.AutoSplitDoublePage,
o.NoBlankPage,
o.Manga,
!o.NoCover,
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,
))
}
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", "", fmt.Sprintf("Profile to use: \n%s", 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", 85, "Quality of the image")
flag.BoolVar(&opt.NoCrop, "nocrop", false, "Disable cropping")
flag.IntVar(&opt.Brightness, "brightness", 0, "Brightness readjustement: between -100 and 100, > 0 lighter, < 0 darker")
flag.IntVar(&opt.Contrast, "contrast", 0, "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", false, "Auto Rotate page when width > height")
flag.BoolVar(&opt.AutoSplitDoublePage, "autosplitdoublepage", false, "Auto Split double page when width > height")
flag.BoolVar(&opt.NoBlankPage, "noblankpage", false, "Remove blank pages")
flag.BoolVar(&opt.Manga, "manga", false, "Manga mode (right to left)")
flag.BoolVar(&opt.NoCover, "nocover", false, "Indicate if your comic doesn't have a cover. The first page will be used as a cover and include after the title.")
flag.BoolVar(&opt.AddPanelView, "addpanelview", false, "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", 0, "Limit size of the ePub: Default nolimit (0), Minimum 20")
flag.IntVar(&opt.Workers, "workers", runtime.NumCPU(), "Number of workers")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", filepath.Base(os.Args[0]))
flag.PrintDefaults()
}
flag.Parse()
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),
)
}
profileIdx, profileMatch := ProfilesIdx[opt.Profile]
if !profileMatch {
fmt.Fprintln(os.Stderr, "Profile doesn't exists!")
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 opt.Brightness < -100 || opt.Brightness > 100 {
fmt.Fprintln(os.Stderr, "Brightness should be between -100 and 100")
flag.Usage()
os.Exit(1)
}
if opt.Contrast < -100 || opt.Contrast > 100 {
fmt.Fprintln(os.Stderr, "Contrast should be between -100 and 100")
flag.Usage()
os.Exit(1)
}
if opt.Title == "" {
ext := filepath.Ext(defaultOutput)
opt.Title = filepath.Base(defaultOutput[0 : len(defaultOutput)-len(ext)])
}
if opt.Auto {
opt.AutoRotate = true
opt.AutoSplitDoublePage = true
}
fmt.Fprintln(os.Stderr, opt)
if err := epub.NewEpub(&epub.EpubOptions{
Input: opt.Input,
Output: opt.Output,
LimitMb: opt.LimitMb,
Title: opt.Title,
Author: opt.Author,
ImageOptions: &epub.ImageOptions{
ViewWidth: profile.Width,
ViewHeight: profile.Height,
Quality: opt.Quality,
Crop: !opt.NoCrop,
Palette: profile.Palette,
Brightness: opt.Brightness,
Contrast: opt.Contrast,
AutoRotate: opt.AutoRotate,
AutoSplitDoublePage: opt.AutoSplitDoublePage,
NoBlankPage: opt.NoBlankPage,
Manga: opt.Manga,
HasCover: !opt.NoCover,
AddPanelView: opt.AddPanelView,
Workers: opt.Workers,
},
}).Write(); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
os.Exit(0)
}