Skip to content
This repository has been archived by the owner on Jun 3, 2022. It is now read-only.

Commit

Permalink
fix: use custom simple downloader, fixes various quirks
Browse files Browse the repository at this point in the history
Allows the remote file Content-Length to mismatch the local file's
size, which will trigger a re-download.
Displays progress in real-time.
  • Loading branch information
ViRb3 committed Nov 22, 2020
1 parent c37e9f9 commit 6e6b765
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 135 deletions.
18 changes: 8 additions & 10 deletions util/archive.go → archive/archive.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package util
package archive

import (
"archive/tar"
"archive/zip"
"bytes"
"github.com/cavaliercoder/grab"
"cornstone/downloader"
"cornstone/util"
"github.com/h2non/filetype"
"github.com/h2non/filetype/matchers"
"github.com/mholt/archiver/v3"
Expand Down Expand Up @@ -89,7 +90,7 @@ func processFile(file archiver.File, config ExtractCommonConfig) error {
fullName = fullName[firstPathIndex+1:]
}
}
fullName = SafeJoin(config.DestPath, fullName)
fullName = util.SafeJoin(config.DestPath, fullName)
if file.IsDir() {
if err := os.MkdirAll(fullName, file.Mode()); err != nil {
return err
Expand Down Expand Up @@ -161,7 +162,7 @@ func ExtractArchiveFromReader(config ExtractReaderConfig) error {
}

func DownloadAndExtract(downloadUrl string, logger *log.Logger, config ExtractCommonConfig) error {
tempFile, err := TempFile()
tempFile, err := util.TempFile()
if err != nil {
return err
}
Expand All @@ -171,15 +172,12 @@ func DownloadAndExtract(downloadUrl string, logger *log.Logger, config ExtractCo
}
defer os.Remove(tempFilePath)

request, err := grab.NewRequest(tempFilePath, downloadUrl)
if err != nil {
return err
}
logger.Println("Downloading...")
result, cancelFunc := NewMultiDownloader(1, request).Do()
request := downloader.Request{tempFilePath, downloadUrl, nil}
result, cancelFunc := downloader.NewMultiDownloader(1, request).Do()
defer cancelFunc()
for resp := range result {
if err := resp.Err(); err != nil {
if err := resp.Err; err != nil {
return err
}
}
Expand Down
5 changes: 3 additions & 2 deletions cmd/manifest/format/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"cornstone/aliases/e"
"cornstone/curseforge"
"cornstone/throttler"
"cornstone/util"
"encoding/json"
"fmt"
Expand Down Expand Up @@ -55,7 +56,7 @@ func execute() error {
}

ctx, cancelFunc := context.WithCancel(context.Background())
throttler := util.NewThrottler(util.ThrottlerConfig{
addonThrottler := throttler.NewThrottler(throttler.Config{
Ctx: ctx,
ResultBuffer: 10,
Workers: concurrentCount,
Expand Down Expand Up @@ -93,7 +94,7 @@ func execute() error {
})

log.Println("Querying addons...")
for result := range throttler.Run() {
for result := range addonThrottler.Run() {
if result.Error != nil {
cancelFunc()
return e.S(result.Error)
Expand Down
5 changes: 3 additions & 2 deletions cmd/multimc/initialize/initialize.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package initialize

import (
"cornstone/aliases/e"
"cornstone/archive"
"cornstone/multimc"
"cornstone/util"
"fmt"
Expand Down Expand Up @@ -49,7 +50,7 @@ func execute() error {
logger := log.New(os.Stderr, "", log.LstdFlags)

log.Println("Obtaining MultiMC...")
if err := util.DownloadAndExtract(downloadUrl, logger, util.ExtractCommonConfig{
if err := archive.DownloadAndExtract(downloadUrl, logger, archive.ExtractCommonConfig{
BasePath: "",
DestPath: multimcPath,
Unwrap: true,
Expand All @@ -63,7 +64,7 @@ func execute() error {
if err := os.MkdirAll(javaPath, 0777); err != nil {
return e.S(err)
}
if err := util.DownloadAndExtract(profile.JavaUrl, logger, util.ExtractCommonConfig{
if err := archive.DownloadAndExtract(profile.JavaUrl, logger, archive.ExtractCommonConfig{
BasePath: "",
DestPath: javaPath,
Unwrap: true,
Expand Down
50 changes: 21 additions & 29 deletions curseforge/installer.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ package curseforge
import (
"context"
"cornstone/aliases/e"
"cornstone/archive"
"cornstone/downloader"
"cornstone/multimc"
"cornstone/throttler"
"cornstone/util"
"encoding/json"
"fmt"
"github.com/cavaliercoder/grab"
"io/ioutil"
"log"
"os"
Expand Down Expand Up @@ -128,15 +130,12 @@ func (i *ModpackInstaller) processForgeServer(manifest *CornManifest, destPath s
downloadUrl := fmt.Sprintf("https://files.minecraftforge.net/maven/net/minecraftforge/forge/%s/%s", fullVersion, forgeName)
savePath := filepath.Join(destPath, forgeName)

request, err := grab.NewRequest(savePath, downloadUrl)
if err != nil {
return err
}
log.Println("Downloading Forge installer...")
result, cancelFunc := util.NewMultiDownloader(i.ConcurrentCount, request).Do()
request := downloader.Request{savePath, downloadUrl, nil}
result, cancelFunc := downloader.NewMultiDownloader(i.ConcurrentCount, request).Do()
defer cancelFunc()
for resp := range result {
if err := resp.Err(); err != nil {
if err := resp.Err; err != nil {
return err
}
}
Expand Down Expand Up @@ -186,7 +185,7 @@ func (i *ModpackInstaller) processMods(manifest *CornManifest, destPath string)
}

ctx, cancelFunc := context.WithCancel(context.Background())
throttler := util.NewThrottler(util.ThrottlerConfig{
addonThrottler := throttler.NewThrottler(throttler.Config{
Ctx: ctx,
ResultBuffer: 10,
Workers: i.ConcurrentCount,
Expand All @@ -200,8 +199,8 @@ func (i *ModpackInstaller) processMods(manifest *CornManifest, destPath string)

log.Println("Building addon download URLs...")
downloadPaths := map[string]bool{}
var requests []*grab.Request
for result := range throttler.Run() {
var requests []downloader.Request
for result := range addonThrottler.Run() {
if result.Error != nil {
cancelFunc()
return result.Error
Expand All @@ -213,10 +212,7 @@ func (i *ModpackInstaller) processMods(manifest *CornManifest, destPath string)
downloadPath += ".disabled"
}
downloadPaths[downloadPath] = true
request, err := grab.NewRequest(downloadPath, opResult.downloadUrl)
if err != nil {
return err
}
request := downloader.Request{downloadPath, opResult.downloadUrl, nil}
requests = append(requests, request)
}

Expand All @@ -243,11 +239,7 @@ func (i *ModpackInstaller) processMods(manifest *CornManifest, destPath string)
}
downloadPaths[downloadPath] = true
}
request, err := grab.NewRequest(downloadPath, file.Url)
if err != nil {
return err
}
request.Tag = file
request := downloader.Request{downloadPath, file.Url, file}
requests = append(requests, request)
}

Expand Down Expand Up @@ -275,11 +267,11 @@ func (i *ModpackInstaller) processMods(manifest *CornManifest, destPath string)
}

log.Println("Obtaining files...")
result, cancelFunc := util.NewMultiDownloader(i.ConcurrentCount, requests...).Do()
results, cancelFunc := downloader.NewMultiDownloader(i.ConcurrentCount, requests...).Do()
defer cancelFunc()
for resp := range result {
request := resp.Request
if err := resp.Err(); err != nil {
for result := range results {
request := result.Request
if err := result.Err; err != nil {
if file, ok := request.Tag.(ExternalFile); ok {
log.Println("error downloading external file: " + file.Name)
if file.Required {
Expand All @@ -297,9 +289,9 @@ func (i *ModpackInstaller) processMods(manifest *CornManifest, destPath string)
if file, ok := request.Tag.(ExternalFile); ok && file.Extract.Enable && file.Required {
extractPath := util.SafeJoin(destPath, file.InstallPath)
log.Printf("Extracting external file '%s'...\n", file.Name)
if err := util.ExtractArchiveFromFile(util.ExtractFileConfig{
ArchivePath: request.Filename,
Common: util.ExtractCommonConfig{
if err := archive.ExtractArchiveFromFile(archive.ExtractFileConfig{
ArchivePath: result.Request.DownloadPath,
Common: archive.ExtractCommonConfig{
BasePath: "",
DestPath: extractPath,
Unwrap: file.Extract.Unwrap,
Expand Down Expand Up @@ -403,7 +395,7 @@ func (i *ModpackInstaller) stageModpack(stagingPath string) error {
if _, err := os.Stat(i.Input); err != nil {
log.Println("Obtaining modpack...")
logger := log.New(os.Stderr, "", log.LstdFlags)
if err := util.DownloadAndExtract(i.Input, logger, util.ExtractCommonConfig{
if err := archive.DownloadAndExtract(i.Input, logger, archive.ExtractCommonConfig{
BasePath: "",
DestPath: stagingPath,
Unwrap: false,
Expand All @@ -412,9 +404,9 @@ func (i *ModpackInstaller) stageModpack(stagingPath string) error {
}
} else {
log.Println("Extracting modpack...")
if err := util.ExtractArchiveFromFile(util.ExtractFileConfig{
if err := archive.ExtractArchiveFromFile(archive.ExtractFileConfig{
ArchivePath: i.Input,
Common: util.ExtractCommonConfig{
Common: archive.ExtractCommonConfig{
BasePath: "",
DestPath: stagingPath,
Unwrap: false,
Expand Down
100 changes: 100 additions & 0 deletions downloader/downloadear.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package downloader

import (
"context"
"cornstone/throttler"
"cornstone/util"
"github.com/ViRb3/sling/v2"
"io"
"net/http"
"os"
"strconv"
)

type Request struct {
DownloadPath string
DownloadUrl string
Tag interface{}
}

type Result struct {
Response *http.Response
Request Request
Err error
}

type MultiDownloader struct {
requests []Request
workers int
client *sling.Sling
skipExisting bool
}

func NewMultiDownloader(workers int, requests ...Request) *MultiDownloader {
downloader := MultiDownloader{
requests: requests,
workers: workers,
client: util.DefaultClient.New(),
}
return &downloader
}

func (s *MultiDownloader) Do() (<-chan Result, context.CancelFunc) {
ctx, cancelFunc := context.WithCancel(context.Background())
var source []interface{}
for i := range s.requests {
source = append(source, s.requests[i])
}

downloadThrottler := throttler.NewThrottler(throttler.Config{
Ctx: ctx,
ResultBuffer: 0,
Workers: s.workers,
Source: source,
Operation: func(sourceItem interface{}) (interface{}, error) {
request := sourceItem.(Request)

resp, err := s.client.New().Head(request.DownloadUrl).Receive(nil, nil)
if err != nil {
return Result{Err: err}, nil
}
if contentLen := resp.Header.Get("Content-Length"); contentLen != "" {
contentLenInt, err := strconv.ParseInt(contentLen, 10, 64)
if err == nil {
if stat, err := os.Stat(request.DownloadPath); err == nil && stat.Size() == contentLenInt {
return Result{nil, request, nil}, nil
}
}
}
resp, err = s.client.New().Get(request.DownloadUrl).ReceiveBody()
if err != nil {
return Result{Err: err}, nil
}
defer resp.Body.Close()
file, err := os.Create(request.DownloadPath)
if err != nil {
return Result{Err: err}, nil
}
defer file.Close()
if _, err := io.Copy(file, resp.Body); err != nil {
os.Remove(file.Name())
return Result{Err: err}, nil
}
return Result{resp, request, nil}, nil
},
})

returnResult := make(chan Result, 10)
go s.handleMultiFile(returnResult, downloadThrottler)
return returnResult, cancelFunc
}

func (s *MultiDownloader) handleMultiFile(returnResult chan Result, downloadThrottler *throttler.Throttler) {
defer close(returnResult)
bar := util.NewBar(len(s.requests))
defer bar.Finish()
for response := range downloadThrottler.Run() {
returnResult <- response.Data.(Result)
bar.Add(1)
}
}
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ go 1.15

require (
github.com/ViRb3/sling/v2 v2.0.1
github.com/cavaliercoder/grab v1.0.1-0.20201108051000-98a5bfe305ec
github.com/h2non/filetype v1.1.0
github.com/mholt/archiver/v3 v3.3.2
github.com/pkg/errors v0.9.1
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/bombsimon/wsl/v3 v3.1.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc=
github.com/cavaliercoder/grab v1.0.1-0.20201108051000-98a5bfe305ec h1:4XvMn0XuV7qxCH22gbnR79r+xTUaLOSA0GW/egpO3SQ=
github.com/cavaliercoder/grab v1.0.1-0.20201108051000-98a5bfe305ec/go.mod h1:NbXoa59CCAGqtRm7kRrcZIk2dTCJMRVF8QI3BOD7isY=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
Expand Down
11 changes: 6 additions & 5 deletions util/throttler.go → throttler/throttler.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package util
package throttler

import (
"context"
"cornstone/util"
"sync"
)

Expand All @@ -11,18 +12,18 @@ type Result struct {
}

type Throttler struct {
ThrottlerConfig
Config
}

type ThrottlerConfig struct {
type Config struct {
Ctx context.Context
ResultBuffer int
Workers int
Source []interface{}
Operation func(sourceItem interface{}) (interface{}, error)
}

func NewThrottler(config ThrottlerConfig) *Throttler {
func NewThrottler(config Config) *Throttler {
return &Throttler{
config,
}
Expand All @@ -33,7 +34,7 @@ func (t *Throttler) Run() <-chan Result {
resultChan := make(chan Result, t.ResultBuffer)
wg := sync.WaitGroup{}

bar := NewBar(len(t.Source))
bar := util.NewBar(len(t.Source))
wg.Add(len(t.Source))

go func() {
Expand Down
Loading

0 comments on commit 6e6b765

Please sign in to comment.