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>
83 lines
2.5 KiB
Go
83 lines
2.5 KiB
Go
package librarykind
|
|
|
|
import "testing"
|
|
|
|
func TestPredicates(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
fn func(string) bool
|
|
in string
|
|
want bool
|
|
}{
|
|
{"IsMovie", IsMovie, "movie", true},
|
|
{"IsMovie", IsMovie, "movies", true},
|
|
{"IsMovie", IsMovie, " MOVIES ", true},
|
|
{"IsMovie", IsMovie, "mixed", false},
|
|
{"IsMovie", IsMovie, "series", false},
|
|
{"IsMovie", IsMovie, "", false},
|
|
|
|
{"IsTV", IsTV, "series", true},
|
|
{"IsTV", IsTV, "tv", true},
|
|
{"IsTV", IsTV, "show", true},
|
|
{"IsTV", IsTV, "tvshows", true},
|
|
{"IsTV", IsTV, "TV", true},
|
|
{"IsTV", IsTV, "mixed", false},
|
|
{"IsTV", IsTV, "movies", false},
|
|
|
|
{"IsMixed", IsMixed, "mixed", true},
|
|
{"IsMixed", IsMixed, " Mixed ", true},
|
|
{"IsMixed", IsMixed, "movies", false},
|
|
|
|
{"IsAudiobook", IsAudiobook, "audiobooks", true},
|
|
{"IsAudiobook", IsAudiobook, "audiobook", true},
|
|
{"IsAudiobook", IsAudiobook, "Audiobook", true},
|
|
{"IsAudiobook", IsAudiobook, " AUDIOBOOKS ", true},
|
|
{"IsAudiobook", IsAudiobook, "movies", false},
|
|
{"IsAudiobook", IsAudiobook, "series", false},
|
|
{"IsAudiobook", IsAudiobook, "", false},
|
|
|
|
{"IsPodcast", IsPodcast, "podcasts", true},
|
|
{"IsPodcast", IsPodcast, "podcast", true},
|
|
{"IsPodcast", IsPodcast, "Podcast", true},
|
|
{"IsPodcast", IsPodcast, " PODCASTS ", true},
|
|
{"IsPodcast", IsPodcast, "series", false},
|
|
{"IsPodcast", IsPodcast, "audiobooks", false},
|
|
{"IsPodcast", IsPodcast, "", false},
|
|
|
|
{"IsEbook", IsEbook, "ebooks", true},
|
|
{"IsEbook", IsEbook, "ebook", true},
|
|
{"IsEbook", IsEbook, "Ebook", true},
|
|
{"IsEbook", IsEbook, " EBOOKS ", true},
|
|
{"IsEbook", IsEbook, "audiobooks", false},
|
|
{"IsEbook", IsEbook, "movies", false},
|
|
{"IsEbook", IsEbook, "", false},
|
|
|
|
{"IsManga", IsManga, "manga", true},
|
|
{"IsManga", IsManga, "Manga", true},
|
|
{"IsManga", IsManga, " MANGA ", true},
|
|
{"IsManga", IsManga, "ebooks", false},
|
|
{"IsManga", IsManga, "movies", false},
|
|
{"IsManga", IsManga, "", false},
|
|
}
|
|
for _, tc := range cases {
|
|
if got := tc.fn(tc.in); got != tc.want {
|
|
t.Errorf("%s(%q) = %v, want %v", tc.name, tc.in, got, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestOf(t *testing.T) {
|
|
if got := Of(" Audiobooks "); got != (Kinds{Audiobook: true}) {
|
|
t.Errorf("Of(audiobooks) = %+v", got)
|
|
}
|
|
if got := Of("mixed"); got != (Kinds{Mixed: true}) {
|
|
t.Errorf("Of(mixed) = %+v", got)
|
|
}
|
|
if got := Of("movies"); got != (Kinds{Movie: true}) {
|
|
t.Errorf("Of(movies) = %+v", got)
|
|
}
|
|
if got := Of("unknown"); got != (Kinds{}) {
|
|
t.Errorf("Of(unknown) = %+v", got)
|
|
}
|
|
}
|