Make full scans local-only then sync; log RESCAN and per-root progress.

This commit is contained in:
Jos Vooges | STH 2026-08-26 02:46:12 +02:00
parent 49d2f8a186
commit 9168a159f7
4 changed files with 28 additions and 23 deletions

View file

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

View file

@ -24,7 +24,7 @@ import (
"github.com/sthmedia/media-node/internal/streaming"
)
var version = "1.2.3"
var version = "1.2.4"
func main() {
if len(os.Args) < 2 {

View file

@ -356,6 +356,7 @@ func (c *Client) handleMessage(msgType string, payload json.RawMessage) {
log.Printf("Playback session revoked: %s", p.SessionID)
}
case "RESCAN":
log.Println("RESCAN requested by master")
go c.scanner.FullScan()
case "FULL_LIBRARY_SYNC":
go c.sendFullSync()

View file

@ -231,6 +231,7 @@ func (s *Scanner) FullScan() {
if s.scanning {
s.rescanQueued = true
s.mu.Unlock()
log.Println("Full scan already running — queued re-run after current pass")
return
}
s.scanning = true
@ -258,8 +259,9 @@ func (s *Scanner) FullScan() {
kind = "episode"
}
before := len(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)
log.Printf("Scanning root %s (%s) shelf=%s ...", root.Path, kind, root.ShelfID)
s.walkRoot(root.Path, kind, root.ShelfID, found, false)
log.Printf("Scan root done %s → +%d files (total %d)", root.Path, len(found)-before, len(found))
}
if aborted {
@ -271,31 +273,29 @@ func (s *Scanner) FullScan() {
continue
}
// Local prune only — Master learns removals via the full library sync that follows.
existing, _ := s.store.AllFiles()
removed := 0
for _, f := range existing {
if !found[f.Path] {
rev, _ := s.store.NextRevision()
event := LibraryEvent{
Type: "FILE_REMOVED",
NodeRevision: rev,
File: fileToInfo(f),
}
_ = s.store.DeleteFile(f.LocalFileID)
s.onEvents([]LibraryEvent{event})
removed++
}
}
log.Printf("Full scan completed (%d files)", len(found))
log.Printf("Full scan completed (%d files, %d removed locally)", len(found), removed)
s.mu.Lock()
s.scanning = false
s.status = "idle"
again := s.rescanQueued
doneHook := s.onScanComplete
s.mu.Unlock()
if again {
if s.rescanQueued {
s.scanning = false
s.status = "idle"
s.mu.Unlock()
log.Println("Re-running full scan after path/config change...")
continue
}
s.scanning = false
s.status = "idle"
doneHook := s.onScanComplete
s.mu.Unlock()
if doneHook != nil {
go doneHook()
}
@ -303,7 +303,7 @@ func (s *Scanner) FullScan() {
}
}
func (s *Scanner) walkRoot(dir, mediaType, shelfID string, found map[string]bool) {
func (s *Scanner) walkRoot(dir, mediaType, shelfID string, found map[string]bool, emitEvents bool) {
if dir == "" {
return
}
@ -315,7 +315,7 @@ func (s *Scanner) walkRoot(dir, mediaType, shelfID string, found map[string]bool
return nil
}
found[path] = true
s.indexFile(path, info, "FILE_UPDATED", mediaType, shelfID)
s.indexFile(path, info, "FILE_UPDATED", mediaType, shelfID, emitEvents)
return nil
})
}
@ -425,7 +425,7 @@ func (s *Scanner) runWatchSession() {
info, err := os.Stat(path)
if err == nil && !info.IsDir() && isVideo(path) {
kind, shelf := s.detectRootMeta(path, rootType, rootShelf)
s.indexFile(path, info, "FILE_ADDED", kind, shelf)
s.indexFile(path, info, "FILE_ADDED", kind, shelf, true)
}
}
pending = make(map[string]struct{})
@ -474,7 +474,7 @@ func (s *Scanner) detectRootType(path string, rootType map[string]string) string
return detectMediaType(path)
}
func (s *Scanner) indexFile(path string, info os.FileInfo, eventType, preferredType, shelfID string) {
func (s *Scanner) indexFile(path string, info os.FileInfo, eventType, preferredType, shelfID string, emitEvents bool) {
mediaType := preferredType
if mediaType == "" {
mediaType = detectMediaType(path)
@ -504,6 +504,10 @@ func (s *Scanner) indexFile(path string, info os.FileInfo, eventType, preferredT
}
_ = s.store.UpsertFile(lf)
if !emitEvents {
return
}
info2 := buildMediaInfo(lf)
info2.ShelfID = shelfID
event := LibraryEvent{