feat(jobs): surface background jobs with progress, logs and controls
Add a central job registry that library scans and search index builds report into, so background work is visible instead of buried in the settings page. - backend/jobs: registry with per-job ring-buffer logs, capability-driven controls, and one coalesced JobsChanged snapshot at 4Hz - pause survives restart via a job_state table; a paused scan is adopted back on launch and skipped by the soft scan - top-bar indicator, popover, details drawer and a Jobs page replacing the config page's scan UI; per-library start/stop retained - scan timing breakdown moves into the job log, Full rescan to the Jobs page; delete the orphaned library-manager component Also add cmd/indexbuild and cmd/indexexport so the explore index can be built once centrally rather than by every install, which today streams ~205GB from the ListenBrainz spark dump on first run. indexbuild picks build/refresh/rebuild from index state; the Gitea workflow runs it on push, weekly, or manually and publishes only when content changed. fresh-install no longer defaults YJ_HOME under /tmp: it is tmpfs on most distros, and the import needs ~6GB of real disk. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,7 @@ import (
|
||||
|
||||
"yellowjacket/backend/database"
|
||||
"yellowjacket/backend/events"
|
||||
"yellowjacket/backend/jobs"
|
||||
)
|
||||
|
||||
// Service is the Wails-bound service for the explore feature.
|
||||
@@ -148,6 +149,52 @@ func (e *Service) StopIndexBuild() {
|
||||
e.index.StopBuild()
|
||||
}
|
||||
|
||||
// SetJobRegistry wires the background job registry into the search
|
||||
// index so its build reports progress and controls to the frontend.
|
||||
func (e *Service) SetJobRegistry(reg *jobs.Registry) {
|
||||
e.index.SetJobRegistry(reg)
|
||||
}
|
||||
|
||||
// AdoptPausedIndexBuild re-registers a build paused in a previous
|
||||
// session so it appears in the jobs panel, still paused.
|
||||
func (e *Service) AdoptPausedIndexBuild() {
|
||||
e.index.AdoptPausedBuild()
|
||||
}
|
||||
|
||||
// IndexImportComplete reports whether the dump import has finished all
|
||||
// of its stages. Distinct from IsIndexReady, which only means the index
|
||||
// holds enough rows to answer queries — a partially imported index is
|
||||
// ready but not complete. Used by the headless builder to decide
|
||||
// whether another run is needed.
|
||||
func (e *Service) IndexImportComplete() bool {
|
||||
return e.index.ImportComplete()
|
||||
}
|
||||
|
||||
// IndexBaselineSeries returns the incremental listens series the index's
|
||||
// popularity is caught up to. A change across a refresh means new data
|
||||
// was folded in.
|
||||
func (e *Service) IndexBaselineSeries() int {
|
||||
return e.index.BaselineSeries()
|
||||
}
|
||||
|
||||
// IndexLastImported returns when the dump import last completed, or the
|
||||
// zero time if it never has.
|
||||
func (e *Service) IndexLastImported() time.Time {
|
||||
return e.index.LastImported()
|
||||
}
|
||||
|
||||
// PrepareIndexRebuild clears the completion marker so the next build
|
||||
// re-imports from the newest published dump.
|
||||
func (e *Service) PrepareIndexRebuild() {
|
||||
e.index.PrepareRebuild()
|
||||
}
|
||||
|
||||
// RefreshIndexNow folds newly published incremental listens dumps into
|
||||
// the index synchronously. Pass 0 to bypass the cadence gate.
|
||||
func (e *Service) RefreshIndexNow(minInterval time.Duration) {
|
||||
e.index.RefreshNow(e.ctx, minInterval)
|
||||
}
|
||||
|
||||
// IsIndexReady returns true once the index has been populated.
|
||||
func (e *Service) IsIndexReady() bool {
|
||||
return e.index.IsReady()
|
||||
|
||||
Reference in New Issue
Block a user