Skip to content
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
dl
/dl
*.part*
18 changes: 9 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ all: help
.PHONY: build
build:
@echo "Building $(BINARY_NAME)..."
@$(GO_BUILD) $(LDFLAGS) -o $(BINARY_NAME) .
@$(GO_BUILD) $(LDFLAGS) -o $(BINARY_NAME) ./cmd/dl

# Build for all platforms
.PHONY: build-all
Expand All @@ -34,21 +34,21 @@ build-all: build-linux build-darwin build-windows
.PHONY: build-linux
build-linux:
@echo "Building for Linux..."
@GOOS=linux GOARCH=amd64 $(GO_BUILD) $(LDFLAGS) -o $(BINARY_NAME)-linux-amd64 .
@GOOS=linux GOARCH=arm64 $(GO_BUILD) $(LDFLAGS) -o $(BINARY_NAME)-linux-arm64 .
@GOOS=linux GOARCH=amd64 $(GO_BUILD) $(LDFLAGS) -o $(BINARY_NAME)-linux-amd64 ./cmd/dl
@GOOS=linux GOARCH=arm64 $(GO_BUILD) $(LDFLAGS) -o $(BINARY_NAME)-linux-arm64 ./cmd/dl

# Build for macOS
.PHONY: build-darwin
build-darwin:
@echo "Building for macOS..."
@GOOS=darwin GOARCH=amd64 $(GO_BUILD) $(LDFLAGS) -o $(BINARY_NAME)-darwin-amd64 .
@GOOS=darwin GOARCH=arm64 $(GO_BUILD) $(LDFLAGS) -o $(BINARY_NAME)-darwin-arm64 .
@GOOS=darwin GOARCH=amd64 $(GO_BUILD) $(LDFLAGS) -o $(BINARY_NAME)-darwin-amd64 ./cmd/dl
@GOOS=darwin GOARCH=arm64 $(GO_BUILD) $(LDFLAGS) -o $(BINARY_NAME)-darwin-arm64 ./cmd/dl

# Build for Windows
.PHONY: build-windows
build-windows:
@echo "Building for Windows..."
@GOOS=windows GOARCH=amd64 $(GO_BUILD) $(LDFLAGS) -o $(BINARY_NAME)-windows-amd64.exe .
@GOOS=windows GOARCH=amd64 $(GO_BUILD) $(LDFLAGS) -o $(BINARY_NAME)-windows-amd64.exe ./cmd/dl

# Run tests
.PHONY: test
Expand Down Expand Up @@ -101,7 +101,7 @@ clean:
.PHONY: install
install:
@echo "Installing $(BINARY_NAME)..."
@$(GO) install $(LDFLAGS) .
@$(GO) install $(LDFLAGS) ./cmd/dl
@echo "Installed to $(shell go env GOPATH)/bin/$(BINARY_NAME)"
@if ! echo $$PATH | grep -q "$(shell go env GOPATH)/bin"; then \
echo ""; \
Expand Down Expand Up @@ -136,7 +136,7 @@ test-resume: build
.PHONY: dev
dev:
@echo "Building with race detector..."
@$(GO_BUILD) -race $(LDFLAGS) -o $(BINARY_NAME) .
@$(GO_BUILD) -race $(LDFLAGS) -o $(BINARY_NAME) ./cmd/dl
@echo "Ready for development testing"

# Show help
Expand All @@ -158,4 +158,4 @@ help:
@echo " make test-download - Test download functionality"
@echo " make test-resume - Test resume functionality"
@echo " make dev - Build with race detector for development"
@echo " make help - Show this help message"
@echo " make help - Show this help message"
40 changes: 39 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Requires Go 1.23+
```bash
git clone https://github.com/mgomes/dl.git
cd dl
go build
go build -o dl ./cmd/dl
```

## Usage
Expand All @@ -33,6 +33,44 @@ go build
dl <url> [url2] [url3] ...
```

## Library usage

```go
package main

import (
"context"
"fmt"
"os"

"github.com/mgomes/dl"
)

