fix(web): scan progress — correct percentages, no layout shift, refresh-survivable
Bug: the admin 'Scanning Libraries' widget fed the server's 0-1 progress straight into percent strings and bar widths, so the overall and per-library percentages capped at '1%' and the bars never filled. Both now scale by 100 (matching the setup page, which was already correct). Bug: scan state only reached clients via websocket events, so a page opened (or refreshed) mid-scan showed nothing until the next tick — and the admin widget never re-attached at all. The worker now exposes active jobs (with their library_id so rows keep their names), served as GET /api/scanner/active (admin, re-attaches the widget + polling) and GET /api/scanner/active-summary (any user, restores the sidebar indicator on load). Idle hides both, exactly as before. Annoyance: the sidebar's 'Scanning… N%' was a block element between the logo and the nav, shoving every menu entry down while a scan ran. The percent now lives inside the fixed-height logo row next to the spinner (spinner last, so growing digits extend leftward); nothing below it moves.
This commit is contained in:
1 parent
9bc149f3d9
commit
fd6f0e3353
11 files changed
+245
-38
No files matched your search
@@ -73,8 +73,12 @@ func (j *Job) UpdateProgress(progress float64, filesScanned, newItems, errors in
|
||||
}
|
||||
|
||||
type JobResult struct {
|
||||
JobID string `json:"job_id"`
|
||||
Status JobStatus `json:"status"`
|
||||
JobID string `json:"job_id"`
|
||||
Status JobStatus `json:"status"`
|
||||
// LibraryID carries the scan job's library (from Params) so clients
|
||||
// can label progress rows after a page refresh, when the original
|
||||
// start-request context is gone.
|
||||
LibraryID string `json:"library_id,omitempty"`
|
||||
Error string `json:"error"`
|
||||
Result interface{} `json:"result"`
|
||||
Progress float64 `json:"progress"`
|
||||
@@ -237,10 +241,12 @@ func (w *Worker) worker() {
|
||||
}
|
||||
|
||||
func (w *Worker) processJob(job *Job) {
|
||||
libID, _ := job.Params["library_id"].(string)
|
||||
w.mu.Lock()
|
||||
w.results[job.ID] = &JobResult{
|
||||
JobID: job.ID,
|
||||
Status: JobStatusRunning,
|
||||
JobID: job.ID,
|
||||
Status: JobStatusRunning,
|
||||
LibraryID: libID,
|
||||
}
|
||||
w.mu.Unlock()
|
||||
|
||||
@@ -313,8 +319,9 @@ func (w *Worker) processJob(job *Job) {
|
||||
}
|
||||
|
||||
w.results[job.ID] = &JobResult{
|
||||
JobID: job.ID,
|
||||
Status: status,
|
||||
JobID: job.ID,
|
||||
Status: status,
|
||||
LibraryID: libID,
|
||||
Error: func() string {
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
@@ -981,6 +988,23 @@ func (w *Worker) GetJobStatus(jobID string) (*JobResult, bool) {
|
||||
return result, exists
|
||||
}
|
||||
|
||||
// GetActiveScanJobs returns snapshots of every pending/running job.
|
||||
// Clients use it to re-attach to in-flight scans (e.g. after a page
|
||||
// refresh mid-scan) instead of only learning about scans from
|
||||
// websocket events.
|
||||
func (w *Worker) GetActiveScanJobs() []JobResult {
|
||||
w.mu.RLock()
|
||||
defer w.mu.RUnlock()
|
||||
|
||||
active := make([]JobResult, 0)
|
||||
for _, result := range w.results {
|
||||
if result.Status == JobStatusRunning || result.Status == JobStatusPending {
|
||||
active = append(active, *result)
|
||||
}
|
||||
}
|
||||
return active
|
||||
}
|
||||
|
||||
func (w *Worker) CancelJob(jobID string) error {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
Reference in new issue
Block a user