github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/builtins/core/httpclient/getfile.go (about)

     1  package httpclient
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  	"net/url"
     8  	"os"
     9  	"strconv"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/lmorg/murex/builtins/pipes/file"
    14  	"github.com/lmorg/murex/lang"
    15  	"github.com/lmorg/murex/utils/ansi/codes"
    16  	"github.com/lmorg/murex/utils/humannumbers"
    17  	"github.com/lmorg/murex/utils/readline"
    18  )
    19  
    20  func cmdGetFile(p *lang.Process) (err error) {
    21  	if p.Parameters.Len() == 0 {
    22  		return errors.New("URL required")
    23  	}
    24  
    25  	url, err := p.Parameters.String(0)
    26  	if err != nil {
    27  		return err
    28  	}
    29  	validateURL(&url, p.Config)
    30  
    31  	var body io.Reader
    32  	if p.IsMethod {
    33  		body = p.Stdin
    34  	} else {
    35  		body = nil
    36  	}
    37  
    38  	resp, err := Request(p.Context, "GET", url, body, p.Config, disableTimeout)
    39  	if err != nil {
    40  		return err
    41  	}
    42  
    43  	p.Stdout.SetDataType(lang.MimeToMurex(resp.Header.Get("Content-Type")))
    44  
    45  	quit := make(chan bool)
    46  	cl := resp.Header.Get("Content-Length")
    47  	filename := extractFileName(url)
    48  	if p.Stdout.IsTTY() {
    49  		p.Stdout, err = file.NewFile(filename)
    50  		if err != nil {
    51  			return err
    52  		}
    53  		p.Stdout.Open()
    54  	}
    55  
    56  	var i int
    57  	if cl == "" {
    58  		cl = "{unknown}"
    59  	} else {
    60  		i, _ = strconv.Atoi(cl)
    61  		cl = humannumbers.Bytes(uint64(i))
    62  	}
    63  
    64  	defer func() {
    65  		quit <- true
    66  		resp.Body.Close()
    67  		written, _ := p.Stdout.Stats()
    68  
    69  		os.Stderr.WriteString(fmt.Sprintf(
    70  			"%sDownloaded %s to %s\n",
    71  			"\x1b["+strconv.Itoa(readline.GetTermWidth()+2)+"D"+codes.ClearLine+codes.Reset,
    72  			humannumbers.Bytes(written),
    73  			filename,
    74  		))
    75  	}()
    76  
    77  	go func() {
    78  		var last, written, speed uint64
    79  		select {
    80  		case <-quit:
    81  			return
    82  		default:
    83  		}
    84  
    85  		for {
    86  			if p.Stderr.IsTTY() {
    87  				time.Sleep(10 * time.Millisecond)
    88  				written, _ = p.Stdout.Stats()
    89  				speed = (written - last) * 100
    90  			} else {
    91  				time.Sleep(2 * time.Second)
    92  				written, _ = p.Stdout.Stats()
    93  				speed = (written - last) * 2000
    94  			}
    95  			last = written
    96  
    97  			select {
    98  			case <-quit:
    99  				return
   100  			default:
   101  			}
   102  
   103  			msg := fmt.Sprintf(
   104  				"Downloading... %s of %s @ %s/s....",
   105  				humannumbers.Bytes(written),
   106  				cl,
   107  				humannumbers.Bytes(speed),
   108  			)
   109  			printGaugeBar(float64(written), float64(i), msg)
   110  		}
   111  	}()
   112  
   113  	_, err = io.Copy(p.Stdout, resp.Body)
   114  	return err
   115  }
   116  
   117  func extractFileName(address string) string {
   118  	u, err := url.Parse(address)
   119  	if err != nil {
   120  		return address
   121  	}
   122  
   123  	if len(u.Path) == 0 || u.Path == "/" {
   124  		return u.Host
   125  	}
   126  
   127  	split := strings.Split(u.Path, "/")
   128  	for i := len(split) - 1; i > -1; i-- {
   129  		if len(split[i]) != 0 && split[i] != "/" {
   130  			return split[i]
   131  		}
   132  	}
   133  
   134  	return u.Path
   135  }
   136  
   137  func printGaugeBar(value, max float64, message string) {
   138  	width := readline.GetTermWidth()
   139  	cells := int((float64(width) / max) * value)
   140  
   141  	s := "\x1b[" + strconv.Itoa(width+2) + "D" + codes.ClearLine + codes.Reset
   142  	if cells > 0 {
   143  		s += codes.Invert
   144  	}
   145  
   146  	for i := 0; i < width; i++ {
   147  		if cells+1 == i {
   148  			s += codes.Reset
   149  		}
   150  
   151  		if i < len(message) {
   152  			s += string([]byte{message[i]})
   153  		} else {
   154  			s += " "
   155  		}
   156  	}
   157  
   158  	os.Stderr.WriteString(s + codes.Reset)
   159  }