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
totalToDownload int64
totalDownloaded int64
progressPerFile map[string]int64 // map of filename to downloaded bytes
progressMutex sync.Mutex
speedAverager *SpeedAverager
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() {
pw.cancelButton.SetSensitive(true)
pw.AddToTotalDownloaded(downloaded)
pw.bar.SetFraction(float64(pw.totalDownloaded) / float64(pw.totalToDownload))
pw.speedAverager.AddSpeed(calculateDownloadSpeed(pw.totalDownloaded, pw.startTime, time.Now()))
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())))))
pw.progressMutex.Lock()
pw.progressPerFile[filename] += downloaded
total := pw.totalDownloaded
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() {
gtk.MainIteration()
@ -121,15 +128,20 @@ func (pw *ProgressWindow) SetDownloadSize(size int64) {
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.totalDownloaded = total
pw.totalDownloaded += pw.progressPerFile[filename]
delete(pw.progressPerFile, filename)
pw.progressMutex.Unlock()
}
func (pw *ProgressWindow) AddToTotalDownloaded(toAdd int64) {
func (pw *ProgressWindow) SetTotalDownloadedForFile(filename string, downloaded int64) {
pw.progressMutex.Lock()
pw.totalDownloaded += toAdd
pw.progressPerFile[filename] = downloaded
pw.progressMutex.Unlock()
}

View file

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

View file

@ -10,16 +10,17 @@ type WriterProgress struct {
progressReporter ProgressReporter
updateProgressTicker *time.Ticker
downloadToReport int64 // Number of bytes to report to the progressReporter since the last update
filename string
}
func newWriterProgress(writer io.Writer, progressReporter ProgressReporter) *WriterProgress {
return &WriterProgress{writer: writer, progressReporter: progressReporter, updateProgressTicker: time.NewTicker(25 * time.Millisecond), downloadToReport: 0}
func newWriterProgress(writer io.Writer, progressReporter ProgressReporter, filename string) *WriterProgress {
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) {
select {
case <-r.updateProgressTicker.C:
r.progressReporter.UpdateDownloadProgress(r.downloadToReport)
r.progressReporter.UpdateDownloadProgress(r.downloadToReport, r.filename)
r.downloadToReport = 0
default:
}