Abort in-progress scans when roots change and sync shelves after full scan.
This commit is contained in:
parent
71c0244dd9
commit
49d2f8a186
4 changed files with 57 additions and 16 deletions
|
|
@ -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 ./
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue