From aed61b8b613815da50fd2dd19bfe0941197b0fdc Mon Sep 17 00:00:00 2001 From: mkelvers Date: Wed, 17 Jun 2026 18:54:58 +0200 Subject: [PATCH] feat: fetch actual episode count for airing anime --- internal/anime/details_handler.go | 40 ++++++++++++++++++++++++++++++- internal/anime/handler_test.go | 14 +++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/internal/anime/details_handler.go b/internal/anime/details_handler.go index c4ee6fa..e981467 100644 --- a/internal/anime/details_handler.go +++ b/internal/anime/details_handler.go @@ -18,8 +18,20 @@ const ( animeSectionTimeout = 12 * time.Second watchOrderTimeout = 15 * time.Second audioLookupTimeout = 8 * time.Second + episodeCountTimeout = 4 * time.Second ) +func listedEpisodeCount(episodes []domain.EpisodeData) int { + count := 0 + for _, episode := range episodes { + if episode.MalID <= 0 || episode.IsRecap { + continue + } + count++ + } + return count +} + func releasedEpisodeCount(anime domain.Anime, now time.Time) int { if !anime.Airing || anime.Aired.From == "" { return 0 @@ -37,6 +49,32 @@ func releasedEpisodeCount(anime domain.Anime, now time.Time) int { return count } +func (h *AnimeHandler) animeEpisodeCount(ctx context.Context, anime domain.Anime, now time.Time) int { + if h.svc != nil && anime.Airing { + episodeCtx, cancel := context.WithTimeout(ctx, episodeCountTimeout) + defer cancel() + + episodes, err := h.svc.GetAllEpisodes(episodeCtx, anime.MalID) + if err == nil { + if count := listedEpisodeCount(episodes); count > 0 { + return count + } + } else { + observability.Warn( + "anime_episode_count_fetch_failed", + "anime", + "", + map[string]any{ + "anime_id": anime.MalID, + }, + err, + ) + } + } + + return releasedEpisodeCount(anime, now) +} + func animeAudioAvailabilityLabel(episodes []domain.CanonicalEpisode) string { hasKnownSub := false for _, episode := range episodes { @@ -122,7 +160,7 @@ func (h *AnimeHandler) HandleAnimeDetails(c *gin.Context) { } audioAvailability := h.animeAudioAvailability(c.Request.Context(), anime) - episodesCount := releasedEpisodeCount(anime, time.Now()) + episodesCount := h.animeEpisodeCount(c.Request.Context(), anime, time.Now()) c.HTML(http.StatusOK, "anime.gohtml", gin.H{ "Anime": anime, diff --git a/internal/anime/handler_test.go b/internal/anime/handler_test.go index 36434c3..dcb708e 100644 --- a/internal/anime/handler_test.go +++ b/internal/anime/handler_test.go @@ -113,6 +113,20 @@ func TestReleasedEpisodeCount(t *testing.T) { } } +func TestListedEpisodeCount(t *testing.T) { + episodes := []domain.EpisodeData{ + {MalID: 1, Title: "Episode 1"}, + {MalID: 2, Title: "Episode 2"}, + {MalID: 3, Title: "Recap", IsRecap: true}, + {Title: "missing id"}, + } + + got := listedEpisodeCount(episodes) + if got != 2 { + t.Fatalf("listedEpisodeCount() = %d, want 2", got) + } +} + func TestAnimeAudioAvailabilityLabel(t *testing.T) { tests := []struct { name string