87 lines
2.4 KiB
Go
87 lines
2.4 KiB
Go
package ui
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"git.stinnesbeck.com/nils/arr-tui/internal/sabnzbd"
|
|
"github.com/rivo/tview"
|
|
)
|
|
|
|
// watchInterval is how often SABnzbd's history is checked for finished
|
|
// downloads in the background, whatever view is showing.
|
|
const watchInterval = 15 * time.Second
|
|
|
|
// downloadEvent is a download that just finished or failed.
|
|
type downloadEvent struct {
|
|
name string
|
|
failed bool
|
|
message string // failure reason, if any
|
|
}
|
|
|
|
// diffHistory compares SABnzbd's history with what was seen before and
|
|
// returns the downloads that reached a final state since. seen maps job id to
|
|
// the last known status and is updated. With baseline set (the first poll) the
|
|
// current state is only recorded, so downloads that finished before arr-tui
|
|
// started do not produce notifications.
|
|
func diffHistory(seen map[string]string, slots []sabnzbd.HistorySlot, baseline bool) []downloadEvent {
|
|
var events []downloadEvent
|
|
for _, s := range slots {
|
|
prev, known := seen[s.ID]
|
|
seen[s.ID] = s.Status
|
|
final := s.Status == "Completed" || s.Failed()
|
|
if baseline || !final || (known && prev == s.Status) {
|
|
continue
|
|
}
|
|
events = append(events, downloadEvent{name: s.Name, failed: s.Failed(), message: s.FailMessage})
|
|
}
|
|
return events
|
|
}
|
|
|
|
// watchDownloads polls SABnzbd's history until ctx ends and reports finished
|
|
// and failed downloads as popups (and a bell, if enabled). Errors are not
|
|
// reported here: the connection indicator and the log already do that.
|
|
func (a *App) watchDownloads(ctx context.Context, sab *sabnzbd.Client, bell bool) {
|
|
seen := map[string]string{}
|
|
baseline := true
|
|
t := time.NewTicker(watchInterval)
|
|
defer t.Stop()
|
|
for {
|
|
hctx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
|
slots, err := sab.History(hctx, 30)
|
|
cancel()
|
|
if err == nil {
|
|
events := diffHistory(seen, slots, baseline)
|
|
baseline = false
|
|
if len(events) > 0 {
|
|
a.post(func() { a.announce(events, bell) })
|
|
}
|
|
}
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-t.C:
|
|
}
|
|
}
|
|
}
|
|
|
|
// announce shows a popup for each event; UI goroutine only.
|
|
func (a *App) announce(events []downloadEvent, bell bool) {
|
|
for _, e := range events {
|
|
if e.failed {
|
|
text := "Download failed: " + tview.Escape(e.name)
|
|
if e.message != "" {
|
|
text += " (" + tview.Escape(truncate(e.message, 60)) + ")"
|
|
}
|
|
a.SetMessage("[red]" + text)
|
|
} else {
|
|
a.SetMessage("[green]Download finished: " + tview.Escape(e.name))
|
|
}
|
|
}
|
|
if bell {
|
|
if a.screen != nil {
|
|
a.screen.Beep()
|
|
}
|
|
}
|
|
}
|