From 5e63c3551d8fbf90ee905ec82f924f83f5226424 Mon Sep 17 00:00:00 2001 From: mkelvers Date: Wed, 6 May 2026 14:09:11 +0200 Subject: [PATCH] jikan: add parallel episode fetching --- api/playback/handler.go | 32 +++++------------------ integrations/jikan/episodes.go | 48 ++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 26 deletions(-) diff --git a/api/playback/handler.go b/api/playback/handler.go index f00dfae..042c714 100644 --- a/api/playback/handler.go +++ b/api/playback/handler.go @@ -56,20 +56,9 @@ func (h *Handler) HandleWatchPage(w http.ResponseWriter, r *http.Request) { return } - // Fetch episodes sequentially (pages are in correct order: 1-100, 101-200, etc.) - pageSize := 100 - var allEpisodes []jikan.Episode - for page := 1; ; page++ { - resp, err := h.jikanClient.GetEpisodes(r.Context(), id, page) - if err != nil || len(resp.Data) == 0 { - break - } - allEpisodes = append(allEpisodes, resp.Data...) - - // If we got fewer than pageSize, we've reached the end - if len(resp.Data) < pageSize { - break - } + allEpisodes, err := h.jikanClient.GetAllEpisodes(r.Context(), id) + if err != nil { + log.Printf("failed to fetch episodes: %v", err) } user := middleware.GetUser(r.Context()) @@ -419,18 +408,9 @@ func (h *Handler) HandleEpisodeThumbnails(w http.ResponseWriter, r *http.Request return } - // Fetch episodes sequentially - pageSize := 100 - var allEpisodes []jikan.Episode - for page := 1; ; page++ { - resp, err := h.jikanClient.GetEpisodes(r.Context(), id, page) - if err != nil || len(resp.Data) == 0 { - break - } - allEpisodes = append(allEpisodes, resp.Data...) - if len(resp.Data) < pageSize { - break - } + allEpisodes, err := h.jikanClient.GetAllEpisodes(r.Context(), id) + if err != nil { + log.Printf("failed to fetch thumbnails/episodes: %v", err) } // Fill gaps if anime has known total diff --git a/integrations/jikan/episodes.go b/integrations/jikan/episodes.go index b1c1d0d..c202ec4 100644 --- a/integrations/jikan/episodes.go +++ b/integrations/jikan/episodes.go @@ -3,6 +3,7 @@ package jikan import ( "context" "fmt" + "sync" "time" ) @@ -19,6 +20,53 @@ func (c *Client) GetEpisodes(ctx context.Context, animeID int, page int) (Episod return result, err } +func (c *Client) GetAllEpisodes(ctx context.Context, animeID int) ([]Episode, error) { + // First page to get total pages + first, err := c.GetEpisodes(ctx, animeID, 1) + if err != nil { + return nil, err + } + + if first.Pagination.LastVisiblePage <= 1 { + return first.Data, nil + } + + all := make([][]Episode, first.Pagination.LastVisiblePage) + all[0] = first.Data + + var wg sync.WaitGroup + errs := make(chan error, first.Pagination.LastVisiblePage-1) + + // Fetch remaining pages in parallel + // Note: Client.getWithCache handles rate limiting (3 req/sec) + for p := 2; p <= first.Pagination.LastVisiblePage; p++ { + wg.Add(1) + go func(page int) { + defer wg.Done() + resp, err := c.GetEpisodes(ctx, animeID, page) + if err != nil { + errs <- err + return + } + all[page-1] = resp.Data + }(p) + } + + wg.Wait() + close(errs) + + if err := <-errs; err != nil { + return nil, err + } + + var result []Episode + for _, pageData := range all { + result = append(result, pageData...) + } + + return result, nil +} + func (c *Client) GetEpisodesRange(ctx context.Context, animeID int, startPage, endPage int) ([]Episode, error) { var all []Episode for page := startPage; page <= endPage; page++ {