Files
minstrel/internal/library/notify.go
T
bvandeusenandClaude Opus 5.5 7e6228c13f
release / govulncheck (push) Successful in 20s
release / web (push) Successful in 1m20s
release / go (push) Successful in 1m37s
release / integration (push) Successful in 5m15s
release / android (push) Successful in 5m26s
release / Build signed APK (releases and dev) (push) Successful in 5m45s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 16s
release / Verify release artifacts (tag releases only) (push) Skipped
docs: library comments say what the scanner and resolver do, not how they came to be (#6277)
Rewrites the internal/library comment blocks to rule 32. Task, milestone,
decision and rule ids are gone; the incidents behind them are restated as
the constraint they left (why a file is deleted before its row, why the
missing mark is a mark, why duplicates block on duration). Counts and
album names from one library are dropped from production comments; test
comments keep the names their fixtures use. The tagReadVersion log keeps
each version's before and after, without the measurements.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 12:27:34 -04:00

82 lines
2.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package library
import (
"context"
"sync"
"github.com/jackc/pgx/v5/pgtype"
"git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq"
"git.fabledsword.com/bvandeusen/minstrel/internal/notifications"
)
// Package-level notifier for library-health notices to admins: a
// scan that failed, tracks gone missing, new duplicates. Set once at startup
// beside SetEventBus, for the same reason the bus is package-level. Nil, as in
// tests that never set it, means nobody is told.
var (
notifierMu sync.RWMutex
notifier *notifications.Notifier
)
// SetNotifier wires the notifications inbox into the library package.
func SetNotifier(n *notifications.Notifier) {
notifierMu.Lock()
defer notifierMu.Unlock()
notifier = n
}
// notifyAdmins tells every admin about a library-health event. These kinds
// coalesce, so a burst is one unread notice with a running count.
func notifyAdmins(ctx context.Context, kind notifications.Kind, p notifications.Payload) {
notifierMu.RLock()
n := notifier
notifierMu.RUnlock()
n.NotifyLogged(ctx, kind, notifications.ToAdmins(pgtype.UUID{}), p.Map())
}
// notifyItems is how many names a notice carries; the notifications package
// keeps the newest ten across a coalesced burst.
const notifyItems = 10
// TrackLabel names a track as a notice does: "Artist – Title", or the title
// alone when the artist is unknown.
func TrackLabel(artist, title string) string {
if artist == "" {
return title
}
return artist + " – " + title
}
// notifyStillMissing tells admins which of the tracks a scan marked missing
// are still gone after it adopted moves and folded replacements. A marked
// track the scan healed is not news: reporting it would make a quality
// upgrade read as lost music.
func (s *Scanner) notifyStillMissing(ctx context.Context, marked []pgtype.UUID) {
if len(marked) == 0 || ctx.Err() != nil {
return
}
rows, err := dbq.New(s.pool).ListStillMissingTrackLabels(ctx, marked)
if err != nil {
s.logger.Warn("library scan: listing missing tracks for the notice failed", "err", err)
return
}
if len(rows) == 0 {
return
}
items := make([]string, 0, min(len(rows), notifyItems))
for _, r := range rows[:min(len(rows), notifyItems)] {
items = append(items, TrackLabel(r.ArtistName, r.Title))
}
notifyAdmins(ctx, notifications.KindTracksMissing, notifications.Payload{Count: int64(len(rows)), Items: items})
}
// notifyScanFinished tells admins a scan run ended in error. A scan cut short
// by shutdown is not a failure anyone needs telling about.
func notifyScanFinished(ctx context.Context, errMsg string) {
if errMsg == "" || ctx.Err() != nil {
return
}
notifyAdmins(ctx, notifications.KindScanFailed, notifications.Payload{Count: 1, Detail: errMsg})
}