diff options
| author | Mitch Riedstra <mitch@riedstra.us> | 2021-01-09 15:22:27 -0500 |
|---|---|---|
| committer | Mitch Riedstra <mitch@riedstra.us> | 2021-01-09 15:22:27 -0500 |
| commit | 602790e2ca33ad7f22235bf2ae548cef7db8b814 (patch) | |
| tree | f79fc9a7f6e019a3f2a774e13d030937b3ae9f86 /cmd | |
| parent | e31c9168627c040317e5cc8566724f88910439ae (diff) | |
| download | steam-export-602790e2ca33ad7f22235bf2ae548cef7db8b814.tar.gz steam-export-602790e2ca33ad7f22235bf2ae548cef7db8b814.tar.xz | |
Add a progress bar to the UI for installation via HTTP. Uses polling, but whatever.
Diffstat (limited to 'cmd')
| -rw-r--r-- | cmd/web/css.go | 16 | ||||
| -rw-r--r-- | cmd/web/download.go | 1 | ||||
| -rw-r--r-- | cmd/web/formatBytes.go | 29 | ||||
| -rw-r--r-- | cmd/web/index.go | 19 | ||||
| -rw-r--r-- | cmd/web/install.go | 57 | ||||
| -rw-r--r-- | cmd/web/js.go | 109 | ||||
| -rw-r--r-- | cmd/web/main.go | 1 |
7 files changed, 217 insertions, 15 deletions
diff --git a/cmd/web/css.go b/cmd/web/css.go index 3b772de..ec7267a 100644 --- a/cmd/web/css.go +++ b/cmd/web/css.go @@ -72,6 +72,22 @@ h1,h2,h3 { } } + +#status { + width: 100%; + background-color: #ddd; +} + +.installBar { + width: 0%; + height: 30px; + /* background-color: #4CAF50; */ + background-color: #268bd2; + text-align: center; + line-height: 30px; + color: white; +} + `)) if err != nil { diff --git a/cmd/web/download.go b/cmd/web/download.go index 1c70717..a47d88c 100644 --- a/cmd/web/download.go +++ b/cmd/web/download.go @@ -38,7 +38,6 @@ func gameDownloader(w http.ResponseWriter, r *http.Request) { }() var total int64 - start := time.Now() for { n, err := io.CopyN(w, rdr, 256*1024*1024) diff --git a/cmd/web/formatBytes.go b/cmd/web/formatBytes.go new file mode 100644 index 0000000..d5d2aab --- /dev/null +++ b/cmd/web/formatBytes.go @@ -0,0 +1,29 @@ +package main + +import ( + "fmt" + "math" +) + +func formatBytes(b int64) string { + if b < 1024 { + return fmt.Sprintf("%d b", b) + } + + s := "" + + pfxs := "kmgt" + for i := 0; i < len(pfxs); i++ { + pow := math.Pow(float64(1024), float64(i+1)) + // This one is too big, return the previous string + if b < int64(pow) { + return s + } + s = fmt.Sprintf("%.2f %cb", + float64(b)/(pow), + pfxs[i]) + } + + return s +} + diff --git a/cmd/web/index.go b/cmd/web/index.go index 6229f71..970810f 100644 --- a/cmd/web/index.go +++ b/cmd/web/index.go @@ -18,6 +18,8 @@ var ( <title>Steam Game index</title> </head> <body> + + <nav> <a href="/">Home</a> {{ if .Local }} @@ -28,19 +30,16 @@ var ( </nav> {{ if .Local }} +<script src="/main.js"></script> <h2>Library: {{.Lib.Folder}}</h2> -{{ if .Info.Running }} -<pre><code> -Currently Downloading from: {{.Info.Url}} -</pre></code> -{{ end }} +<div id="status"> + <div id="installBar" class="installBar" style="display: none;">0%</div> +</div> -{{ if .Info.Error }} -<pre><code> -Error {{.Info.Error}} Downloading from: {{.Info.Url}} -{{ end }} -</pre></code> +<pre><code id="message" style="display: none;"> +</code></pre> +<br /> <h3>About</h3> <p> diff --git a/cmd/web/install.go b/cmd/web/install.go index 678037a..d9a2379 100644 --- a/cmd/web/install.go +++ b/cmd/web/install.go @@ -3,9 +3,11 @@ package main import ( "encoding/json" "fmt" + "io" "net/http" "net/url" "os" + "strconv" "strings" "sync" "time" @@ -36,6 +38,8 @@ func statsHandler(w http.ResponseWriter, r *http.Request) { status.m.RLock() defer status.m.RUnlock() + w.Header().Add("Content-type", "application/json") + enc := json.NewEncoder(w) err := enc.Encode(status.s) @@ -52,12 +56,57 @@ func installHttp(u string) error { return fmt.Errorf("Installer: getting %w", err) } - err = Lib.Extract(resp.Body) + estSize, err := strconv.ParseInt(resp.Header.Get("Estimated-size"), 10, 64) if err != nil { - return fmt.Errorf("Installer: extracting %w", err) + return fmt.Errorf("Failed to convert estimated size header: %w", err) } - resp.Body.Close() - return nil + + status.m.Lock() + status.s.Size = estSize + status.m.Unlock() + + rdr, wrtr := io.Pipe() + + go func() { + err = Lib.Extract(rdr) + if err != nil { + Logger.Printf("Installer: extracting %w", err) + } + resp.Body.Close() + }() + + var total int64 + start := time.Now() + status.m.Lock() + status.s.Start = &start + status.m.Unlock() + for { + var n int64 + n, err = io.CopyN(wrtr, resp.Body, 100*1024*1024) + if err == io.EOF { + break + } else if err != nil { + Logger.Printf("Error encountered read from response body in installer: %s", err) + break + } + + total += n + mb := float64(total / 1024 / 1024) + rate := mb / time.Since(start).Seconds() + + Logger.Printf("Downloading from %s, Size: %s, %0.1f%% Done, Rate: %.2f mb/s", + u, formatBytes(estSize), float64(total)/float64(estSize)*100, rate) + + status.m.Lock() + status.s.Transferred = total + status.m.Unlock() + } + + if err == io.EOF { + return nil + } + + return err } func installPath(p string) error { diff --git a/cmd/web/js.go b/cmd/web/js.go new file mode 100644 index 0000000..0d54fab --- /dev/null +++ b/cmd/web/js.go @@ -0,0 +1,109 @@ +package main + +import ( + // "io/ioutil" + "net/http" +) + +func jsHandler(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Content-type", "application/javascript") + _, err := w.Write([]byte(` +// pretty print duration when given in seconds +function formatDuration(dur) { + var out = ""; + + var hours = 0; + var minutes = 0; + + if(dur > (60*60)){ + hours = Math.trunc(dur/(60*60)) + out = out + hours + " hours " + } + + if(dur > 60){ + minutes = Math.trunc((dur-(hours*60*60))/60) + out = out + minutes + " minutes " + } + + seconds = Math.trunc(dur - ( (hours*60*60) + (minutes*60) )) + out = out + seconds + " seconds " + + return out; +} + +function setStatus(stat) { + var elem = document.getElementById("installBar"); + var msg = document.getElementById("message"); + + // console.log(stat) + + if(stat.Running) { + percent = Math.round((stat.Transferred/stat.Size)*10000)/100; + + var t = Date.parse(stat.Start); + // var n = Date.now(); + var elapsed = Date.now() - t; + var trans = (stat.Transferred/1024/1024); + + console.log(trans) + console.log(elapsed / 1000) + + var rate = Math.round(trans / (elapsed / 1000)) + + elem.style.width = percent + "%"; + elem.innerHTML = percent + "%"; + elem.style.display = ""; + + // in seconds + var eta = Math.round(((stat.Size - trans)/1024/1024) / rate); + + msg.innerHTML = "Installing: " + stat.Url + + "\nRate: " + rate + "mb/s" + + "\nEta: " + formatDuration(eta); + msg.style.display = ""; + } + + if(!stat.Running && stat.Transferred >= 50 && stat.Error == null) { + elem.style.width = 100 + "%"; + elem.innerHTML = 100 + "%"; + msg.innerHTML = "Completed install for: " + stat.Url; + elem.style.display = ""; + msg.style.display = ""; + } + + if(stat.Error != null) { + msg.innerHTML = "Errors encountered while installing from: \n\n" + + stat.Url + "\n\n" + + JSON.stringify(stat.Error, undefined, 2); + elem.style.display = "hidden"; + msg.style.display = ""; + } +} + +function updateStatus() { +window.fetch('/status') + .then(function(response){ + return response.json(); + }).then(function(json){ + return setStatus(json); + }); +} + +document.addEventListener("DOMContentLoaded",function(){ + setInterval(updateStatus, 750); +}); +`)) + + /* + b, err := ioutil.ReadFile("C:\\Users\\mitch\\Documents\\my.js") + if err != nil { + Logger.Printf("While reading js: %s", err) + return + } + _, err = w.Write(b) + */ + + if err != nil { + Logger.Printf("While sending js: %s", err) + } +} diff --git a/cmd/web/main.go b/cmd/web/main.go index a16f15f..d926a20 100644 --- a/cmd/web/main.go +++ b/cmd/web/main.go @@ -155,6 +155,7 @@ func main() { r.HandleFunc("/download/{game}", gameDownloader) r.HandleFunc("/status", statsHandler) r.HandleFunc("/style.css", cssHandler) + r.HandleFunc("/main.js", jsHandler) r.HandleFunc("/", index) s := http.Server{ |
