Show progress bars as aborted on export / import error

This commit is contained in:
Philipp Wolfer 2025-05-04 12:59:40 +02:00
parent 15d939e150
commit aae5123c3d
No known key found for this signature in database
GPG key ID: 8FDF744D4919943B
12 changed files with 35 additions and 21 deletions

View file

@ -3,6 +3,7 @@
## 0.6.0 - WIP ## 0.6.0 - WIP
- Fix program hanging endlessly if import fails (#11) - Fix program hanging endlessly if import fails (#11)
- If import fails still store the last successfully imported timestamp - If import fails still store the last successfully imported timestamp
- Show progress bars as aborted on export / import error
- JSPF: implemented export as loves and listens - JSPF: implemented export as loves and listens
- JSPF: write track duration - JSPF: write track duration
- JSPF: read username and recording MSID - JSPF: read username and recording MSID

View file

@ -94,7 +94,7 @@ out:
for { for {
result, err := b.client.UserHistory(offset, perPage) result, err := b.client.UserHistory(offset, perPage)
if err != nil { if err != nil {
progress <- p.Complete() progress <- p.Abort()
results <- models.ListensResult{Error: err} results <- models.ListensResult{Error: err}
return return
} }
@ -160,7 +160,7 @@ out:
for { for {
result, err := b.client.UserTracks(offset, perPage) result, err := b.client.UserTracks(offset, perPage)
if err != nil { if err != nil {
progress <- p.Complete() progress <- p.Abort()
results <- models.LovesResult{Error: err} results <- models.LovesResult{Error: err}
return return
} }

View file

@ -118,6 +118,6 @@ func process[R models.LovesResult | models.ListensResult, P ImportProcessor[R]](
func handleError(result models.ImportResult, err error, progress chan models.Progress) models.ImportResult { func handleError(result models.ImportResult, err error, progress chan models.Progress) models.ImportResult {
result.Error = err result.Error = err
progress <- models.Progress{}.FromImportResult(result).Complete() progress <- models.Progress{}.FromImportResult(result).Abort()
return result return result
} }

View file

@ -96,7 +96,7 @@ func (b *JSPFBackend) FinishImport() error {
func (b *JSPFBackend) ExportListens(oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.Progress) { func (b *JSPFBackend) ExportListens(oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.Progress) {
err := b.readJSPF() err := b.readJSPF()
if err != nil { if err != nil {
progress <- models.Progress{}.Complete() progress <- models.Progress{}.Abort()
results <- models.ListensResult{Error: err} results <- models.ListensResult{Error: err}
return return
} }
@ -128,7 +128,7 @@ func (b *JSPFBackend) ImportListens(export models.ListensResult, importResult mo
func (b *JSPFBackend) ExportLoves(oldestTimestamp time.Time, results chan models.LovesResult, progress chan models.Progress) { func (b *JSPFBackend) ExportLoves(oldestTimestamp time.Time, results chan models.LovesResult, progress chan models.Progress) {
err := b.readJSPF() err := b.readJSPF()
if err != nil { if err != nil {
progress <- models.Progress{}.Complete() progress <- models.Progress{}.Abort()
results <- models.LovesResult{Error: err} results <- models.LovesResult{Error: err}
return return
} }

View file

@ -108,7 +108,7 @@ out:
result, err := b.client.User.GetRecentTracks(args) result, err := b.client.User.GetRecentTracks(args)
if err != nil { if err != nil {
results <- models.ListensResult{Error: err} results <- models.ListensResult{Error: err}
progress <- p.Complete() progress <- p.Abort()
return return
} }
@ -127,7 +127,7 @@ out:
timestamp, err := strconv.ParseInt(scrobble.Date.Uts, 10, 64) timestamp, err := strconv.ParseInt(scrobble.Date.Uts, 10, 64)
if err != nil { if err != nil {
results <- models.ListensResult{Error: err} results <- models.ListensResult{Error: err}
progress <- p.Complete() progress <- p.Abort()
break out break out
} }
if timestamp > oldestTimestamp.Unix() { if timestamp > oldestTimestamp.Unix() {
@ -268,7 +268,7 @@ out:
"page": page, "page": page,
}) })
if err != nil { if err != nil {
progress <- p.Complete() progress <- p.Abort()
results <- models.LovesResult{Error: err} results <- models.LovesResult{Error: err}
return return
} }
@ -282,7 +282,7 @@ out:
for _, track := range result.Tracks { for _, track := range result.Tracks {
timestamp, err := strconv.ParseInt(track.Date.Uts, 10, 64) timestamp, err := strconv.ParseInt(track.Date.Uts, 10, 64)
if err != nil { if err != nil {
progress <- p.Complete() progress <- p.Abort()
results <- models.LovesResult{Error: err} results <- models.LovesResult{Error: err}
return return
} }

View file

@ -86,7 +86,7 @@ func (b *ListenBrainzApiBackend) ExportListens(oldestTimestamp time.Time, result
for { for {
result, err := b.client.GetListens(b.username, time.Now(), minTime) result, err := b.client.GetListens(b.username, time.Now(), minTime)
if err != nil { if err != nil {
progress <- p.Complete() progress <- p.Abort()
results <- models.ListensResult{Error: err} results <- models.ListensResult{Error: err}
return return
} }
@ -199,8 +199,9 @@ func (b *ListenBrainzApiBackend) ExportLoves(oldestTimestamp time.Time, results
go b.exportLoves(oldestTimestamp, exportChan) go b.exportLoves(oldestTimestamp, exportChan)
for existingLoves := range exportChan { for existingLoves := range exportChan {
if existingLoves.Error != nil { if existingLoves.Error != nil {
progress <- p.Complete() progress <- p.Abort()
results <- models.LovesResult{Error: existingLoves.Error} results <- models.LovesResult{Error: existingLoves.Error}
return
} }
p.Total = int64(existingLoves.Total) p.Total = int64(existingLoves.Total)

View file

@ -134,7 +134,7 @@ func (b *ScrobblerLogBackend) FinishImport() error {
func (b *ScrobblerLogBackend) ExportListens(oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.Progress) { func (b *ScrobblerLogBackend) ExportListens(oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.Progress) {
file, err := os.Open(b.filePath) file, err := os.Open(b.filePath)
if err != nil { if err != nil {
progress <- models.Progress{}.Complete() progress <- models.Progress{}.Abort()
results <- models.ListensResult{Error: err} results <- models.ListensResult{Error: err}
return return
} }

View file

@ -106,7 +106,7 @@ func (b *SpotifyApiBackend) ExportListens(oldestTimestamp time.Time, results cha
for { for {
result, err := b.client.RecentlyPlayedAfter(minTime, MaxItemsPerGet) result, err := b.client.RecentlyPlayedAfter(minTime, MaxItemsPerGet)
if err != nil { if err != nil {
progress <- p.Complete() progress <- p.Abort()
results <- models.ListensResult{Error: err} results <- models.ListensResult{Error: err}
return return
} }
@ -118,7 +118,7 @@ func (b *SpotifyApiBackend) ExportListens(oldestTimestamp time.Time, results cha
// Set minTime to the newest returned listen // Set minTime to the newest returned listen
after, err := strconv.ParseInt(result.Cursors.After, 10, 64) after, err := strconv.ParseInt(result.Cursors.After, 10, 64)
if err != nil { if err != nil {
progress <- p.Complete() progress <- p.Abort()
results <- models.ListensResult{Error: err} results <- models.ListensResult{Error: err}
return return
} else if after <= minTime.Unix() { } else if after <= minTime.Unix() {
@ -169,7 +169,7 @@ out:
for { for {
result, err := b.client.UserTracks(offset, perPage) result, err := b.client.UserTracks(offset, perPage)
if err != nil { if err != nil {
progress <- p.Complete() progress <- p.Abort()
results <- models.LovesResult{Error: err} results <- models.LovesResult{Error: err}
return return
} }

View file

@ -75,7 +75,7 @@ func (b *SpotifyHistoryBackend) InitConfig(config *config.ServiceConfig) error {
func (b *SpotifyHistoryBackend) ExportListens(oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.Progress) { func (b *SpotifyHistoryBackend) ExportListens(oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.Progress) {
files, err := filepath.Glob(path.Join(b.dirPath, historyFileGlob)) files, err := filepath.Glob(path.Join(b.dirPath, historyFileGlob))
if err != nil { if err != nil {
progress <- models.Progress{}.Complete() progress <- models.Progress{}.Abort()
results <- models.ListensResult{Error: err} results <- models.ListensResult{Error: err}
return return
} }
@ -86,7 +86,7 @@ func (b *SpotifyHistoryBackend) ExportListens(oldestTimestamp time.Time, results
for i, filePath := range files { for i, filePath := range files {
history, err := readHistoryFile(filePath) history, err := readHistoryFile(filePath)
if err != nil { if err != nil {
progress <- models.Progress{}.Complete() progress <- models.Progress{}.Abort()
results <- models.ListensResult{Error: err} results <- models.ListensResult{Error: err}
return return
} }

View file

@ -66,14 +66,14 @@ func (b *SubsonicApiBackend) InitConfig(config *config.ServiceConfig) error {
func (b *SubsonicApiBackend) ExportLoves(oldestTimestamp time.Time, results chan models.LovesResult, progress chan models.Progress) { func (b *SubsonicApiBackend) ExportLoves(oldestTimestamp time.Time, results chan models.LovesResult, progress chan models.Progress) {
err := b.client.Authenticate(b.password) err := b.client.Authenticate(b.password)
if err != nil { if err != nil {
progress <- models.Progress{}.Complete() progress <- models.Progress{}.Abort()
results <- models.LovesResult{Error: err} results <- models.LovesResult{Error: err}
return return
} }
starred, err := b.client.GetStarred2(map[string]string{}) starred, err := b.client.GetStarred2(map[string]string{})
if err != nil { if err != nil {
progress <- models.Progress{}.Complete() progress <- models.Progress{}.Abort()
results <- models.LovesResult{Error: err} results <- models.LovesResult{Error: err}
return return
} }

View file

@ -59,10 +59,12 @@ func setupProgressBar(p *mpb.Progress, name string) *mpb.Bar {
), ),
mpb.AppendDecorators( mpb.AppendDecorators(
decor.OnComplete( decor.OnComplete(
decor.OnAbort(
decor.EwmaETA(decor.ET_STYLE_GO, 0, decor.WC{C: decor.DSyncWidth}), decor.EwmaETA(decor.ET_STYLE_GO, 0, decor.WC{C: decor.DSyncWidth}),
i18n.Tr("error"),
),
i18n.Tr("done"), i18n.Tr("done"),
), ),
// decor.OnComplete(decor.Percentage(decor.WC{W: 5, C: decor.DSyncWidthR}), "done"),
decor.Name(" "), decor.Name(" "),
), ),
) )
@ -73,6 +75,10 @@ func updateProgressBar(bar *mpb.Bar, wg *sync.WaitGroup, progressChan chan model
defer wg.Done() defer wg.Done()
lastIterTime := time.Now() lastIterTime := time.Now()
for progress := range progressChan { for progress := range progressChan {
if progress.Aborted {
bar.Abort(false)
return
}
oldIterTime := lastIterTime oldIterTime := lastIterTime
lastIterTime = time.Now() lastIterTime = time.Now()
bar.EwmaSetCurrent(progress.Elapsed, lastIterTime.Sub(oldIterTime)) bar.EwmaSetCurrent(progress.Elapsed, lastIterTime.Sub(oldIterTime))

View file

@ -213,6 +213,7 @@ type Progress struct {
Total int64 Total int64
Elapsed int64 Elapsed int64
Completed bool Completed bool
Aborted bool
} }
func (p Progress) FromImportResult(result ImportResult) Progress { func (p Progress) FromImportResult(result ImportResult) Progress {
@ -226,3 +227,8 @@ func (p Progress) Complete() Progress {
p.Completed = true p.Completed = true
return p return p
} }
func (p Progress) Abort() Progress {
p.Aborted = true
return p
}