feat: add trailer, characters, and recommendations to anime details
This commit is contained in:
@@ -6,6 +6,30 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func (c *Client) GetAnimeCharacters(ctx context.Context, id int) ([]CharacterEntry, error) {
|
||||
url := fmt.Sprintf("%s/anime/%d/characters", c.baseURL, id)
|
||||
cacheKey := fmt.Sprintf("anime:characters:%d", id)
|
||||
|
||||
var resp CharactersResponse
|
||||
if err := c.getWithCache(ctx, cacheKey, 24*time.Hour, url, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp.Data, nil
|
||||
}
|
||||
|
||||
func (c *Client) GetAnimeRecommendations(ctx context.Context, id int) ([]RecommendationEntry, error) {
|
||||
url := fmt.Sprintf("%s/anime/%d/recommendations", c.baseURL, id)
|
||||
cacheKey := fmt.Sprintf("anime:recommendations:%d", id)
|
||||
|
||||
var resp RecommendationsResponse
|
||||
if err := c.getWithCache(ctx, cacheKey, 24*time.Hour, url, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp.Data, nil
|
||||
}
|
||||
|
||||
func (c *Client) GetAnimeByID(ctx context.Context, id int) (Anime, error) {
|
||||
cacheKey := fmt.Sprintf("anime:%d", id)
|
||||
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
package jikan
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
type RecommendationEntry struct {
|
||||
Entry struct {
|
||||
MalID int `json:"mal_id"`
|
||||
URL string `json:"url"`
|
||||
Images struct {
|
||||
Webp struct {
|
||||
LargeImageURL string `json:"large_image_url"`
|
||||
} `json:"webp"`
|
||||
} `json:"images"`
|
||||
Title string `json:"title"`
|
||||
} `json:"entry"`
|
||||
Votes int `json:"votes"`
|
||||
}
|
||||
|
||||
type RecommendationsResponse struct {
|
||||
Data []RecommendationEntry `json:"data"`
|
||||
}
|
||||
|
||||
func (c *Client) GetRecommendations(ctx context.Context, animeID int, limit int) ([]Anime, error) {
|
||||
cacheKey := fmt.Sprintf("recs:%d", animeID)
|
||||
|
||||
var cached []Anime
|
||||
if c.getCache(ctx, cacheKey, &cached) {
|
||||
if limit > 0 && len(cached) > limit {
|
||||
return cached[:limit], nil
|
||||
}
|
||||
return cached, nil
|
||||
}
|
||||
|
||||
var result RecommendationsResponse
|
||||
reqURL := fmt.Sprintf("%s/anime/%d/recommendations", c.baseURL, animeID)
|
||||
|
||||
if err := c.fetchWithRetry(ctx, reqURL, &result); err != nil {
|
||||
var stale []Anime
|
||||
if c.getStaleCache(ctx, cacheKey, &stale) {
|
||||
if limit > 0 && len(stale) > limit {
|
||||
return stale[:limit], nil
|
||||
}
|
||||
return stale, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
max := len(result.Data)
|
||||
if limit > 0 && max > limit {
|
||||
max = limit
|
||||
}
|
||||
|
||||
animes := make([]Anime, 0, max)
|
||||
for i := 0; i < max; i++ {
|
||||
rec := result.Data[i]
|
||||
|
||||
var fullAnime Anime
|
||||
animeCacheKey := fmt.Sprintf("anime:%d", rec.Entry.MalID)
|
||||
|
||||
if c.getCache(ctx, animeCacheKey, &fullAnime) {
|
||||
animes = append(animes, fullAnime)
|
||||
} else {
|
||||
anime := Anime{
|
||||
MalID: rec.Entry.MalID,
|
||||
Title: rec.Entry.Title,
|
||||
Images: struct {
|
||||
Jpg struct {
|
||||
LargeImageURL string `json:"large_image_url"`
|
||||
} `json:"jpg"`
|
||||
Webp struct {
|
||||
LargeImageURL string `json:"large_image_url"`
|
||||
} `json:"webp"`
|
||||
}{
|
||||
Webp: struct {
|
||||
LargeImageURL string `json:"large_image_url"`
|
||||
}{
|
||||
LargeImageURL: rec.Entry.Images.Webp.LargeImageURL,
|
||||
},
|
||||
},
|
||||
}
|
||||
animes = append(animes, anime)
|
||||
}
|
||||
}
|
||||
|
||||
c.setCache(ctx, cacheKey, animes, time.Hour*24)
|
||||
return animes, nil
|
||||
}
|
||||
@@ -76,6 +76,18 @@ type Anime struct {
|
||||
Timezone string `json:"timezone"`
|
||||
String string `json:"string"`
|
||||
} `json:"broadcast"`
|
||||
Trailer struct {
|
||||
YoutubeID string `json:"youtube_id"`
|
||||
URL string `json:"url"`
|
||||
EmbedURL string `json:"embed_url"`
|
||||
Images struct {
|
||||
ImageURL string `json:"image_url"`
|
||||
SmallImageURL string `json:"small_image_url"`
|
||||
MediumImageURL string `json:"medium_image_url"`
|
||||
LargeImageURL string `json:"large_image_url"`
|
||||
MaximumImageURL string `json:"maximum_image_url"`
|
||||
} `json:"images"`
|
||||
} `json:"trailer"`
|
||||
Streaming []struct {
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url"`
|
||||
@@ -87,6 +99,62 @@ type Anime struct {
|
||||
} `json:"external"`
|
||||
}
|
||||
|
||||
type CharacterVoiceActor struct {
|
||||
Person struct {
|
||||
MalID int `json:"mal_id"`
|
||||
URL string `json:"url"`
|
||||
Images struct {
|
||||
Jpg struct {
|
||||
ImageURL string `json:"image_url"`
|
||||
} `json:"jpg"`
|
||||
} `json:"images"`
|
||||
Name string `json:"name"`
|
||||
} `json:"person"`
|
||||
Language string `json:"language"`
|
||||
}
|
||||
|
||||
type CharacterEntry struct {
|
||||
Character struct {
|
||||
MalID int `json:"mal_id"`
|
||||
URL string `json:"url"`
|
||||
Images struct {
|
||||
Jpg struct {
|
||||
ImageURL string `json:"image_url"`
|
||||
} `json:"jpg"`
|
||||
Webp struct {
|
||||
ImageURL string `json:"image_url"`
|
||||
SmallImageURL string `json:"small_image_url"`
|
||||
} `json:"webp"`
|
||||
} `json:"images"`
|
||||
Name string `json:"name"`
|
||||
} `json:"character"`
|
||||
Role string `json:"role"`
|
||||
VoiceActors []CharacterVoiceActor `json:"voice_actors"`
|
||||
}
|
||||
|
||||
type CharactersResponse struct {
|
||||
Data []CharacterEntry `json:"data"`
|
||||
}
|
||||
|
||||
type RecommendationEntry struct {
|
||||
Entry struct {
|
||||
MalID int `json:"mal_id"`
|
||||
URL string `json:"url"`
|
||||
Images struct {
|
||||
Webp struct {
|
||||
LargeImageURL string `json:"large_image_url"`
|
||||
} `json:"webp"`
|
||||
} `json:"images"`
|
||||
Title string `json:"title"`
|
||||
} `json:"entry"`
|
||||
URL string `json:"url"`
|
||||
Votes int `json:"votes"`
|
||||
}
|
||||
|
||||
type RecommendationsResponse struct {
|
||||
Data []RecommendationEntry `json:"data"`
|
||||
}
|
||||
|
||||
func (a Anime) ScoredByFormatted() string {
|
||||
return formatNumber(a.ScoredBy)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user