add json output

This commit is contained in:
Celogeek 2023-09-25 09:33:02 +02:00
parent 4a3fc60732
commit a552d7ac90
Signed by: celogeek
SSH Key Fingerprint: SHA256:DEDfxIK2nUWXbslbRkww3zsauDjhWHlTXar+ak4lDJ4
9 changed files with 162 additions and 25 deletions

View File

@ -8,6 +8,7 @@ It use goflag with additional feature:
package converter package converter
import ( import (
"encoding/json"
"errors" "errors"
"flag" "flag"
"fmt" "fmt"
@ -155,6 +156,7 @@ func (c *Converter) InitParse() {
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.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")
} }
@ -400,12 +402,24 @@ func (c *Converter) Fatal(err error) {
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)
var mem runtime.MemStats var mem runtime.MemStats
runtime.ReadMemStats(&mem) runtime.ReadMemStats(&mem)
fmt.Fprintf(
os.Stderr, if c.Options.Json {
"Completed in %s, Memory usage %d Mb\n", json.NewEncoder(os.Stdout).Encode(map[string]any{
time.Since(c.startAt).Round(time.Millisecond), "type": "stats",
mem.Sys/1024/1024, "data": map[string]any{
) "elapse": elapse,
"memory_usage_mb": mem.Sys / 1024 / 1024,
},
})
} else {
fmt.Fprintf(
os.Stderr,
"Completed in %s, Memory usage %d Mb\n",
elapse,
mem.Sys/1024/1024,
)
}
} }

View File

