Ready to implement a loading bar
This commit is contained in:
+118
-22
@@ -2,7 +2,11 @@ package page
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"math"
|
"math"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-gl/gl/v3.3-core/gl"
|
"github.com/go-gl/gl/v3.3-core/gl"
|
||||||
@@ -23,6 +27,87 @@ const (
|
|||||||
defaultProclamationPlayCount int = 1
|
defaultProclamationPlayCount int = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ProgressUpdate is sent over a channel as bytes are read from the network
|
||||||
|
// during a download. Read/Total let a consumer compute percentage; Done
|
||||||
|
// signals a clean finish; Err signals failure. The channel carrying these
|
||||||
|
// is closed after the final update (whether that's a Done or an Err), so
|
||||||
|
// ranging over it is sufficient to know when the download has finished.
|
||||||
|
type ProgressUpdate struct {
|
||||||
|
Read int64
|
||||||
|
Total int64
|
||||||
|
Done bool
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProgressReader wraps an io.Reader and emits a ProgressUpdate on every
|
||||||
|
// Read() call. It never blocks on the send: if nobody is currently
|
||||||
|
// listening on updates (or the consumer is behind), the update is dropped
|
||||||
|
// rather than stalling the download itself.
|
||||||
|
type ProgressReader struct {
|
||||||
|
Reader io.Reader
|
||||||
|
Total int64
|
||||||
|
read int64
|
||||||
|
updates chan<- ProgressUpdate
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pr *ProgressReader) Read(p []byte) (int, error) {
|
||||||
|
n, err := pr.Reader.Read(p)
|
||||||
|
if n > 0 {
|
||||||
|
pr.read += int64(n)
|
||||||
|
select {
|
||||||
|
case pr.updates <- ProgressUpdate{Read: pr.read, Total: pr.Total}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// downloadFile starts the download in its own goroutine and returns
|
||||||
|
// immediately with a channel of progress updates. The caller ranges over
|
||||||
|
// the returned channel to observe progress; the channel is closed when the
|
||||||
|
// download finishes, successfully or not, so the loop ends naturally.
|
||||||
|
func downloadFile(url, destPath string) <-chan ProgressUpdate {
|
||||||
|
updates := make(chan ProgressUpdate, 16)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer close(updates)
|
||||||
|
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
updates <- ProgressUpdate{Err: err}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
updates <- ProgressUpdate{Err: fmt.Errorf("bad status: %s", resp.Status)}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := os.Create(destPath)
|
||||||
|
if err != nil {
|
||||||
|
updates <- ProgressUpdate{Err: err}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer out.Close()
|
||||||
|
|
||||||
|
pr := &ProgressReader{
|
||||||
|
Reader: resp.Body,
|
||||||
|
Total: resp.ContentLength, // -1 if server didn't send it
|
||||||
|
updates: updates,
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := io.Copy(out, pr); err != nil {
|
||||||
|
updates <- ProgressUpdate{Err: err}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
updates <- ProgressUpdate{Read: pr.read, Total: pr.Total, Done: true}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return updates
|
||||||
|
}
|
||||||
|
|
||||||
type ScrollerProclamation struct {
|
type ScrollerProclamation struct {
|
||||||
db.Proclamation
|
db.Proclamation
|
||||||
colorPattern.ColorPattern
|
colorPattern.ColorPattern
|
||||||
@@ -260,31 +345,42 @@ func (p *ScrollerPage) Init(ctx *PageCtx) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
audioFilePath := p.Context.DbPtr.Find
|
var selectedAudio db.AudioFile
|
||||||
|
|
||||||
// Download if it doesn't exist
|
if err := p.Context.DbPtr.Where("Selected = ?", true).First(&selectedAudio).Error; err != nil {
|
||||||
fmt.Printf("%s", audioFilePath)
|
logger.Error("error querying database for selected audio: %v", err)
|
||||||
|
} else {
|
||||||
|
_, err = os.Stat(selectedAudio.Path)
|
||||||
|
if err != nil { // If err == nil, then the file was most likely not found
|
||||||
|
url := "https://files.radiantwavetech.com/audio/" + filepath.Base(selectedAudio.Path)
|
||||||
|
|
||||||
// // Check if the track has previously been downloaded
|
updates := downloadFile(url, selectedAudio.Path)
|
||||||
// // TODO: Download audio via a goroutine
|
var downloadErr error
|
||||||
// // this is a path
|
for u := range updates {
|
||||||
// // figure out how to get the filename, not the path, from the path.
|
if u.Err != nil {
|
||||||
// _, err = os.Stat(audioFilePath)
|
downloadErr = u.Err
|
||||||
// if err != nil {
|
break
|
||||||
// filename := filepath.Base(audioFilePath)
|
}
|
||||||
// url := "https://files.radiantwavetech.com/audio/" + filename
|
if u.Total > 0 {
|
||||||
// logger.Info("%s not present. Downloading...", entry.DisplayName)
|
pct := float64(u.Read) / float64(u.Total) * 100
|
||||||
// // Download the track if not, display loading bar
|
fmt.Printf("\rDownloaded %d / %d bytes (%.1f%%)", u.Read, u.Total, pct)
|
||||||
// if err = netreq.Download(filename, filepath.Join(p.Base.Context.CfgPtr.AssetsDir, "audio"), url); err != nil {
|
} else {
|
||||||
// logger.Error("Failed to download file: %v", err)
|
fmt.Printf("\rDownloaded %d bytes", u.Read)
|
||||||
// return
|
}
|
||||||
// }
|
if u.Done {
|
||||||
// logger.Info("Downloaded %s", entry.DisplayName)
|
fmt.Println()
|
||||||
// }
|
}
|
||||||
|
}
|
||||||
|
if downloadErr != nil {
|
||||||
|
return fmt.Errorf("failed to downoad audio file: %v", downloadErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if err := mixer.Play(audioFile, true, p.Context.CfgPtr); err != nil {
|
logger.Info("Playing %s", selectedAudio.Path)
|
||||||
// logger.Error("Mixer returned an error: %v", err)
|
if err := mixer.Play(selectedAudio.Path, true, p.Context.CfgPtr); err != nil {
|
||||||
// }
|
logger.Error("Mixer returned an error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
currentDevice := mixer.CurrentDevice()
|
currentDevice := mixer.CurrentDevice()
|
||||||
if currentDevice == "" {
|
if currentDevice == "" {
|
||||||
|
|||||||
Reference in New Issue
Block a user