Abort in-progress scans when roots change and sync shelves after full scan.

This commit is contained in:
Jos Vooges | STH 2026-08-26 02:39:38 +02:00
parent 71c0244dd9
commit 49d2f8a186
4 changed files with 57 additions and 16 deletions

View file

@ -7,8 +7,8 @@ WORKDIR /src
RUN apk add --no-cache git ca-certificates RUN apk add --no-cache git ca-certificates
COPY node/media-node/ ./ COPY node/media-node/ ./
RUN go mod tidy \ RUN go mod tidy \
&& CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w -X main.version=1.2.2" -o /media-node-linux-amd64 ./cmd/media-node \ && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w -X main.version=1.2.3" -o /media-node-linux-amd64 ./cmd/media-node \
&& CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="-s -w -X main.version=1.2.2" -o /media-node-linux-arm64 ./cmd/media-node && CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="-s -w -X main.version=1.2.3" -o /media-node-linux-arm64 ./cmd/media-node
FROM base AS builder FROM base AS builder
COPY package.json pnpm-workspace.yaml ./ COPY package.json pnpm-workspace.yaml ./

View file

@ -24,7 +24,7 @@ import (
"github.com/sthmedia/media-node/internal/streaming" "github.com/sthmedia/media-node/internal/streaming"
) )
var version = "1.2.2" var version = "1.2.3"
func main() { func main() {
if len(os.Args) < 2 { if len(os.Args) < 2 {
@ -125,6 +125,9 @@ func runService() {
}) })
ctrlClient = control.NewClient(cfg, creds, store, sc, streamer, version, configPath) ctrlClient = control.NewClient(cfg, creds, store, sc, streamer, version, configPath)
sc.SetOnScanComplete(func() {
ctrlClient.RequestFullLibrarySync()
})
streamer.SetSessionEndHandler(func(sessionID, reason string) { streamer.SetSessionEndHandler(func(sessionID, reason string) {
ctrlClient.NotifySessionEnded(sessionID, reason) ctrlClient.NotifySessionEnded(sessionID, reason)
}) })

View file

@ -594,6 +594,10 @@ func (c *Client) flushLibraryEvents(events []scanner.LibraryEvent) {
} }
} }
func (c *Client) RequestFullLibrarySync() {
go c.sendFullSync()
}
func (c *Client) sendFullSync() { func (c *Client) sendFullSync() {
c.mu.Lock() c.mu.Lock()
if c.fullSyncActive { if c.fullSyncActive {

View file

@ -42,15 +42,16 @@ type LibraryEvent struct {
type EventHandler func(events []LibraryEvent) type EventHandler func(events []LibraryEvent)
type Scanner struct { type Scanner struct {
cfg *config.Config cfg *config.Config
store *database.Store store *database.Store
onEvents EventHandler onEvents EventHandler
mu sync.Mutex onScanComplete func()
scanning bool mu sync.Mutex
rescanQueued bool scanning bool
status string rescanQueued bool
resetSchedule chan struct{} status string
reloadWatch chan struct{} resetSchedule chan struct{}
reloadWatch chan struct{}
} }
func New(cfg *config.Config, store *database.Store, onEvents EventHandler) *Scanner { func New(cfg *config.Config, store *database.Store, onEvents EventHandler) *Scanner {
@ -64,6 +65,13 @@ func New(cfg *config.Config, store *database.Store, onEvents EventHandler) *Scan
} }
} }
// SetOnScanComplete registers a hook after a full scan finishes (no pending re-run).
func (s *Scanner) SetOnScanComplete(fn func()) {
s.mu.Lock()
s.onScanComplete = fn
s.mu.Unlock()
}
func (s *Scanner) Status() string { func (s *Scanner) Status() string {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
@ -231,15 +239,36 @@ func (s *Scanner) FullScan() {
roots := s.cfg.Media.EffectiveRoots() roots := s.cfg.Media.EffectiveRoots()
s.mu.Unlock() s.mu.Unlock()
log.Println("Starting full media scan...") log.Printf("Starting full media scan (%d roots)...", len(roots))
found := make(map[string]bool) found := make(map[string]bool)
aborted := false
for _, root := range roots { for _, root := range roots {
s.mu.Lock()
if s.rescanQueued {
s.mu.Unlock()
aborted = true
log.Printf("Aborting scan early (roots/config changed); will re-run")
break
}
s.mu.Unlock()
kind := root.Kind kind := root.Kind
if kind == "series" { if kind == "series" {
kind = "episode" kind = "episode"
} }
before := len(found)
s.walkRoot(root.Path, kind, root.ShelfID, found) s.walkRoot(root.Path, kind, root.ShelfID, found)
log.Printf("Scan root %s (%s) shelf=%s → +%d files", root.Path, kind, root.ShelfID, len(found)-before)
}
if aborted {
s.mu.Lock()
s.scanning = false
s.status = "idle"
s.mu.Unlock()
log.Println("Re-running full scan after path/config change...")
continue
} }
existing, _ := s.store.AllFiles() existing, _ := s.store.AllFiles()
@ -261,11 +290,16 @@ func (s *Scanner) FullScan() {
s.scanning = false s.scanning = false
s.status = "idle" s.status = "idle"
again := s.rescanQueued again := s.rescanQueued
doneHook := s.onScanComplete
s.mu.Unlock() s.mu.Unlock()
if !again { if again {
return log.Println("Re-running full scan after path/config change...")
continue
} }
log.Println("Re-running full scan after path/config change...") if doneHook != nil {
go doneHook()
}
return
} }
} }