func main() {
ctx := context.Background()
downloader := dl.Downloader{
URI: "https://example.com/file.zip",
Context: ctx,
WorkingDir: ".",
Boost: 4,
Retries: 3,
Resume: true,
}

if err := downloader.FetchMetadata(); err != nil {
fmt.Printf("metadata error: %v\n", err)
os.Exit(1)
}

if err := downloader.Fetch(); err != nil {
fmt.Printf("download error: %v\n", err)
os.Exit(1)
}

fmt.Println("saved to", downloader.OutputPath())
}
```

### Options

```
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
42 changes: 22 additions & 20 deletions main.go → cmd/dl/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"os"
"os/signal"
"syscall"

"github.com/mgomes/dl"
)

func main() {
Expand Down Expand Up @@ -52,54 +54,54 @@ func main() {
}()

for _, uri := range fileURIs {
var dl download
dl.uri = uri
dl.boost = *boostPtr
dl.retries = *retriesPtr
dl.resume = *resumePtr && !*noResumePtr
dl.bandwidthLimit = bandwidthLimit
dl.ctx = ctx

if err := dl.FetchMetadata(); err != nil {
downloader := dl.Downloader{
URI: uri,
Boost: *boostPtr,
Retries: *retriesPtr,
Resume: *resumePtr && !*noResumePtr,
BandwidthLimit: bandwidthLimit,
Context: ctx,
}

if err := downloader.FetchMetadata(); err != nil {
fmt.Fprintf(os.Stderr, "Error fetching metadata for %s: %v\n", uri, err)
os.Exit(1)
}

if *filenamePtr != "" {
dl.filename = *filenamePtr
downloader.Filename = *filenamePtr
}

wd, err := os.Getwd()
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting working directory: %v\n", err)
os.Exit(1)
}
dl.workingDir = wd
downloader.WorkingDir = wd

fmt.Println("Downloading:", dl.filename)
fmt.Println("Downloading:", downloader.Filename)

if !dl.supportsRange && dl.boost > 1 {
if !downloader.SupportsRange() && downloader.Boost > 1 {
fmt.Println("Server does not support partial content. Falling back to single-threaded download.")
dl.boost = 1
downloader.Boost = 1
}

if err := dl.Fetch(); err != nil {
downloader.Progress = newProgressBarReporter(downloader.FileSize())

if err := downloader.Fetch(); err != nil {
if errors.Is(err, context.Canceled) {
fmt.Println("\nDownload cancelled")
os.Exit(1)
}
fmt.Fprintf(os.Stderr, "Error while downloading: %v\n", err)
if dl.progress == nil {
_ = os.Remove(dl.outputPath())
}
os.Exit(1)
}

fmt.Println("Download completed:", dl.filename)
fmt.Println("Download completed:", downloader.Filename)

if *checksumPtr != "" {
fmt.Printf("Verifying checksum...")
if err := verifyChecksum(dl.outputPath(), *checksumPtr); err != nil {
if err := verifyChecksum(downloader.OutputPath(), *checksumPtr); err != nil {
fmt.Fprintf(os.Stderr, "\nChecksum verification failed: %v\n", err)
os.Exit(1)
}
Expand Down
42 changes: 42 additions & 0 deletions cmd/dl/progress.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package main

import "github.com/schollz/progressbar/v3"

type progressBarReporter struct {
bar *progressbar.ProgressBar
}

func newProgressBarReporter(total uint64) *progressBarReporter {
return &progressBarReporter{
bar: progressbar.DefaultBytes(int64(total), "Downloading"),
}
}

func (p *progressBarReporter) SetTotal(total uint64) {
if p.bar == nil {
p.bar = progressbar.DefaultBytes(int64(total), "Downloading")
return
}
p.bar.ChangeMax64(int64(total))
}

func (p *progressBarReporter) SetDownloaded(downloaded uint64) {
if p.bar == nil {
return
}
_ = p.bar.Set64(int64(downloaded))
}

func (p *progressBarReporter) AddDownloaded(delta uint64) {
if p.bar == nil {
return
}
_ = p.bar.Add64(int64(delta))
}

func (p *progressBarReporter) Done() {
if p.bar == nil {
return
}
_ = p.bar.Finish()
}
Loading