@ -4,6 +4,7 @@ Manage options with default value from config.
package options package options
import ( import (
"encoding/json"
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
@ -69,6 +70,7 @@ type Options struct {
Dry bool `yaml:"-"` Dry bool `yaml:"-"`
DryVerbose bool `yaml:"-"` DryVerbose bool `yaml:"-"`
Quiet bool `yaml:"-"` Quiet bool `yaml:"-"`
Json bool `yaml:"-"`
Version bool `yaml:"-"` Version bool `yaml:"-"`
Help bool `yaml:"-"` Help bool `yaml:"-"`
@ -116,13 +118,73 @@ func (o *Options) String() string {
{"Title", o.Title}, {"Title", o.Title},
{"Workers", o.Workers}, {"Workers", o.Workers},
} { } {
b.WriteString(fmt.Sprintf("\n %-26s: %v", v.K, v.V)) b.WriteString(fmt.Sprintf("\n %-32s: %v", v.K, v.V))
} }
b.WriteString(o.ShowConfig()) b.WriteString(o.ShowConfig())
b.WriteRune('\n') b.WriteRune('\n')
return b.String() return b.String()
} }
func (o *Options) MarshalJSON() ([]byte, error) {
out := map[string]any{
"input": o.Input,
"output": o.Output,
"author": o.Author,
"title": o.Title,
"workers": o.Workers,
"profile": o.GetProfile(),
"format": o.Format,
"grayscale": o.Grayscale,
"crop": o.Crop,
"autocontrast": o.AutoContrast,
"autorotate": o.AutoRotate,
"noblankimage": o.NoBlankImage,
"manga": o.Manga,
"hascover": o.HasCover,
"stripfirstdirectoryfromtoc": o.StripFirstDirectoryFromToc,
"sortpathmode": o.SortPathMode,
"foregroundcolor": o.ForegroundColor,
"backgroundcolor": o.BackgroundColor,
"resize": !o.NoResize,
"aspectratio": o.AspectRatio,
"portraitonly": o.PortraitOnly,
"titlepage": o.TitlePage,
}
if o.Format == "jpeg" {
out["quality"] = o.Quality
}
if o.Grayscale {
out["grayscale_mode"] = o.GrayscaleMode
}
if o.Crop {
out["crop_ratio"] = map[string]any{
"left": o.CropRatioLeft,
"right": o.CropRatioRight,
"up": o.CropRatioUp,
"bottom": o.CropRatioBottom,
}
}
if o.Brightness != 0 {
out["brightness"] = o.Brightness
}
if o.Contrast != 0 {
out["contrast"] = o.Contrast
}
if o.PortraitOnly || !o.AppleBookCompatibility {
out["autosplitdoublepage"] = o.AutoSplitDoublePage
if o.AutoSplitDoublePage {
out["keepdoublepageifsplitted"] = o.KeepDoublePageIfSplitted
}
}
if o.LimitMb != 0 {
out["limitmb"] = o.LimitMb
}
if !o.PortraitOnly {
out["applebookcompatibility"] = o.AppleBookCompatibility
}
return json.Marshal(out)
}
// Config file: ~/.go-comic-converter.yaml // Config file: ~/.go-comic-converter.yaml
func (o *Options) FileName() string { func (o *Options) FileName() string {
home, _ := os.UserHomeDir() home, _ := os.UserHomeDir()
@ -205,19 +267,19 @@ func (o *Options) ShowConfig() string {
{"Grayscale", o.Grayscale, true}, {"Grayscale", o.Grayscale, true},
{"Grayscale Mode", grayscaleMode, o.Grayscale}, {"Grayscale Mode", grayscaleMode, o.Grayscale},
{"Crop", o.Crop, true}, {"Crop", o.Crop, true},
{"CropRatio", fmt.Sprintf("%d Left - %d Up - %d Right - %d Bottom", o.CropRatioLeft, o.CropRatioUp, o.CropRatioRight, o.CropRatioBottom), o.Crop}, {"Crop Ratio", fmt.Sprintf("%d Left - %d Up - %d Right - %d Bottom", o.CropRatioLeft, o.CropRatioUp, o.CropRatioRight, o.CropRatioBottom), o.Crop},
{"Brightness", o.Brightness, o.Brightness != 0}, {"Brightness", o.Brightness, o.Brightness != 0},
{"Contrast", o.Contrast, o.Contrast != 0}, {"Contrast", o.Contrast, o.Contrast != 0},
{"AutoContrast", o.AutoContrast, true}, {"Auto Contrast", o.AutoContrast, true},
{"AutoRotate", o.AutoRotate, true}, {"Auto Rotate", o.AutoRotate, true},
{"AutoSplitDoublePage", o.AutoSplitDoublePage, o.PortraitOnly || !o.AppleBookCompatibility}, {"Auto Split DoublePage", o.AutoSplitDoublePage, o.PortraitOnly || !o.AppleBookCompatibility},
{"KeepDoublePageIfSplitted", o.KeepDoublePageIfSplitted, (o.PortraitOnly || !o.AppleBookCompatibility) && o.AutoSplitDoublePage}, {"Keep DoublePage If Splitted", o.KeepDoublePageIfSplitted, (o.PortraitOnly || !o.AppleBookCompatibility) && o.AutoSplitDoublePage},
{"NoBlankImage", o.NoBlankImage, true}, {"No Blank Image", o.NoBlankImage, true},
{"Manga", o.Manga, true}, {"Manga", o.Manga, true},
{"HasCover", o.HasCover, true}, {"Has Cover", o.HasCover, true},
{"LimitMb", fmt.Sprintf("%d Mb", o.LimitMb), o.LimitMb != 0}, {"Limit", fmt.Sprintf("%d Mb", o.LimitMb), o.LimitMb != 0},
{"StripFirstDirectoryFromToc", o.StripFirstDirectoryFromToc, true}, {"Strip First Directory From Toc", o.StripFirstDirectoryFromToc, true},
{"SortPathMode", sortpathmode, true}, {"Sort Path Mode", sortpathmode, true},
{"Foreground Color", fmt.Sprintf("#%s", o.ForegroundColor), true}, {"Foreground Color", fmt.Sprintf("#%s", o.ForegroundColor), true},
{"Background Color", fmt.Sprintf("#%s", o.BackgroundColor), true}, {"Background Color", fmt.Sprintf("#%s", o.BackgroundColor), true},
{"Resize", !o.NoResize, true}, {"Resize", !o.NoResize, true},
@ -227,7 +289,7 @@ func (o *Options) ShowConfig() string {
{"Apple Book Compatibility", o.AppleBookCompatibility, !o.PortraitOnly}, {"Apple Book Compatibility", o.AppleBookCompatibility, !o.PortraitOnly},
} { } {
if v.Condition { if v.Condition {
b.WriteString(fmt.Sprintf("\n %-30s: %v", v.Key, v.Value)) b.WriteString(fmt.Sprintf("\n %-32s: %v", v.Key, v.Value))
} }
} }
return b.String() return b.String()

View File

@ -9,10 +9,10 @@ import (
) )
type Profile struct { type Profile struct {
Code string Code string `json:"code"`
Description string Description string `json:"description"`
Width int Width int `json:"width"`
Height int Height int `json:"height"`
} }
type Profiles []Profile type Profiles []Profile

View File

@ -369,6 +369,7 @@ func (e *ePub) Write() error {
CurrentJob: 2, CurrentJob: 2,
TotalJob: 2, TotalJob: 2,
Quiet: e.Quiet, Quiet: e.Quiet,
Json: e.Json,
}) })
e.computeViewPort(epubParts) e.computeViewPort(epubParts)
@ -454,7 +455,9 @@ func (e *ePub) Write() error {
bar.Add(1) bar.Add(1)
} }
bar.Close() bar.Close()
fmt.Fprintln(os.Stderr) if !e.Json {
fmt.Fprintln(os.Stderr)
}
// display corrupted images // display corrupted images
hasError := false hasError := false

