88 "fmt"
99 "fyne.io/fyne/v2/dialog"
1010 "github.com/cavaliergopher/grab/v3"
11+ "github.com/dustin/go-humanize"
1112 "hash/crc32"
1213 "os"
1314 "path/filepath"
@@ -20,6 +21,7 @@ type Update struct {
2021 IndexFile * IndexedFile
2122 Retry bool
2223 RemoveTakenFlag bool
24+ Failure error
2325 Progress float64
2426 Bytes int64
2527 Done bool
@@ -74,6 +76,10 @@ func (d *Downloader) Resume() error {
7476 // Reset context
7577 d .ctx , d .cancel = context .WithCancel (context .Background ())
7678
79+ // Reset failure count
80+ d .state .downloadFailures = 0
81+ _ = d .state .formatDownloadFailures .Set ("0" )
82+
7783 // Set up background
7884 d .reqch = make (chan * grab.Request , 10 )
7985 d .respch = make (chan * grab.Response , 4 )
@@ -121,19 +127,31 @@ func (d *Downloader) Resume() error {
121127 IndexFile : f ,
122128 Retry : false ,
123129 RemoveTakenFlag : true ,
130+ Failure : nil ,
124131 Progress : 1 ,
125132 Bytes : 0 ,
126133 Done : true ,
127134 }
128135 return
129136 } else {
130- // Bad download, retry if below 3 retries
131- if f .RetryCount < 3 {
137+ // Bad download, retry if below 5 retries
138+ if f .RetryCount < 5 {
132139 f .RetryCount += 1
133140 d .updatech <- & Update {
134141 IndexFile : f ,
135142 Retry : true ,
136143 RemoveTakenFlag : false ,
144+ Failure : nil ,
145+ Progress : 1 ,
146+ Bytes : 0 ,
147+ Done : true ,
148+ }
149+ } else {
150+ d .updatech <- & Update {
151+ IndexFile : f ,
152+ Retry : false ,
153+ RemoveTakenFlag : false ,
154+ Failure : err ,
137155 Progress : 1 ,
138156 Bytes : 0 ,
139157 Done : true ,
@@ -146,6 +164,7 @@ func (d *Downloader) Resume() error {
146164 IndexFile : f ,
147165 Retry : false ,
148166 RemoveTakenFlag : false ,
167+ Failure : nil ,
149168 Progress : 1 ,
150169 Bytes : resp .BytesComplete (),
151170 Done : true ,
@@ -175,8 +194,10 @@ func (d *Downloader) Resume() error {
175194 // Get next empty dir
176195 dir , err := d .state .Repo .GetNextEmptyDir ()
177196 if err != nil {
178- dialog .NewError (& DatabaseError {err }, d .state .window ).Show ()
179- return
197+ if err != sql .ErrNoRows {
198+ dialog .NewError (& DatabaseError {err }, d .state .window ).Show ()
199+ return
200+ }
180201 }
181202 dest := filepath .Join (d .installPath , dir )
182203 err = os .MkdirAll (dest , os .ModePerm )
@@ -270,6 +291,12 @@ func (d *Downloader) Resume() error {
270291 _ = d .state .fileProgress4 .Set (0 )
271292
272293 for update := range d .updatech {
294+ // Download failure, maximum retries reached
295+ if update .Failure != nil {
296+ d .state .downloadFailures += 1
297+ _ = d .state .formatDownloadFailures .Set (humanize .Comma (d .state .downloadFailures ))
298+ }
299+
273300 // Failed download because of context cancel, remove taken flag instead, ignore ui update
274301 if update .RemoveTakenFlag {
275302 err = d .state .Repo .ClearTaken (update .IndexFile )
@@ -284,6 +311,7 @@ func (d *Downloader) Resume() error {
284311 d .newRequestWg .Add (1 )
285312 go func () {
286313 defer d .newRequestWg .Done ()
314+ time .Sleep (time .Second * 1 ) // Retry after 3 seconds
287315 select {
288316 case <- d .ctx .Done ():
289317 {
@@ -305,62 +333,68 @@ func (d *Downloader) Resume() error {
305333 }
306334
307335 // Update UI element
308- updateIdx := - 1
309- for idx , f := range uifiles {
310- if f .Filepath == update .IndexFile .Filepath {
311- // Send bytes update to speed handler
312- bytesDiff := update .Bytes - f .Bytes
313- speedch <- bytesDiff
314- // Update ui file
315- f .Progress = update .Progress
316- f .Done = update .Done
317- f .Bytes = update .Bytes
318- updateIdx = idx
319- break
320- }
321- }
322- if updateIdx == - 1 {
323- // Didn't find existing entry, find an older one to replace
336+ if update .Failure == nil {
337+ updateIdx := - 1
324338 for idx , f := range uifiles {
325- if f .Done == true {
339+ if f .Filepath == update . IndexFile . Filepath {
326340 // Send bytes update to speed handler
327- bytesDiff := update .Bytes
341+ bytesDiff := update .Bytes - f . Bytes
328342 speedch <- bytesDiff
329343 // Update ui file
330- f .Filepath = update .IndexFile .Filepath
331344 f .Progress = update .Progress
332345 f .Done = update .Done
333346 f .Bytes = update .Bytes
334347 updateIdx = idx
335348 break
336349 }
337350 }
338- }
339- // If updated ui state, update element
340- if updateIdx != - 1 {
341- if updateIdx == 0 {
342- _ = d .state .fileTitle1 .Set (update .IndexFile .Filepath )
343- _ = d .state .fileProgress1 .Set (update .Progress )
344- }
345- if updateIdx == 1 {
346- _ = d .state .fileTitle2 .Set (update .IndexFile .Filepath )
347- _ = d .state .fileProgress2 .Set (update .Progress )
348- }
349- if updateIdx == 2 {
350- _ = d .state .fileTitle3 .Set (update .IndexFile .Filepath )
351- _ = d .state .fileProgress3 .Set (update .Progress )
351+ if updateIdx == - 1 {
352+ // Didn't find existing entry, find an older one to replace
353+ for idx , f := range uifiles {
354+ if f .Done == true {
355+ // Send bytes update to speed handler
356+ bytesDiff := update .Bytes
357+ speedch <- bytesDiff
358+ // Update ui file
359+ f .Filepath = update .IndexFile .Filepath
360+ f .Progress = update .Progress
361+ f .Done = update .Done
362+ f .Bytes = update .Bytes
363+ updateIdx = idx
364+ break
365+ }
366+ }
352367 }
353- if updateIdx == 3 {
354- _ = d .state .fileTitle4 .Set (update .IndexFile .Filepath )
355- _ = d .state .fileProgress4 .Set (update .Progress )
368+ // If updated ui state, update element
369+ if updateIdx != - 1 {
370+ if updateIdx == 0 {
371+ _ = d .state .fileTitle1 .Set (update .IndexFile .Filepath )
372+ _ = d .state .fileProgress1 .Set (update .Progress )
373+ }
374+ if updateIdx == 1 {
375+ _ = d .state .fileTitle2 .Set (update .IndexFile .Filepath )
376+ _ = d .state .fileProgress2 .Set (update .Progress )
377+ }
378+ if updateIdx == 2 {
379+ _ = d .state .fileTitle3 .Set (update .IndexFile .Filepath )
380+ _ = d .state .fileProgress3 .Set (update .Progress )
381+ }
382+ if updateIdx == 3 {
383+ _ = d .state .fileTitle4 .Set (update .IndexFile .Filepath )
384+ _ = d .state .fileProgress4 .Set (update .Progress )
385+ }
356386 }
357387 }
358388
359389 if update .Done {
360- // Mark as done
361- err := d .state .Repo .MarkFileDone (update .IndexFile )
362- if err != nil {
363- dialog .NewError (& DatabaseError {err }, d .state .window ).Show ()
390+ if update .Failure == nil {
391+ // Mark as done
392+ d .state .downloadedSize += update .IndexFile .Size
393+ d .state .downloadedFiles += 1
394+ err := d .state .Repo .MarkFileDone (update .IndexFile )
395+ if err != nil {
396+ dialog .NewError (& DatabaseError {err }, d .state .window ).Show ()
397+ }
364398 }
365399
366400 d .newRequestWg .Add (1 )
@@ -392,18 +426,40 @@ func (d *Downloader) Resume() error {
392426 }()
393427
394428 // Update Total Progress bar state
395- d .state .downloadedSize += update .IndexFile .Size
396- d .state .downloadedFiles += 1
397429 err = d .state .formatDownloadedSize .Set (FormatBytes (d .state .downloadedSize ))
398430 if err != nil {
399431 dialog .NewError (err , d .state .window ).Show ()
400432 }
433+ err = d .state .formatDownloadedFiles .Set (humanize .Comma (d .state .downloadedFiles ))
434+ if err != nil {
435+ dialog .NewError (err , d .state .window ).Show ()
436+ }
401437 progress := float64 (d .state .downloadedSize ) / float64 (d .state .totalSize )
402438 err = d .state .progressBarTotal .Set (progress )
403439 if err != nil {
404440 dialog .NewError (err , d .state .window ).Show ()
405441 }
406442
443+ // Check if we're done
444+ totalFiles := d .state .downloadedFiles + d .state .downloadFailures
445+ if totalFiles == d .state .totalFiles {
446+ // Done!
447+ err := d .state .Repo .ClearTakenAll ()
448+ d .cancel ()
449+ go func () {
450+ d .Stop (true )
451+ if err != nil {
452+ dialog .NewError (& DatabaseError {err }, d .state .window ).Show ()
453+ } else {
454+ if d .state .downloadFailures > 0 {
455+ dialog .NewInformation ("Finished" , fmt .Sprintf ("Install finished with %d failures, you will have to press start again to retry these failed files." , d .state .downloadFailures ), d .state .window ).Show ()
456+ } else {
457+ dialog .NewInformation ("Finished" , "Install finished with no failures" , d .state .window ).Show ()
458+ }
459+ }
460+ }()
461+ }
462+
407463 }
408464 }
409465 }()
@@ -423,18 +479,20 @@ func (d *Downloader) Resume() error {
423479 d .reqch <- req
424480 }
425481 d .running = true
426- d . started = true
482+ _ = d . state . runningLabel . Set ( "Running" )
427483
428484 return nil
429485}
430486
431- func (d * Downloader ) Stop () {
487+ func (d * Downloader ) Stop (skipCancelContext bool ) {
432488 if ! d .running {
433489 return
434490 }
435491
436492 // Stop new and current requests
437- d .cancel ()
493+ if ! skipCancelContext {
494+ d .cancel ()
495+ }
438496 d .newRequestWg .Wait ()
439497 close (d .reqch )
440498
@@ -448,6 +506,7 @@ func (d *Downloader) Stop() {
448506 d .updaterWg .Wait ()
449507
450508 d .running = false
509+ _ = d .state .runningLabel .Set ("Stopped" )
451510}
452511
453512func (d * Downloader ) NewRequest (f * IndexedFile ) (* grab.Request , error ) {
0 commit comments