Skip to content

Commit f15e162

Browse files
committed
cleanup progress
1 parent 35e07fb commit f15e162

File tree

14 files changed

+67
-56
lines changed

14 files changed

+67
-56
lines changed

.golangci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ linters:
3535
- golift.io
3636
- golang.org/x
3737
- gopkg.in/yaml.v3
38+
- code.cloudfoundry.org/bytefmt
3839

3940
exclusions:
4041
generated: lax

examples/docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,4 @@ services:
137137
- UN_CMDHOOK_0_EXCLUDE_1=lidarr
138138
- UN_CMDHOOK_0_TIMEOUT=10s
139139

140-
## => Content Auto Generated, 20 APR 2025 20:24 UTC
140+
## => Content Auto Generated, 21 APR 2025 05:15 UTC

examples/unpackerr.conf.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,4 +306,4 @@ dir_mode = "0755"
306306
## You can adjust how long to wait for the command to run.
307307
# timeout = "10s"
308308

309-
## => Content Auto Generated, 20 APR 2025 20:24 UTC
309+
## => Content Auto Generated, 21 APR 2025 05:15 UTC

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ require (
2525
)
2626

2727
require (
28+
code.cloudfoundry.org/bytefmt v0.37.0 // indirect
2829
github.com/andybalholm/brotli v1.1.1 // indirect
2930
github.com/beorn7/perks v1.0.1 // indirect
3031
github.com/bodgit/plumbing v1.3.0 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2k
1414
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
1515
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
1616
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
17+
code.cloudfoundry.org/bytefmt v0.37.0 h1:GR5rZgr/6QLV/U2/xgORHUY4lu4EEBgN4/cz7SD3GqM=
18+
code.cloudfoundry.org/bytefmt v0.37.0/go.mod h1:u3/LRyPLBYFtn8h9CnzTeupRMD+76qCovDU0vND81lU=
1719
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
1820
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
1921
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=

pkg/unpackerr/folder.go

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"strings"
1212
"time"
1313

14+
"code.cloudfoundry.org/bytefmt"
1415
"github.com/fsnotify/fsnotify"
1516
"github.com/radovskyb/watcher"
1617
"golift.io/cnfg"
@@ -278,7 +279,7 @@ func (u *Unpackerr) extractTrackedItem(name string, folder *Folder, now time.Tim
278279
}
279280

280281
// create a queue counter in the main history; add to u.Map and send webhook for a new folder.
281-
u.updateQueueStatus(&newStatus{Name: name, Status: QUEUED}, u.folders.Folders[name].updated, true)
282+
item := u.updateQueueStatus(&newStatus{Name: name, Status: QUEUED}, u.folders.Folders[name].updated, true)
282283
u.updateHistory(FolderString + ": " + name)
283284

284285
var exclude []string
@@ -297,7 +298,7 @@ func (u *Unpackerr) extractTrackedItem(name string, folder *Folder, now time.Tim
297298
DeleteOrig: false,
298299
CBChannel: u.folders.Updates,
299300
CBFunction: nil,
300-
Progress: u.progressUpdateCallback(name),
301+
Progress: u.progressUpdateCallback(item),
301302
LogFile: !folder.config.DisableLog,
302303
DisableRecursion: folder.config.DisableRecursion,
303304
})
@@ -332,17 +333,15 @@ func getFileList(path string) []os.FileInfo {
332333
func (u *Unpackerr) folderXtractrCallback(resp *xtractr.Response) {
333334
folder, ok := u.folders.Folders[resp.X.Name]
334335

335-
switch {
336-
case !ok:
336+
switch item := u.Map[resp.X.Name]; {
337+
case !ok, item == nil:
337338
// It doesn't exist? weird. delete it and bail out.
339+
delete(u.folders.Folders, resp.X.Name)
338340
delete(u.Map, resp.X.Name)
341+
339342
return
340343
case !resp.Done:
341-
if u.Map[resp.X.Name] != nil && u.Map[resp.X.Name].Progress != nil {
342-
// Update the total archive count in the progress status.
343-
u.Map[resp.X.Name].Progress.Archives = resp.Archives.Count()
344-
}
345-
344+
item.XProg.Archives = resp.Archives.Count() + resp.Extras.Count()
346345
folder.status = EXTRACTING
347346
u.Printf("[Folder] Extraction Started: %s, retries: %d, items in queue: %d", resp.X.Name, folder.retries, resp.Queued)
348347
case errors.Is(resp.Error, xtractr.ErrNoCompressedFiles):
@@ -356,9 +355,9 @@ func (u *Unpackerr) folderXtractrCallback(resp *xtractr.Response) {
356355
default: // this runs in a go routine
357356
u.updateMetrics(resp, FolderString, folder.config.Path)
358357
u.Printf("[Folder] Extraction Finished: %s => elapsed: %v, archives: %d, "+
359-
"extra archives: %d, files extracted: %d, written: %dMiB",
358+
"extra archives: %d, files extracted: %d, written: %sB",
360359
resp.X.Name, resp.Elapsed.Round(time.Second), resp.Archives.Count(),
361-
resp.Extras.Count(), len(resp.NewFiles), resp.Size/mebiByte)
360+
resp.Extras.Count(), len(resp.NewFiles), bytefmt.ByteSize(resp.Size))
362361

363362
folder.archives = resp.Archives
364363
folder.status = EXTRACTED
@@ -557,7 +556,7 @@ type newStatus struct {
557556
// updateQueueStatus for an on-going tracked extraction.
558557
// This is called from a channel callback to update status in a single go routine.
559558
// This is used by apps and Folders in a few other places as well.
560-
func (u *Unpackerr) updateQueueStatus(data *newStatus, now time.Time, sendHook bool) {
559+
func (u *Unpackerr) updateQueueStatus(data *newStatus, now time.Time, sendHook bool) *Extract {
561560
if _, ok := u.Map[data.Name]; !ok {
562561
// This is a new Folder being queued for extraction.
563562
// Arr apps do not land here. They create their own queued items in u.Map.
@@ -569,11 +568,13 @@ func (u *Unpackerr) updateQueueStatus(data *newStatus, now time.Time, sendHook b
569568
IDs: map[string]any{"title": data.Name}, // required or webhook may break.
570569
}
571570

571+
u.Map[data.Name].XProg = &ExtractProgress{Extract: u.Map[data.Name]}
572+
572573
if sendHook {
573574
u.runAllHooks(u.Map[data.Name])
574575
}
575576

576-
return
577+
return u.Map[data.Name]
577578
}
578579

579580
if data.Resp != nil {
@@ -586,4 +587,6 @@ func (u *Unpackerr) updateQueueStatus(data *newStatus, now time.Time, sendHook b
586587
if sendHook {
587588
u.runAllHooks(u.Map[data.Name])
588589
}
590+
591+
return u.Map[data.Name]
589592
}

pkg/unpackerr/handlers.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"strings"
88
"time"
99

10+
"code.cloudfoundry.org/bytefmt"
1011
"golift.io/cnfg"
1112
"golift.io/starr"
1213
"golift.io/xtractr"
@@ -25,7 +26,7 @@ type Extract struct {
2526
Status ExtractStatus
2627
IDs map[string]any
2728
Resp *xtractr.Response
28-
Progress *Progress
29+
XProg *ExtractProgress
2930
}
3031

3132
// Shared config items for all starr apps.
@@ -75,7 +76,7 @@ func (u *Unpackerr) checkQueueChanges(now time.Time) {
7576
}
7677

7778
u.Printf("[%s] Status: %s (%v, elapsed: %v) %s", data.App, name, data.Status.Desc(),
78-
now.Sub(data.Updated).Round(time.Second), data.Progress)
79+
now.Sub(data.Updated).Round(time.Second), data.XProg)
7980
}
8081
}
8182

@@ -134,7 +135,7 @@ func (u *Unpackerr) extractCompletedDownload(name string, now time.Time, item *E
134135
TempFolder: false,
135136
DeleteOrig: false,
136137
CBChannel: u.updates,
137-
Progress: u.progressUpdateCallback(name),
138+
Progress: u.progressUpdateCallback(item),
138139
})
139140

140141
u.logQueuedDownload(queueSize, item, files)
@@ -208,10 +209,8 @@ func (u *Unpackerr) checkExtractDone(now time.Time) {
208209
func (u *Unpackerr) handleXtractrCallback(resp *xtractr.Response) {
209210
if item := u.Map[resp.X.Name]; resp.Done && item != nil {
210211
u.updateMetrics(resp, item.App, item.URL)
211-
212-
if item.Progress != nil {
213-
item.Progress.Archives = resp.Archives.Count()
214-
}
212+
} else if item != nil {
213+
item.XProg.Archives = resp.Archives.Count() + resp.Extras.Count()
215214
}
216215

217216
switch now := resp.Started.Add(resp.Elapsed); {
@@ -224,8 +223,8 @@ func (u *Unpackerr) handleXtractrCallback(resp *xtractr.Response) {
224223
default:
225224
files := fileList(resp.X.Path)
226225
u.Printf("Extraction Finished: %s => elapsed: %v, archives: %d, extra archives: %d, "+
227-
"files extracted: %d, wrote: %dMiB", resp.X.Name, resp.Elapsed.Round(time.Second),
228-
resp.Archives.Count(), resp.Extras.Count(), len(resp.NewFiles), resp.Size/mebiByte)
226+
"files extracted: %d, wrote: %sB", resp.X.Name, resp.Elapsed.Round(time.Second),
227+
resp.Archives.Count(), resp.Extras.Count(), len(resp.NewFiles), bytefmt.ByteSize(resp.Size))
229228
u.Debugf("Extraction Finished: %d files in path: %s", len(files), files)
230229
u.updateQueueStatus(&newStatus{Name: resp.X.Name, Status: EXTRACTED, Resp: resp}, now, true)
231230
}

pkg/unpackerr/lidarr.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ func (u *Unpackerr) checkLidarrQueue(now time.Time) {
104104
"reason": buildStatusReason(record.Status, record.StatusMessages),
105105
},
106106
}
107+
u.Map[record.Title].XProg = &ExtractProgress{Extract: u.Map[record.Title]}
107108

108109
fallthrough
109110
default:

pkg/unpackerr/progress.go

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,33 @@ package unpackerr
33
import (
44
"fmt"
55
"path/filepath"
6+
"strings"
67
"time"
78

9+
"code.cloudfoundry.org/bytefmt"
810
"golift.io/xtractr"
911
)
1012

1113
const (
12-
minimumProgressInterval = 2 * time.Second
14+
minimumProgressInterval = time.Second
1315
defaultProgressInterval = 15 * time.Second
1416
)
1517

16-
type Progress struct {
18+
// ExtractProgress holds the progress for an entire Extract.
19+
// An Extract is "a new item in a watch folder" or "a download in a starr app".
20+
// Either may produce multiple xtractr.XFile structs (extractable archives).
21+
type ExtractProgress struct {
1722
*xtractr.Progress
18-
// Name of this item in the Map.
19-
Name string
23+
// Extract extract that exists in the map.
24+
*Extract
2025
// Number of archives in this Xtract.
2126
Archives int
2227
// Number of archives extracted from this Xtract.
2328
Extracted int
2429
}
2530

26-
func (p *Progress) String() string {
27-
if p == nil {
31+
func (p *ExtractProgress) String() string {
32+
if p == nil || p.Progress == nil {
2833
return "no progress yet"
2934
}
3035

@@ -36,32 +41,26 @@ func (p *Progress) String() string {
3641
wrote, total = p.Read, p.Compressed
3742
}
3843

39-
return fmt.Sprintf("extracted %d/%d archives, current: %d/%d bytes (%.0f%%): %s",
40-
p.Extracted, p.Archives, wrote, total, p.Percent(), filepath.Base(p.XFile.FilePath))
44+
return fmt.Sprintf("on archive: %d/%d @ %sB/%sB (%.0f%%): %s",
45+
p.Extracted+1, p.Archives, bytefmt.ByteSize(wrote), bytefmt.ByteSize(total),
46+
p.Percent(), strings.TrimLeft(strings.TrimPrefix(p.XFile.FilePath, p.Path), string(filepath.Separator)))
4147
}
4248

43-
func (u *Unpackerr) progressUpdateCallback(name string) func(xtractr.Progress) {
44-
return func(prog xtractr.Progress) {
45-
u.progress <- &Progress{Progress: &prog, Name: name} // ends up in u.handleProgress() (below)
49+
func (u *Unpackerr) progressUpdateCallback(item *Extract) func(xtractr.Progress) {
50+
return func(prog xtractr.Progress) { // sends update to u.handleProgress() (below)
51+
u.progChan <- &ExtractProgress{Progress: &prog, Extract: item}
4652
}
4753
}
4854

49-
func (u *Unpackerr) handleProgress(prog *Progress) {
50-
if item := u.Map[prog.Name]; item != nil {
51-
if item.Progress == nil {
52-
item.Progress = prog
53-
} else {
54-
item.Progress.Progress = prog.Progress
55-
}
56-
57-
if prog.Done {
58-
item.Progress.Extracted++
59-
}
60-
61-
if item.Resp != nil {
62-
prog.Archives = item.Resp.Archives.Count()
63-
}
55+
// prog = what just came in, it's ephemeral.
56+
// prog.XProg = what is saved in the map, update this one.
57+
// prog.Progress = also what just came in, we just set it here.
58+
func (u *Unpackerr) handleProgress(prog *ExtractProgress) {
59+
if prog.XProg.Progress != nil && prog.XProg.XFile != prog.XFile {
60+
prog.XProg.Extracted++
6461
}
62+
63+
prog.XProg.Progress = prog.Progress
6564
}
6665

6766
func (u *Unpackerr) printProgress(now time.Time) {
@@ -70,7 +69,9 @@ func (u *Unpackerr) printProgress(now time.Time) {
7069
continue
7170
}
7271

73-
u.Printf("[%s] Status: %s (%v, elapsed: %v) %s", data.App, name, data.Status.Desc(),
74-
now.Sub(data.Updated).Round(time.Second), data.Progress)
72+
if prog := data.XProg.String(); prog != "no progress yet" {
73+
u.Printf("[%s] Status: %s (%v, elapsed: %v) %s", data.App, name, data.Status.Desc(),
74+
now.Sub(data.Updated).Round(time.Second), prog)
75+
}
7576
}
7677
}

pkg/unpackerr/radarr.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ func (u *Unpackerr) checkRadarrQueue(now time.Time) {
103103
"reason": buildStatusReason(record.Status, record.StatusMessages),
104104
},
105105
}
106+
u.Map[record.Title].XProg = &ExtractProgress{Extract: u.Map[record.Title]}
106107

107108
fallthrough
108109
default:

0 commit comments

Comments
 (0)