Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add new wgot cmd #37

Merged
merged 1 commit into from
Oct 31, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,29 @@ before:
- go mod tidy
builds:
-
id: got
env:
- CGO_ENABLED=0
dir: cmd/got
binary: got
ldflags:
- -s -w -X main.version={{.Version}}+{{.ShortCommit}}
goos:
- linux
- darwin
- freebsd
- windows
goarch:
- amd64
- arm64
- arm
- 386
-
id: wgot
env:
- CGO_ENABLED=0
dir: cmd/wgot
binary: wgot
ldflags:
- -s -w -X main.version={{.Version}}+{{.ShortCommit}}
goos:
Expand Down Expand Up @@ -37,7 +57,7 @@ signs:
- artifacts: checksum

snapshot:
name_template: "{{ .Tag }}-next"
name_template: "{{ .Binary }}-{{ .Tag }}-next"

changelog:
sort: asc
Expand Down
269 changes: 269 additions & 0 deletions cmd/wgot/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
package main

import (
"bufio"
"context"
"errors"
"fmt"
"log"
"net/url"
"os"
"os/signal"
"strings"
"syscall"

"github.com/dustin/go-humanize"
"github.com/melbahja/got"
"github.com/urfave/cli/v2"
"gitlab.com/poldi1405/go-ansi"
"gitlab.com/poldi1405/go-indicators/progress"
"golang.org/x/crypto/ssh/terminal"
)

var version string

var HeaderSlice []got.GotHeader

func main() {

// New context.
ctx, cancel := context.WithCancel(context.Background())

interruptChan := make(chan os.Signal, 1)

signal.Notify(interruptChan, syscall.SIGINT, syscall.SIGKILL, syscall.SIGTERM)

go func() {
<-interruptChan
cancel()
signal.Stop(interruptChan)
log.Fatal(got.ErrDownloadAborted)
}()

// CLI app.
app := &cli.App{
Name: "Got",
Usage: "The fastest http downloader.",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "output",
Usage: "Download `path`, if dir passed the path witll be `dir + output`.",
Aliases: []string{"o"},
},
&cli.StringFlag{
Name: "dir",
Usage: "Save downloaded file to a `directory`.",
Aliases: []string{"d"},
},
&cli.StringFlag{
Name: "file",
Usage: "Batch download from list of urls in a `file`.",
Aliases: []string{"bf", "f"},
},
&cli.Uint64Flag{
Name: "size",
Usage: "Chunk size in `bytes` to split the file.",
Aliases: []string{"chunk"},
},
&cli.UintFlag{
Name: "concurrency",
Usage: "Chunks that will be downloaded concurrently.",
Aliases: []string{"c"},
},
&cli.StringSliceFlag{
Name: "header",
Usage: `Set these HTTP-Headers on the requests. The format has to be: -H "Key: Value"`,
Aliases: []string{"H"},
},
&cli.StringFlag{
Name: "agent",
Usage: `Set user agent for got HTTP requests.`,
Aliases: []string{"u"},
},
},
Version: version,
Authors: []*cli.Author{
{
Name: "Mohamed Elbahja and Contributors",
Email: "bm9qdW5r@gmail.com",
},
},
Action: func(c *cli.Context) error {
return run(ctx, c)
},
}

if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}

func run(ctx context.Context, c *cli.Context) error {

var (
g *got.Got = got.NewWithContext(ctx)
p *progress.Progress = new(progress.Progress)
)

// Set progress style.
p.SetStyle(progressStyle)

// Progress func.
g.ProgressFunc = func(d *got.Download) {

// 55 is just an estimation of the text showed with the progress.
// it's working fine with $COLUMNS >= 47
p.Width = getWidth() - 55

perc, err := progress.GetPercentage(float64(d.Size()), float64(d.TotalSize()))
if err != nil {
perc = 100
}

var bar string
if getWidth() <= 46 {
bar = ""
} else {
bar = r + color(p.GetBar(perc, 100)) + l
}

fmt.Printf(
" %6.2f%% %s %s/%s @ %s/s%s\r",
perc,
bar,
humanize.Bytes(d.Size()),
humanize.Bytes(d.TotalSize()),
humanize.Bytes(d.Speed()),
ansi.ClearRight(),
)
}

info, err := os.Stdin.Stat()

if err != nil {
return err
}

// Create dir if not exists.
if c.String("dir") != "" {

if _, err := os.Stat(c.String("dir")); os.IsNotExist(err) {
os.MkdirAll(c.String("dir"), os.ModePerm)
}
}

// Set default user agent.
if c.String("agent") != "" {
got.UserAgent = c.String("agent")
}

// Piped stdin
if info.Mode()&os.ModeNamedPipe > 0 || info.Size() > 0 {

if err := multiDownload(ctx, c, g, bufio.NewScanner(os.Stdin)); err != nil {
return err
}
}

// Batch file.
if c.String("file") != "" {

file, err := os.Open(c.String("file"))

if err != nil {
return err
}

if err := multiDownload(ctx, c, g, bufio.NewScanner(file)); err != nil {
return err
}
}

if c.StringSlice("header") != nil {
header := c.StringSlice("header")

for _, h := range header {
split := strings.SplitN(h, ":", 2)
if len(split) == 1 {
return errors.New("malformatted header " + h)
}
HeaderSlice = append(HeaderSlice, got.GotHeader{Key: split[0], Value: strings.TrimSpace(split[1])})
}
}

// Download from args.
for _, url := range c.Args().Slice() {

if err = download(ctx, c, g, url); err != nil {
return err
}

fmt.Print(ansi.ClearLine())
fmt.Println(fmt.Sprintf("✔ %s", url))
}

return nil
}

func getWidth() int {

if width, _, err := terminal.GetSize(0); err == nil && width > 0 {
return width
}

return 80
}

func multiDownload(ctx context.Context, c *cli.Context, g *got.Got, scanner *bufio.Scanner) error {

for scanner.Scan() {

url := strings.TrimSpace(scanner.Text())

if url == "" {
continue
}

if err := download(ctx, c, g, url); err != nil {
return err
}

fmt.Print(ansi.ClearLine())
fmt.Println(fmt.Sprintf("✔ %s", url))
}

return nil
}

func download(ctx context.Context, c *cli.Context, g *got.Got, url string) (err error) {

if url, err = getURL(url); err != nil {
return err
}

return g.Do(&got.Download{
URL: url,
Dir: c.String("dir"),
Dest: c.String("output"),
Header: HeaderSlice,
Interval: 150,
ChunkSize: c.Uint64("size"),
Concurrency: c.Uint("concurrency"),
})
}

func getURL(URL string) (string, error) {

u, err := url.Parse(URL)

if err != nil {
return "", err
}

// Fallback to https by default.
if u.Scheme == "" {
u.Scheme = "https"
}

return u.String(), nil
}
19 changes: 19 additions & 0 deletions cmd/wgot/variables_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//go:build !windows
// +build !windows

package main

import (
"fmt"

"gitlab.com/poldi1405/go-ansi"
)

var (
progressStyle = "block"
r, l = "▕", "▏"
)

func color(content ...interface{}) string {
return ansi.Blue(fmt.Sprint(content...))
}
16 changes: 16 additions & 0 deletions cmd/wgot/variables_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//go:build windows
// +build windows

package main

import "fmt"

// Windows doesn't handle the block-style very well
var (
progressStyle = "double-"
r, l = "[", "]"
)

func color(content ...interface{}) string {
return fmt.Sprint(content...)
}