feat(daemon): add on-demand library scan via heartbeat and WebSocket

This commit is contained in:
Deivid Soto 2026-04-07 11:36:42 +02:00
parent 4cf07c411c
commit a9179dc758
4 changed files with 56 additions and 15 deletions

View file

@ -55,6 +55,9 @@ type Daemon struct {
// pollNow triggers an immediate poll (e.g. on resume)
pollNow chan struct{}
// ScanNow triggers an immediate library scan (from heartbeat or WebSocket control event)
ScanNow chan struct{}
}
// NewDaemon creates a daemon with the given transport.
@ -71,6 +74,7 @@ func NewDaemon(cfg DaemonConfig, transport Transport) *Daemon {
cfg: cfg,
transport: transport,
pollNow: make(chan struct{}, 1),
ScanNow: make(chan struct{}, 1),
}
}
@ -236,6 +240,15 @@ func (d *Daemon) heartbeat(ctx context.Context) {
}
WriteState(&d.State)
// Trigger library scan if requested
if resp.Scan {
log.Printf("Library scan requested by server")
select {
case d.ScanNow <- struct{}{}:
default: // scan already pending
}
}
// Log once per version when server suggests an upgrade
if resp.Upgrade != nil && resp.Upgrade.Version != "" && resp.Upgrade.Version != d.lastNotifiedVersion {
d.lastNotifiedVersion = resp.Upgrade.Version
@ -266,9 +279,17 @@ func (d *Daemon) handleEvent(event ServerEvent) {
}
case "control":
if event.Control != nil && d.OnControlAction != nil {
if event.Control != nil {
log.Printf("Control action via WebSocket: %s task %s", event.Control.Action, event.Control.TaskID)
d.OnControlAction(event.Control.Action, event.Control.TaskID)
if event.Control.Action == "scan" {
select {
case d.ScanNow <- struct{}{}:
default:
}
}
if d.OnControlAction != nil {
d.OnControlAction(event.Control.Action, event.Control.TaskID)
}
}
case "disconnected":

View file

@ -137,6 +137,7 @@ type HeartbeatResponse struct {
Success bool `json:"success"`
Upgrade *UpgradeSignal `json:"upgrade,omitempty"`
Watching bool `json:"watching,omitempty"` // true when a user is viewing download progress in the web UI
Scan bool `json:"scan,omitempty"` // true when user triggered a library scan from the web UI
}
// UpgradeSignal tells the agent to upgrade to a specific version.
@ -290,9 +291,10 @@ type DebridAccount struct {
// LibrarySyncRequest sends scanned media items to the server.
type LibrarySyncRequest struct {
Items []LibrarySyncItem `json:"items"`
ScanPath string `json:"scanPath"`
IsLastBatch bool `json:"isLastBatch"`
Items []LibrarySyncItem `json:"items"`
ScanPath string `json:"scanPath"`
IsLastBatch bool `json:"isLastBatch"`
SyncStartedAt string `json:"syncStartedAt,omitempty"` // ISO-8601; same for all batches in a session
}
// LibrarySyncItem is a single scanned media file with ffprobe metadata.