refacto progress bars, images option

This commit is contained in:
lapwat
2021-10-06 23:41:23 +02:00
parent ee41e49dd1
commit 2d1d5a964a
9 changed files with 189 additions and 113 deletions

View File

@@ -1,16 +1,16 @@
``` ```
papeer get --format epub --recursive --delay 500 --limit 10 https://news.ycombinator.com/ papeer get --format=epub --recursive --delay=500 --limit=10 https://news.ycombinator.com/
6s [===============================================>--------------------] 70% Status: 7 out of 10 chapters [===============================================>--------------------] Chapters 7 / 10
0s [====================================================================] 100% 1. Three ex-US intelligence officers admit hacking for UAE [====================================================================] 1. Three ex-US intelligence officers admit hacking for UAE
0s [====================================================================] 100% 2. Show HN: Time Travel Debugger [====================================================================] 2. Show HN: Time Travel Debugger
0s [====================================================================] 100% 3. How much faster is Java 17? [====================================================================] 3. How much faster is Java 17?
0s [====================================================================] 100% 4. The First Webcam Was Invented to Keep an Eye on a Coffee Pot [====================================================================] 4. The First Webcam Was Invented to Keep an Eye on a Coffee Pot
0s [====================================================================] 100% 5. Nikon's 2021 Photomicrography Competition Winners [====================================================================] 5. Nikon's 2021 Photomicrography Competition Winners
0s [====================================================================] 100% 6. HTTP Status 418 I'm a teapot [====================================================================] 6. HTTP Status 418 I'm a teapot
0s [====================================================================] 100% 7. H3: Hexagonal hierarchical geospatial indexing system [====================================================================] 7. H3: Hexagonal hierarchical geospatial indexing system
--- [--------------------------------------------------------------------] 0% 8. Automatic cipher suite ordering in Gos crypto/tls [--------------------------------------------------------------------] 8. Automatic cipher suite ordering in Gos crypto/tls
--- [--------------------------------------------------------------------] 0% 9. Find engineering roles at over 800 YC-funded startups [--------------------------------------------------------------------] 9. Find engineering roles at over 800 YC-funded startups
--- [--------------------------------------------------------------------] 0% 10. Futarchy: Robin Hanson on prediction markets [--------------------------------------------------------------------] 10. Futarchy: Robin Hanson on prediction markets
Ebook saved to "Hacker_News.epub" Ebook saved to "Hacker_News.epub"
``` ```
@@ -29,14 +29,14 @@ go get -u github.com/lapwat/papeer
```sh ```sh
platform=linux platform=linux
# platform=darwin for MacOS # platform=darwin for MacOS
curl -L https://github.com/lapwat/papeer/releases/download/v0.1.0/papeer-v0.1.0-$platform-amd64 > papeer curl -L https://github.com/lapwat/papeer/releases/download/v0.2.0/papeer-v0.2.0-$platform-amd64 > papeer
chmod +x papeer chmod +x papeer
sudo mv papeer /usr/local/bin sudo mv papeer /usr/local/bin
``` ```
### On Windows ### On Windows
Download [latest release](https://github.com/lapwat/papeer/releases/download/v0.1.0/papeer-v0.1.0-windows-amd64.exe). Download [latest release](https://github.com/lapwat/papeer/releases/download/v0.2.0/papeer-v0.2.0-windows-amd64.exe).
## Install kindlegen to export websites to MOBI (optional) ## Install kindlegen to export websites to MOBI (optional)
@@ -69,10 +69,11 @@ Flags:
-d, --delay int time to wait before downloading next chapter, in milliseconds (default -1) -d, --delay int time to wait before downloading next chapter, in milliseconds (default -1)
-f, --format string file format [md, epub, mobi] (default "md") -f, --format string file format [md, epub, mobi] (default "md")
-h, --help help for papeer -h, --help help for papeer
--images retrieve images only
-i, --include include URL as first chapter, in resursive mode -i, --include include URL as first chapter, in resursive mode
-l, --limit int limit number of chapters, in recursive mode (default -1) -l, --limit int limit number of chapters, in recursive mode (default -1)
-o, --output string output file -o, --offset int skip first chapters, in recursive mode
-q, --quiet do not show logs --output string output file
-r, --recursive create one chapter per natigation item -r, --recursive create one chapter per natigation item
-s, --selector string table of content CSS selector -s, --selector string table of content CSS selector
--stdout print to standard output --stdout print to standard output

View File

@@ -3,11 +3,10 @@ package book
type link struct { type link struct {
href string href string
text string text string
class string
} }
func NewLink(href, text, class string) link { func NewLink(href, text string) link {
return link{href, text, class} return link{href, text}
} }
func (c link) Href() string { func (c link) Href() string {
@@ -17,7 +16,3 @@ func (c link) Href() string {
func (c link) Text() string { func (c link) Text() string {
return c.text return c.text
} }
func (c link) Class() string {
return c.class
}

47
book/progress.go Normal file
View File

@@ -0,0 +1,47 @@
package book
import (
"fmt"
"github.com/gosuri/uiprogress"
)
type progress struct {
global *uiprogress.Bar
individuals []*uiprogress.Bar
}
func NewProgress(links []link) progress {
uiprogress.Start()
global := uiprogress.AddBar(len(links))
global.AppendFunc(func(b *uiprogress.Bar) string {
return fmt.Sprintf("Chapters %d / %d", b.Current(), len(links))
})
individuals := []*uiprogress.Bar{}
// hide individual bars if more than 50 chapters
if len(links) <= 50 {
for index, link := range links {
bar := uiprogress.AddBar(1)
barText := fmt.Sprintf("%d. %s", index+1, link.text)
bar.AppendFunc(func(b *uiprogress.Bar) string {
return barText
})
individuals = append(individuals, bar)
}
}
return progress{global, individuals}
}
func (p *progress) IncrGlobal() {
p.global.Incr()
}
func (p *progress) Incr(index int) {
p.global.Incr()
if len(p.individuals) > index {
p.individuals[index].Incr()
}
}

View File

@@ -12,73 +12,66 @@ import (
"github.com/PuerkitoBio/goquery" "github.com/PuerkitoBio/goquery"
readability "github.com/go-shiori/go-readability" readability "github.com/go-shiori/go-readability"
colly "github.com/gocolly/colly/v2" colly "github.com/gocolly/colly/v2"
"github.com/gosuri/uiprogress"
) )
func NewBookFromURL(url, selector string, recursive, include bool, limit, delay int) book { func NewBookFromURL(url, selector string, recursive, include, images bool, limit, offset, delay int) book {
if recursive { if recursive {
home := NewChapterFromURL(url) chapters := tableOfContent(url, selector, limit, offset, delay, include, images)
b := New(home.Name(), home.Author())
chapters := tableOfContent(url, selector, limit, delay) b := New(chapters[0].Name(), chapters[0].Author())
if include {
b.AddChapter(home)
}
for _, c := range chapters { for _, c := range chapters {
b.AddChapter(c) b.AddChapter(c)
} }
return b return b
} else { } else {
c := NewChapterFromURL(url) c := NewChapterFromURL(url, images)
b := New(c.Name(), c.Author()) b := New(c.Name(), c.Author())
b.AddChapter(c) b.AddChapter(c)
return b return b
} }
} }
func NewChapterFromURL(url string) chapter { func NewChapterFromURL(url string, images bool) chapter {
article, err := readability.FromURL(url, 30*time.Second) article, err := readability.FromURL(url, 30*time.Second)
if err != nil { if err != nil {
log.Fatalf("failed to parse %s, %v\n", url, err) log.Fatalf("failed to parse %s, %v\n", url, err)
} }
return chapter{article.Title, article.Byline, article.Content} content := strings.ReplaceAll(article.Content, "\n", "")
if images {
// Load the HTML document
doc, err := goquery.NewDocumentFromReader(strings.NewReader(content))
if err != nil {
log.Fatal(err)
}
// Find the review items
doc.Find("img").Each(func(i int, s *goquery.Selection) {
content, _ = goquery.OuterHtml(s)
})
}
return chapter{article.Title, article.Byline, content}
} }
func tableOfContent(url, selector string, limit, delay int) []chapter { func tableOfContent(url, selector string, limit, offset, delay int, include, images bool) []chapter {
base, err := urllib.Parse(url) base, err := urllib.Parse(url)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
links := GetLinks(base, selector) links, err := GetLinks(base, selector, limit, offset, include)
if limit != -1 { if err != nil {
limit = int(math.Min(float64(limit), float64(len(links)))) log.Fatal(err)
links = links[:limit]
} }
chapters := make([]chapter, len(links)) chapters := make([]chapter, len(links))
progress := NewProgress(links)
// init global progress bar
uiprogress.Start()
barGlobal := uiprogress.AddBar(len(links)).AppendCompleted().PrependElapsed()
barGlobal.AppendFunc(func(b *uiprogress.Bar) string {
return fmt.Sprintf("Status: %d out of %d chapters", b.Current(), len(links))
})
// init progress bars
bars := []*uiprogress.Bar{}
for index, link := range links {
bar := uiprogress.AddBar(1).AppendCompleted().PrependElapsed()
barText := fmt.Sprintf("%d. %s", index+1, link.text)
bar.AppendFunc(func(b *uiprogress.Bar) string {
return barText
})
bars = append(bars, bar)
}
if delay >= 0 { if delay >= 0 {
for index, link := range links { for index, link := range links {
// and then use it to parse relative URLs // and then use it to parse relative URLs
u, err := base.Parse(link.href) u, err := base.Parse(link.href)
@@ -86,16 +79,15 @@ func tableOfContent(url, selector string, limit, delay int) []chapter {
log.Fatal(err) log.Fatal(err)
} }
chapters[index] = NewChapterFromURL(u.String()) chapters[index] = NewChapterFromURL(u.String(), images)
progress.Incr(index)
bars[index].Incr() // short sleep for last chapter to let the progress bar update
barGlobal.Incr() if index == len(links)-1 {
delay = 100
// do not wait after downloading last chapter
if index < len(links)-1 {
time.Sleep(time.Duration(delay) * time.Millisecond)
} }
time.Sleep(time.Duration(delay) * time.Millisecond)
} }
} else { } else {
@@ -112,10 +104,9 @@ func tableOfContent(url, selector string, limit, delay int) []chapter {
log.Fatal(err) log.Fatal(err)
} }
chapters[index] = NewChapterFromURL(u.String()) chapters[index] = NewChapterFromURL(u.String(), images)
progress.Incr(index)
bars[index].Incr()
barGlobal.Incr()
}(index, l) }(index, l)
} }
wg.Wait() wg.Wait()
@@ -140,8 +131,7 @@ func GetPath(elm *goquery.Selection) string {
return join return join
} }
func GetLinks(url *urllib.URL, selector string, limit, offset int, include bool) ([]link, error) {
func GetLinks(url *urllib.URL, selector string) []link {
selectorSet := true selectorSet := true
if selector == "" { if selector == "" {
selector = "a" selector = "a"
@@ -158,13 +148,17 @@ func GetLinks(url *urllib.URL, selector string) []link {
href := e.Attr("href") href := e.Attr("href")
text := strings.TrimSpace(e.Text) text := strings.TrimSpace(e.Text)
path := GetPath(e.DOM) path := GetPath(e.DOM)
key := path
// include element class in key if selector is set
if !selectorSet {
class := e.Attr("class") class := e.Attr("class")
key := fmt.Sprintf("%s.%s", path, class) key = fmt.Sprintf("%s.%s", path, class)
}
if selectorSet || text != "" { if selectorSet || text != "" {
pathLinks[key] = append(pathLinks[key], NewLink(href, text, class)) pathLinks[key] = append(pathLinks[key], NewLink(href, text))
pathCount[key] += len(text) pathCount[key] += len(text)
// pathCount[key]++
if pathCount[key] > pathCount[pathMax] { if pathCount[key] > pathCount[pathMax] {
pathMax = key pathMax = key
@@ -172,28 +166,24 @@ func GetLinks(url *urllib.URL, selector string) []link {
} }
}) })
c.Visit(url.String()) c.Visit(url.String())
return pathLinks[pathMax]
// // visit and count link classes links := pathLinks[pathMax]
// classesLinks := map[string][]link{} if len(links) == 0 {
// classesCount := map[string]int{} return []link{}, fmt.Errorf("no link found for selector: %s", selector)
// classMax := "" }
// c := colly.NewCollector() end := len(links)
// c.OnHTML(selector, func(e *colly.HTMLElement) { if limit != -1 {
// href := e.Attr("href") end = int(math.Min(float64(limit+offset), float64(len(links))))
// text := strings.TrimSpace(e.Text) }
// class := e.Attr("class")
// if selectorSet || class != "" && text != "" { links = links[offset:end]
// classesLinks[class] = append(classesLinks[class], NewLink(href, text))
// classesCount[class]++
// if classesCount[class] > classesCount[classMax] { if include {
// classMax = class c := NewChapterFromURL(url.String(), false)
// } l := NewLink(url.String(), c.Name())
// } links = append([]link{l}, links...)
// }) }
// c.Visit(url.String())
// return classesLinks[classMax] return links, nil
} }

View File

@@ -15,9 +15,9 @@ import (
"github.com/lapwat/papeer/book" "github.com/lapwat/papeer/book"
) )
var quiet, stdout, recursive, include bool var stdout, recursive, include, images bool
var format, output, selector string var format, output, selector string
var limit, delay int var limit, offset, delay int
var getCmd = &cobra.Command{ var getCmd = &cobra.Command{
Use: "get", Use: "get",
@@ -60,6 +60,10 @@ var getCmd = &cobra.Command{
return errors.New("cannot use limit option if not in recursive mode") return errors.New("cannot use limit option if not in recursive mode")
} }
if cmd.Flags().Changed("offset") && recursive == false {
return errors.New("cannot use offset option if not in recursive mode")
}
if cmd.Flags().Changed("delay") && recursive == false { if cmd.Flags().Changed("delay") && recursive == false {
return errors.New("cannot use delay option if not in recursive mode") return errors.New("cannot use delay option if not in recursive mode")
} }
@@ -68,7 +72,7 @@ var getCmd = &cobra.Command{
}, },
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
url := args[0] url := args[0]
b := book.NewBookFromURL(url, selector, recursive, include, limit, delay) b := book.NewBookFromURL(url, selector, recursive, include, images, limit, offset, delay)
if len(output) == 0 { if len(output) == 0 {
// set default output // set default output
@@ -78,12 +82,16 @@ var getCmd = &cobra.Command{
} }
if format == "md" { if format == "md" {
f, err := os.Create(output) var f *os.File
var err error
if !stdout {
f, err = os.Create(output)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
defer f.Close() defer f.Close()
}
for _, c := range b.Chapters() { for _, c := range b.Chapters() {
content, err := md.NewConverter("", true, nil).ConvertString(c.Content()) content, err := md.NewConverter("", true, nil).ConvertString(c.Content())
@@ -96,7 +104,6 @@ var getCmd = &cobra.Command{
if stdout { if stdout {
fmt.Println(text) fmt.Println(text)
} else { } else {
_, err := f.WriteString(text) _, err := f.WriteString(text)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
@@ -115,8 +122,16 @@ var getCmd = &cobra.Command{
e.SetAuthor(b.Author()) e.SetAuthor(b.Author())
for _, c := range b.Chapters() { for _, c := range b.Chapters() {
if images {
e.AddSection(c.Content(), "", "", "")
} else {
html := fmt.Sprintf("<h1>%s</h1>%s", c.Name(), c.Content()) html := fmt.Sprintf("<h1>%s</h1>%s", c.Name(), c.Content())
e.AddSection(html, c.Name(), "", "")
_, err := e.AddSection(html, c.Name(), "", "")
if err != nil {
log.Fatal(err)
}
}
} }
err := e.Write(output) err := e.Write(output)
@@ -152,7 +167,7 @@ var getCmd = &cobra.Command{
err2 := os.Remove(outputEPUB) err2 := os.Remove(outputEPUB)
if err2 != nil { if err2 != nil {
log.Fatal(err) log.Fatal(err2)
} }
} }
}, },

View File

@@ -27,11 +27,17 @@ var listCmd = &cobra.Command{
log.Fatal(err) log.Fatal(err)
} }
links := book.GetLinks(base, selector) links, err := book.GetLinks(base, selector, limit, offset, include)
if err != nil {
log.Fatal(err)
}
t := table.NewWriter() t := table.NewWriter()
t.SetOutputMirror(os.Stdout) t.SetOutputMirror(os.Stdout)
t.AppendHeader(table.Row{"#", "Name", "Url", "Class"}) t.Style().Options.DrawBorder = false
t.Style().Options.SeparateColumns = false
t.Style().Options.SeparateHeader = false
t.AppendHeader(table.Row{"#", "Name", "Url"})
for index, link := range links { for index, link := range links {
u, err := base.Parse(link.Href()) u, err := base.Parse(link.Href())
@@ -39,7 +45,7 @@ var listCmd = &cobra.Command{
log.Fatal(err) log.Fatal(err)
} }
t.AppendRow([]interface{}{index + 1, link.Text(), u.String(), link.Class()}) t.AppendRow([]interface{}{index + 1, link.Text(), u.String()})
} }
t.Render() t.Render()

View File

@@ -24,13 +24,14 @@ func Execute() {
func init() { func init() {
rootCmd.PersistentFlags().StringVarP(&format, "format", "f", "md", "file format [md, epub, mobi]") rootCmd.PersistentFlags().StringVarP(&format, "format", "f", "md", "file format [md, epub, mobi]")
rootCmd.PersistentFlags().StringVarP(&output, "output", "o", "", "output file") rootCmd.PersistentFlags().StringVarP(&output, "output", "", "", "output file")
rootCmd.PersistentFlags().StringVarP(&selector, "selector", "s", "", "table of content CSS selector") rootCmd.PersistentFlags().StringVarP(&selector, "selector", "s", "", "table of content CSS selector, in resursive mode")
rootCmd.PersistentFlags().BoolVarP(&recursive, "recursive", "r", false, "create one chapter per natigation item") rootCmd.PersistentFlags().BoolVarP(&recursive, "recursive", "r", false, "create one chapter per natigation item")
rootCmd.PersistentFlags().BoolVarP(&include, "include", "i", false, "include URL as first chapter, in resursive mode") rootCmd.PersistentFlags().BoolVarP(&include, "include", "i", false, "include URL as first chapter, in resursive mode")
rootCmd.PersistentFlags().BoolVarP(&quiet, "quiet", "q", false, "do not show logs")
rootCmd.PersistentFlags().BoolVarP(&stdout, "stdout", "", false, "print to standard output") rootCmd.PersistentFlags().BoolVarP(&stdout, "stdout", "", false, "print to standard output")
rootCmd.PersistentFlags().BoolVarP(&images, "images", "", false, "retrieve images only")
rootCmd.PersistentFlags().IntVarP(&limit, "limit", "l", -1, "limit number of chapters, in recursive mode") rootCmd.PersistentFlags().IntVarP(&limit, "limit", "l", -1, "limit number of chapters, in recursive mode")
rootCmd.PersistentFlags().IntVarP(&offset, "offset", "o", 0, "skip first chapters, in recursive mode")
rootCmd.PersistentFlags().IntVarP(&delay, "delay", "d", -1, "time to wait before downloading next chapter, in milliseconds") rootCmd.PersistentFlags().IntVarP(&delay, "delay", "d", -1, "time to wait before downloading next chapter, in milliseconds")
rootCmd.AddCommand(getCmd) rootCmd.AddCommand(getCmd)

9
go.mod
View File

@@ -31,13 +31,18 @@ require (
github.com/jedib0t/go-pretty/v6 v6.2.4 // indirect github.com/jedib0t/go-pretty/v6 v6.2.4 // indirect
github.com/kennygrant/sanitize v1.2.4 // indirect github.com/kennygrant/sanitize v1.2.4 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // indirect github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // indirect
github.com/schollz/progressbar/v3 v3.8.3 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect github.com/sirupsen/logrus v1.8.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/temoto/robotstxt v1.1.2 // indirect github.com/temoto/robotstxt v1.1.2 // indirect
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0 // indirect
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect
golang.org/x/text v0.3.6 // indirect golang.org/x/text v0.3.6 // indirect
google.golang.org/appengine v1.6.7 // indirect google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.27.1 // indirect google.golang.org/protobuf v1.27.1 // indirect

16
go.sum
View File

@@ -240,6 +240,7 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
github.com/kennygrant/sanitize v1.2.4 h1:gN25/otpP5vAsO2djbMhF/LQX6R7+O1TB4yv8NzpJ3o= github.com/kennygrant/sanitize v1.2.4 h1:gN25/otpP5vAsO2djbMhF/LQX6R7+O1TB4yv8NzpJ3o=
github.com/kennygrant/sanitize v1.2.4/go.mod h1:LGsjYYtgxbetdg5owWB2mpgUL6e2nfw2eObZ0u0qvak= github.com/kennygrant/sanitize v1.2.4/go.mod h1:LGsjYYtgxbetdg5owWB2mpgUL6e2nfw2eObZ0u0qvak=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
@@ -259,9 +260,13 @@ github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
@@ -295,6 +300,8 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
@@ -302,6 +309,8 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI= github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI=
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
github.com/schollz/progressbar/v3 v3.8.3 h1:FnLGl3ewlDUP+YdSwveXBaXs053Mem/du+wr7XSYKl8=
github.com/schollz/progressbar/v3 v3.8.3/go.mod h1:pWnVCjSBZsT2X3nx9HfRdnCDrpbevliMeoEVhStwHko=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sebdah/goldie/v2 v2.5.1 h1:hh70HvG4n3T3MNRJN2z/baxPR8xutxo7JVxyi2svl+s= github.com/sebdah/goldie/v2 v2.5.1 h1:hh70HvG4n3T3MNRJN2z/baxPR8xutxo7JVxyi2svl+s=
github.com/sebdah/goldie/v2 v2.5.1/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI= github.com/sebdah/goldie/v2 v2.5.1/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
@@ -380,6 +389,8 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -533,9 +544,14 @@ golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0 h1:xrCZDmdtoloIiooiA9q0OQb9r8HejIHYoHGhGCe1pGg=
golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=