diff --git a/api/anime/handler.go b/api/anime/handler.go index 6dbc608..196dfb7 100644 --- a/api/anime/handler.go +++ b/api/anime/handler.go @@ -119,11 +119,24 @@ func (h *Handler) HandleBrowse(w http.ResponseWriter, r *http.Request) { orderBy := r.URL.Query().Get("order_by") sort := r.URL.Query().Get("sort") - res, err := h.jikanClient.SearchAdvanced(r.Context(), q, animeType, status, orderBy, sort, 1, 24) + var genres []int + for _, g := range r.URL.Query()["genres"] { + id, err := strconv.Atoi(g) + if err == nil { + genres = append(genres, id) + } + } + + res, err := h.jikanClient.SearchAdvanced(r.Context(), q, animeType, status, orderBy, sort, genres, 1, 24) if err != nil { log.Printf("browse error: %v", err) } + genresList, err := h.jikanClient.GetAnimeGenres(r.Context()) + if err != nil { + log.Printf("genres error: %v", err) + } + watchlistMap := make(map[int64]bool) var watchlistIDs []int64 if user != nil { @@ -137,12 +150,14 @@ func (h *Handler) HandleBrowse(w http.ResponseWriter, r *http.Request) { if err := templates.GetRenderer().ExecuteTemplate(w, "browse.gohtml", map[string]any{ "User": user, - "CurrentPath": r.URL.Path, + "CurrentPath": r.URL.Path, "Query": q, "Type": animeType, "Status": status, "OrderBy": orderBy, "Sort": sort, + "Genres": genres, + "GenresList": genresList, "Animes": res.Animes, "WatchlistMap": watchlistMap, "WatchlistIDs": watchlistIDs, @@ -255,7 +270,7 @@ func (h *Handler) HandleQuickSearch(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode([]quickSearchResult{}) return } - res, err := h.jikanClient.SearchAdvanced(r.Context(), query, "", "", "", "", 1, 5) + res, err := h.jikanClient.SearchAdvanced(r.Context(), query, "", "", "", "", nil, 1, 5) if err != nil { log.Printf("quick search error: %v", err) w.WriteHeader(http.StatusOK) diff --git a/integrations/jikan/constants.go b/integrations/jikan/constants.go index 4afdf62..a318dc7 100644 --- a/integrations/jikan/constants.go +++ b/integrations/jikan/constants.go @@ -3,3 +3,4 @@ package jikan import "time" const shortCacheTTL = time.Hour +const longCacheTTL = time.Hour * 24 diff --git a/integrations/jikan/search.go b/integrations/jikan/search.go index 435cbe6..a372322 100644 --- a/integrations/jikan/search.go +++ b/integrations/jikan/search.go @@ -4,13 +4,15 @@ import ( "context" "fmt" "net/url" + "strconv" + "strings" ) func (c *Client) Search(ctx context.Context, query string, page int) (SearchResult, error) { return c.search(ctx, query, page, 0) } -func (c *Client) SearchAdvanced(ctx context.Context, query, animeType, status, orderBy, sort string, page, limit int) (SearchResult, error) { +func (c *Client) SearchAdvanced(ctx context.Context, query, animeType, status, orderBy, sort string, genres []int, page, limit int) (SearchResult, error) { if page < 1 { page = 1 } @@ -18,7 +20,16 @@ func (c *Client) SearchAdvanced(ctx context.Context, query, animeType, status, o limit = 0 } - cacheKey := fmt.Sprintf("search:%s:%s:%s:%s:%s:%d:%d", query, animeType, status, orderBy, sort, page, limit) + genresParam := "" + if len(genres) > 0 { + ids := make([]string, len(genres)) + for i, g := range genres { + ids[i] = strconv.Itoa(g) + } + genresParam = strings.Join(ids, ",") + } + + cacheKey := fmt.Sprintf("search:%s:%s:%s:%s:%s:%s:%d:%d", query, animeType, status, orderBy, sort, genresParam, page, limit) var result SearchResponse reqURL := fmt.Sprintf("%s/anime?page=%d", c.baseURL, page) @@ -37,6 +48,9 @@ func (c *Client) SearchAdvanced(ctx context.Context, query, animeType, status, o if sort != "" { reqURL += "&sort=" + url.QueryEscape(sort) } + if genresParam != "" { + reqURL += "&genres=" + genresParam + } if limit > 0 { reqURL += fmt.Sprintf("&limit=%d", limit) } @@ -127,3 +141,16 @@ func (c *Client) GetTopAnime(ctx context.Context, page int) (TopAnimeResult, err HasNextPage: result.Pagination.HasNextPage, }, nil } + +func (c *Client) GetAnimeGenres(ctx context.Context) ([]Genre, error) { + const cacheKey = "anime_genres" + + var result GenresResponse + reqURL := fmt.Sprintf("%s/genres/anime", c.baseURL) + + if err := c.getWithCache(ctx, cacheKey, longCacheTTL, reqURL, &result); err != nil { + return nil, err + } + + return result.Data, nil +} diff --git a/integrations/jikan/types.go b/integrations/jikan/types.go index bebd857..63e47de 100644 --- a/integrations/jikan/types.go +++ b/integrations/jikan/types.go @@ -143,6 +143,15 @@ type AnimeResponse struct { Data Anime `json:"data"` } +type Genre struct { + MalID int `json:"mal_id"` + Name string `json:"name"` +} + +type GenresResponse struct { + Data []Genre `json:"data"` +} + type SearchResponse struct { Data []Anime `json:"data"` Pagination Pagination `json:"pagination"`