132 lines
3.5 KiB
Go
132 lines
3.5 KiB
Go
package anime
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"mal/internal/database"
|
|
"mal/internal/jikan"
|
|
)
|
|
|
|
type Service struct {
|
|
jikanClient *jikan.Client
|
|
db database.Querier
|
|
}
|
|
|
|
func NewService(jikanClient *jikan.Client, db database.Querier) *Service {
|
|
return &Service{
|
|
jikanClient: jikanClient,
|
|
db: db,
|
|
}
|
|
}
|
|
|
|
func (s *Service) Search(ctx context.Context, query string, page int) (jikan.SearchResult, error) {
|
|
return s.jikanClient.Search(ctx, query, page)
|
|
}
|
|
|
|
func (s *Service) QuickSearch(ctx context.Context, query string, page int, limit int) (jikan.SearchResult, error) {
|
|
return s.jikanClient.SearchWithLimit(ctx, query, page, limit)
|
|
}
|
|
|
|
func (s *Service) GetTopAnime(ctx context.Context, page int) (jikan.TopAnimeResult, error) {
|
|
return s.jikanClient.GetTopAnime(ctx, page)
|
|
}
|
|
|
|
func (s *Service) GetTopAnimeWithPlaceholder(ctx context.Context, page int) (jikan.TopAnimeResult, bool, error) {
|
|
result, err := s.jikanClient.GetTopAnime(ctx, page)
|
|
if err == nil {
|
|
return result, false, nil
|
|
}
|
|
|
|
if jikan.IsRetryableError(err) {
|
|
return jikan.TopAnimeResult{}, true, nil
|
|
}
|
|
|
|
return jikan.TopAnimeResult{}, false, err
|
|
}
|
|
|
|
func (s *Service) GetAiringAnime(ctx context.Context, page int) (jikan.TopAnimeResult, error) {
|
|
return s.jikanClient.GetSeasonsNow(ctx, page)
|
|
}
|
|
|
|
func (s *Service) GetUpcomingAnime(ctx context.Context, page int) (jikan.TopAnimeResult, error) {
|
|
return s.jikanClient.GetSeasonsUpcoming(ctx, page)
|
|
}
|
|
|
|
func (s *Service) GetAnimeDetails(ctx context.Context, id int, userID string) (jikan.Anime, string, int, error) {
|
|
anime, err := s.jikanClient.GetAnimeByID(ctx, id)
|
|
if err != nil {
|
|
if jikan.IsNotFoundError(err) {
|
|
return jikan.Anime{}, "", 1, err
|
|
}
|
|
|
|
s.jikanClient.EnqueueAnimeFetchRetry(ctx, id, err)
|
|
if jikan.IsRetryableError(err) {
|
|
return jikan.Anime{}, "", 1, ErrAnimePendingFetch
|
|
}
|
|
|
|
return jikan.Anime{}, "", 1, fmt.Errorf("failed to fetch anime details: %w", err)
|
|
}
|
|
|
|
currentStatus := ""
|
|
nextEpisode := 1
|
|
if userID != "" {
|
|
entry, err := s.db.GetWatchListEntry(ctx, database.GetWatchListEntryParams{
|
|
UserID: userID,
|
|
AnimeID: int64(id),
|
|
})
|
|
if err == nil {
|
|
currentStatus = entry.Status
|
|
if entry.CurrentEpisode.Valid {
|
|
value := int(entry.CurrentEpisode.Int64)
|
|
if value > 0 {
|
|
nextEpisode = value
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return anime, currentStatus, nextEpisode, nil
|
|
}
|
|
|
|
func (s *Service) GetRelations(ctx context.Context, id int) ([]jikan.RelationEntry, error) {
|
|
return s.jikanClient.GetFullRelations(ctx, id)
|
|
}
|
|
|
|
func (s *Service) GetRecommendations(ctx context.Context, animeID int, limit int) ([]jikan.Anime, error) {
|
|
return s.jikanClient.GetRecommendations(ctx, animeID, limit)
|
|
}
|
|
|
|
func (s *Service) GetAnimeByProducer(ctx context.Context, producerID int, page int) (jikan.StudioAnimeResult, error) {
|
|
return s.jikanClient.GetAnimeByProducer(ctx, producerID, page)
|
|
}
|
|
|
|
func (s *Service) GetProducerByID(ctx context.Context, producerID int) (jikan.ProducerResponse, error) {
|
|
return s.jikanClient.GetProducerByID(ctx, producerID)
|
|
}
|
|
|
|
func (s *Service) GetEpisodes(ctx context.Context, animeID int) ([]jikan.Episode, error) {
|
|
var allEpisodes []jikan.Episode
|
|
page := 1
|
|
|
|
for page <= 20 {
|
|
result, err := s.jikanClient.GetEpisodes(ctx, animeID, page)
|
|
if err != nil {
|
|
if jikan.IsRetryableError(err) && len(allEpisodes) > 0 {
|
|
// Return what we have if we're getting rate limited
|
|
return allEpisodes, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
allEpisodes = append(allEpisodes, result.Data...)
|
|
|
|
if !result.Pagination.HasNextPage {
|
|
break
|
|
}
|
|
page++
|
|
}
|
|
|
|
return allEpisodes, nil
|
|
}
|