View File

@ -54,6 +54,7 @@ func (e *EPUBImageProcessor) Load() (images []*epubimage.Image, err error) {
// processing // processing
bar := epubprogress.New(epubprogress.Options{ bar := epubprogress.New(epubprogress.Options{
Quiet: e.Quiet, Quiet: e.Quiet,
Json: e.Json,
Max: imageCount, Max: imageCount,
Description: "Processing", Description: "Processing",
CurrentJob: 1, CurrentJob: 1,

View File

@ -53,6 +53,7 @@ type Options struct {
DryVerbose bool DryVerbose bool
SortPathMode int SortPathMode int
Quiet bool Quiet bool
Json bool
Workers int Workers int
Image *Image Image *Image
} }

View File

@ -13,6 +13,7 @@ import (
type Options struct { type Options struct {
Quiet bool Quiet bool
Json bool
Max int Max int
Description string Description string
CurrentJob int CurrentJob int
@ -21,13 +22,18 @@ type Options struct {
type EpubProgress interface { type EpubProgress interface {
Add(num int) error Add(num int) error
Close() (err error) Close() error
} }
func New(o Options) EpubProgress { func New(o Options) EpubProgress {
if o.Quiet { if o.Quiet {
return progressbar.DefaultSilent(int64(o.Max)) return progressbar.DefaultSilent(int64(o.Max))
} }
if o.Json {
return newEpubProgressJson(o)
}
fmtJob := fmt.Sprintf("%%0%dd", len(fmt.Sprint(o.TotalJob))) fmtJob := fmt.Sprintf("%%0%dd", len(fmt.Sprint(o.TotalJob)))
fmtDesc := fmt.Sprintf("[%s/%s] %%-15s", fmtJob, fmtJob) fmtDesc := fmt.Sprintf("[%s/%s] %%-15s", fmtJob, fmtJob)
return progressbar.NewOptions(o.Max, return progressbar.NewOptions(o.Max,

View File

@ -0,0 +1,42 @@
package epubprogress
import (
"encoding/json"
"os"
)
type EpubProgressJson struct {
o Options
e *json.Encoder
current int
}
func newEpubProgressJson(o Options) EpubProgress {
return &EpubProgressJson{
o: o,
e: json.NewEncoder(os.Stdout),
}
}
func (p *EpubProgressJson) Add(num int) error {
p.current += num
p.e.Encode(map[string]any{
"type": "progress",
"data": map[string]any{
"progress": map[string]any{
"current": p.current,
"total": p.o.Max,
},
"steps": map[string]any{
"current": p.o.CurrentJob,
"total": p.o.TotalJob,
},
"description": p.o.Description,
},
})
return nil
}
func (p *EpubProgressJson) Close() error {
return nil
}

10
main.go
View File

@ -8,6 +8,7 @@ EPUB is now support by Amazon through [SendToKindle](https://www.amazon.com/gp/s
package main package main
import ( import (
"encoding/json"
"fmt" "fmt"
"os" "os"
"runtime/debug" "runtime/debug"
@ -96,7 +97,13 @@ $ go install github.com/celogeek/go-comic-converter/v%d@%s
cmd.Fatal(err) cmd.Fatal(err)
} }
fmt.Fprintln(os.Stderr, cmd.Options) if cmd.Options.Json {
json.NewEncoder(os.Stdout).Encode(map[string]any{
"type": "options", "data": cmd.Options,
})
} else {
fmt.Fprintln(os.Stderr, cmd.Options)
}
profile := cmd.Options.GetProfile() profile := cmd.Options.GetProfile()
@ -113,6 +120,7 @@ $ go install github.com/celogeek/go-comic-converter/v%d@%s
Dry: cmd.Options.Dry, Dry: cmd.Options.Dry,
DryVerbose: cmd.Options.DryVerbose, DryVerbose: cmd.Options.DryVerbose,
Quiet: cmd.Options.Quiet, Quiet: cmd.Options.Quiet,
Json: cmd.Options.Json,
Image: &epuboptions.Image{ Image: &epuboptions.Image{
Crop: &epuboptions.Crop{Enabled: cmd.Options.Crop, Left: cmd.Options.CropRatioLeft, Up: cmd.Options.CropRatioUp, Right: cmd.Options.CropRatioRight, Bottom: cmd.Options.CropRatioBottom}, Crop: &epuboptions.Crop{Enabled: cmd.Options.Crop, Left: cmd.Options.CropRatioLeft, Up: cmd.Options.CropRatioUp, Right: cmd.Options.CropRatioRight, Bottom: cmd.Options.CropRatioBottom},
Quality: cmd.Options.Quality, Quality: cmd.Options.Quality,