Audiobook and ebook libraries previously never entered the Recently Added pipeline: availability detection only ran for TV/movie/mixed libraries and release_events only knew episode/movie kinds, so server channels (Discord/generic webhooks) could not announce new audiobooks or ebooks. Generalize the movie path into a flat-item-kind registry (internal/notifications/item_kind.go) driving availability detection, recording, channel toggles, payload rendering, test fixtures, and the admin backfill seeder. New kinds share a kind-discriminated item_availability table; movie_availability stays as-is. Channels gain notify_new_audiobooks/notify_new_ebooks toggles (default on, additive API fields) and embeds carry the author from item_people. Flood-safe by construction: existing libraries seed silently on their first post-upgrade full scan. Extract internal/librarykind to replace the is*LibraryType helper copies that had drifted across scanner, libraryingest, and metadata (metadata's movie check silently included mixed; now spelled explicitly). Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
71 lines
2.8 KiB
Go
71 lines
2.8 KiB
Go
package notifications
|
|
|
|
// Availability-fact tables for flat item kinds. Movies keep the dedicated
|
|
// table they launched with; every later kind shares item_availability, which
|
|
// carries a kind discriminator column.
|
|
const (
|
|
movieAvailabilityTable = "movie_availability"
|
|
itemAvailabilityTable = "item_availability"
|
|
)
|
|
|
|
// flatItemKind describes one non-episode release-event kind: a single
|
|
// media_items row becomes newly available and is announced broadcast-only
|
|
// (server channels), with no per-profile fanout. Movies were the first flat
|
|
// kind; audiobooks and ebooks followed. Adding another kind means one entry
|
|
// here, one AvailabilityKinds flag wired in the library-ingest executor, one
|
|
// notify_new_* channel column, and a migration widening the release_events
|
|
// kind CHECK constraints.
|
|
type flatItemKind struct {
|
|
// Kind is the release_events.kind / seed-state value, and doubles as the
|
|
// dedupe-key prefix ("audiobook:{library_id}:{item_id}").
|
|
Kind string
|
|
// ItemType is the media_items.type value the kind announces. It doubles
|
|
// as the display noun in copy ("New audiobook available on Silo").
|
|
ItemType string
|
|
// AvailabilityTable receives the one-way "first became available in this
|
|
// library" facts for the kind.
|
|
AvailabilityTable string
|
|
// WantsToggle reads the channel's notify_new_* toggle for the kind.
|
|
WantsToggle func(ServerChannel) bool
|
|
// Selected reads the ingest scope's AvailabilityKinds flag for the kind.
|
|
Selected func(AvailabilityKinds) bool
|
|
}
|
|
|
|
// flatItemKinds is the ordered registry of every flat release-event kind.
|
|
// Order affects only fixture/sample ordering.
|
|
var flatItemKinds = []flatItemKind{
|
|
{
|
|
Kind: EventKindMovie,
|
|
ItemType: mediaTypeMovie,
|
|
AvailabilityTable: movieAvailabilityTable,
|
|
WantsToggle: func(c ServerChannel) bool { return c.NotifyNewMovies },
|
|
Selected: func(k AvailabilityKinds) bool { return k.Movies },
|
|
},
|
|
{
|
|
Kind: EventKindAudiobook,
|
|
ItemType: mediaTypeAudiobook,
|
|
AvailabilityTable: itemAvailabilityTable,
|
|
WantsToggle: func(c ServerChannel) bool { return c.NotifyNewAudiobooks },
|
|
Selected: func(k AvailabilityKinds) bool { return k.Audiobooks },
|
|
},
|
|
{
|
|
Kind: EventKindEbook,
|
|
ItemType: mediaTypeEbook,
|
|
AvailabilityTable: itemAvailabilityTable,
|
|
WantsToggle: func(c ServerChannel) bool { return c.NotifyNewEbooks },
|
|
Selected: func(k AvailabilityKinds) bool { return k.Ebooks },
|
|
},
|
|
}
|
|
|
|
// flatKindByString looks up a registry entry by event kind. ok is false for
|
|
// episode and for unknown kinds (a newer node's kind string reaching an old
|
|
// node), so callers skip rather than misrender what they cannot describe.
|
|
func flatKindByString(kind string) (flatItemKind, bool) {
|
|
for _, k := range flatItemKinds {
|
|
if k.Kind == kind {
|
|
return k, true
|
|
}
|
|
}
|
|
return flatItemKind{}, false
|
|
}
|