aboutsummaryrefslogtreecommitdiff
path: root/cmd/web
diff options
context:
space:
mode:
authorMitch Riedstra <mitch@riedstra.us>2021-01-09 15:22:27 -0500
committerMitch Riedstra <mitch@riedstra.us>2021-01-09 15:22:27 -0500
commit602790e2ca33ad7f22235bf2ae548cef7db8b814 (patch)
treef79fc9a7f6e019a3f2a774e13d030937b3ae9f86 /cmd/web
parente31c9168627c040317e5cc8566724f88910439ae (diff)
downloadsteam-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/web')
-rw-r--r--cmd/web/css.go16
-rw-r--r--cmd/web/download.go1
-rw-r--r--cmd/web/formatBytes.go29
-rw-r--r--cmd/web/index.go19
-rw-r--r--cmd/web/install.go57
-rw-r--r--cmd/web/js.go109
-rw-r--r--cmd/web/main.go1
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{