Improve progress tracking and reporting

This commit is contained in:
Xpl0itU 2024-04-14 00:33:12 +02:00
parent 5eb05670ad
commit c69ede0ec5
3 changed files with 36 additions and 18 deletions

View file

@ -65,6 +65,7 @@ type ProgressWindow struct {
cancelled bool cancelled bool
totalToDownload int64 totalToDownload int64
totalDownloaded int64 totalDownloaded int64
progressPerFile map[string]int64 // map of filename to downloaded bytes
progressMutex sync.Mutex progressMutex sync.Mutex
speedAverager *SpeedAverager speedAverager *SpeedAverager
startTime time.Time startTime time.Time
@ -79,13 +80,19 @@ func (pw *ProgressWindow) SetGameTitle(title string) {
} }
} }
func (pw *ProgressWindow) UpdateDownloadProgress(downloaded int64) { func (pw *ProgressWindow) UpdateDownloadProgress(downloaded int64, filename string) {
glib.IdleAdd(func() { glib.IdleAdd(func() {
pw.cancelButton.SetSensitive(true) pw.cancelButton.SetSensitive(true)
pw.AddToTotalDownloaded(downloaded) pw.progressMutex.Lock()
pw.bar.SetFraction(float64(pw.totalDownloaded) / float64(pw.totalToDownload)) pw.progressPerFile[filename] += downloaded
pw.speedAverager.AddSpeed(calculateDownloadSpeed(pw.totalDownloaded, pw.startTime, time.Now())) total := pw.totalDownloaded
pw.bar.SetText(fmt.Sprintf("Downloading... (%s/%s) (%s/s)", humanize.Bytes(uint64(pw.totalDownloaded)), humanize.Bytes(uint64(pw.totalToDownload)), humanize.Bytes(uint64(int64(pw.speedAverager.GetAverageSpeed()))))) for _, v := range pw.progressPerFile {
total += v
}
pw.progressMutex.Unlock()
pw.bar.SetFraction(float64(total) / float64(pw.totalToDownload))
pw.speedAverager.AddSpeed(calculateDownloadSpeed(total, pw.startTime, time.Now()))
pw.bar.SetText(fmt.Sprintf("Downloading... (%s/%s) (%s/s)", humanize.Bytes(uint64(total)), humanize.Bytes(uint64(pw.totalToDownload)), humanize.Bytes(uint64(int64(pw.speedAverager.GetAverageSpeed())))))
}) })
for gtk.EventsPending() { for gtk.EventsPending() {
gtk.MainIteration() gtk.MainIteration()
@ -121,15 +128,20 @@ func (pw *ProgressWindow) SetDownloadSize(size int64) {
pw.totalToDownload = size pw.totalToDownload = size
} }
func (pw *ProgressWindow) SetTotalDownloaded(total int64) { func (pw *ProgressWindow) ResetTotalDownloaded() {
pw.progressPerFile = make(map[string]int64)
}
func (pw *ProgressWindow) MarkFileAsDone(filename string) {
pw.progressMutex.Lock() pw.progressMutex.Lock()
pw.totalDownloaded = total pw.totalDownloaded += pw.progressPerFile[filename]
delete(pw.progressPerFile, filename)
pw.progressMutex.Unlock() pw.progressMutex.Unlock()
} }
func (pw *ProgressWindow) AddToTotalDownloaded(toAdd int64) { func (pw *ProgressWindow) SetTotalDownloadedForFile(filename string, downloaded int64) {
pw.progressMutex.Lock() pw.progressMutex.Lock()
pw.totalDownloaded += toAdd pw.progressPerFile[filename] = downloaded
pw.progressMutex.Unlock() pw.progressMutex.Unlock()
} }

View file

@ -28,13 +28,14 @@ var (
type ProgressReporter interface { type ProgressReporter interface {
SetGameTitle(title string) SetGameTitle(title string)
UpdateDownloadProgress(downloaded int64) UpdateDownloadProgress(downloaded int64, filename string)
UpdateDecryptionProgress(progress float64) UpdateDecryptionProgress(progress float64)
Cancelled() bool Cancelled() bool
SetCancelled() SetCancelled()
SetDownloadSize(size int64) SetDownloadSize(size int64)
SetTotalDownloaded(total int64) ResetTotalDownloaded()
AddToTotalDownloaded(toAdd int64) MarkFileAsDone(filename string)
SetTotalDownloadedForFile(filename string, downloaded int64)
SetStartTime(startTime time.Time) SetStartTime(startTime time.Time)
} }
@ -44,6 +45,8 @@ func downloadFileWithSemaphore(ctx context.Context, progressReporter ProgressRep
} }
defer sem.Release(1) defer sem.Release(1)
basePath := filepath.Base(dstPath)
for attempt := 1; attempt <= maxRetries; attempt++ { for attempt := 1; attempt <= maxRetries; attempt++ {
req, err := http.NewRequestWithContext(ctx, "GET", downloadURL, nil) req, err := http.NewRequestWithContext(ctx, "GET", downloadURL, nil)
if err != nil { if err != nil {
@ -77,7 +80,8 @@ func downloadFileWithSemaphore(ctx context.Context, progressReporter ProgressRep
return err return err
} }
writerProgress := newWriterProgress(file, progressReporter) progressReporter.SetTotalDownloadedForFile(basePath, 0)
writerProgress := newWriterProgress(file, progressReporter, basePath)
_, err = io.Copy(writerProgress, resp.Body) _, err = io.Copy(writerProgress, resp.Body)
if err != nil { if err != nil {
file.Close() file.Close()
@ -92,6 +96,7 @@ func downloadFileWithSemaphore(ctx context.Context, progressReporter ProgressRep
file.Close() file.Close()
resp.Body.Close() resp.Body.Close()
writerProgress.Close() writerProgress.Close()
progressReporter.MarkFileAsDone(basePath)
break break
} }
@ -131,7 +136,7 @@ func downloadFile(progressReporter ProgressReporter, client *http.Client, downlo
return err return err
} }
writerProgress := newWriterProgress(file, progressReporter) writerProgress := newWriterProgress(file, progressReporter, filepath.Base(dstPath))
_, err = io.Copy(writerProgress, resp.Body) _, err = io.Copy(writerProgress, resp.Body)
if err != nil { if err != nil {
file.Close() file.Close()
@ -153,7 +158,7 @@ func downloadFile(progressReporter ProgressReporter, client *http.Client, downlo
func DownloadTitle(titleID, outputDirectory string, doDecryption bool, progressReporter ProgressReporter, deleteEncryptedContents bool, logger *Logger, client *http.Client) error { func DownloadTitle(titleID, outputDirectory string, doDecryption bool, progressReporter ProgressReporter, deleteEncryptedContents bool, logger *Logger, client *http.Client) error {
tEntry := getTitleEntryFromTid(titleID) tEntry := getTitleEntryFromTid(titleID)
progressReporter.SetTotalDownloaded(0) progressReporter.ResetTotalDownloaded()
progressReporter.SetGameTitle(tEntry.Name) progressReporter.SetGameTitle(tEntry.Name)
outputDir := strings.TrimRight(outputDirectory, "/\\") outputDir := strings.TrimRight(outputDirectory, "/\\")

View file

@ -10,16 +10,17 @@ type WriterProgress struct {
progressReporter ProgressReporter progressReporter ProgressReporter
updateProgressTicker *time.Ticker updateProgressTicker *time.Ticker
downloadToReport int64 // Number of bytes to report to the progressReporter since the last update downloadToReport int64 // Number of bytes to report to the progressReporter since the last update
filename string
} }
func newWriterProgress(writer io.Writer, progressReporter ProgressReporter) *WriterProgress { func newWriterProgress(writer io.Writer, progressReporter ProgressReporter, filename string) *WriterProgress {
return &WriterProgress{writer: writer, progressReporter: progressReporter, updateProgressTicker: time.NewTicker(25 * time.Millisecond), downloadToReport: 0} return &WriterProgress{writer: writer, progressReporter: progressReporter, updateProgressTicker: time.NewTicker(25 * time.Millisecond), downloadToReport: 0, filename: filename}
} }
func (r *WriterProgress) Write(p []byte) (n int, err error) { func (r *WriterProgress) Write(p []byte) (n int, err error) {
select { select {
case <-r.updateProgressTicker.C: case <-r.updateProgressTicker.C:
r.progressReporter.UpdateDownloadProgress(r.downloadToReport) r.progressReporter.UpdateDownloadProgress(r.downloadToReport, r.filename)
r.downloadToReport = 0 r.downloadToReport = 0
default: default:
} }