aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPriyansh <[email protected]>2025-08-19 13:19:30 +0530
committerPriyansh <[email protected]>2025-08-19 13:19:30 +0530
commit9fbf28c2f77ec74c75a5274cbda897217ceaf571 (patch)
tree87dab6adda634a08b3b1cf74c0dd1e616113aa30
parent6a65e3927506b0e46f18c9c1b41e952418bfd78e (diff)
downloadmetachan-9fbf28c2f77ec74c75a5274cbda897217ceaf571.tar.xz
metachan-9fbf28c2f77ec74c75a5274cbda897217ceaf571.zip
episodes router; anilist support; general fixes
-rw-r--r--controllers/anime.go61
-rw-r--r--database/anime.go9
-rw-r--r--database/anime_cache.go17
-rw-r--r--entities/anime.go4
-rw-r--r--router/router.go5
-rw-r--r--services/anime/helpers.go18
-rw-r--r--services/anime/service.go2
7 files changed, 96 insertions, 20 deletions
diff --git a/controllers/anime.go b/controllers/anime.go
index 7c01768..baaf6f9 100644
--- a/controllers/anime.go
+++ b/controllers/anime.go
@@ -2,6 +2,7 @@ package controllers
import (
"metachan/database"
+ "metachan/entities"
animeService "metachan/services/anime"
"metachan/utils/logger"
"metachan/utils/mappers"
@@ -22,18 +23,9 @@ func getAnimeService() *animeService.Service {
// GetAnimeByMALID fetches anime details by MAL ID
func GetAnimeByMALID(c *fiber.Ctx) error {
- malID := c.Params("mal_id")
- if malID == "" {
- return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
- "error": "Query parameter MAL ID is required",
- })
- }
-
- mapping, err := database.GetAnimeMappingViaMALID(mappers.ForceInt(malID))
+ mapping, err := getAnimeMapping(c)
if err != nil {
- return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
- "error": "Anime not found",
- })
+ return err
}
service := getAnimeService()
@@ -50,3 +42,50 @@ func GetAnimeByMALID(c *fiber.Ctx) error {
return c.JSON(anime)
}
+
+// GetAnimeEpisodesByMALID fetches anime episodes by MAL ID
+func GetAnimeEpisodesByMALID(c *fiber.Ctx) error {
+ mapping, err := getAnimeMapping(c)
+ if err != nil {
+ return err
+ }
+
+ service := getAnimeService()
+ anime, err := service.GetAnimeDetails(mapping)
+ if err != nil {
+ logger.Log("Failed to fetch anime episodes: "+err.Error(), logger.LogOptions{
+ Level: logger.Error,
+ Prefix: "AnimeAPI",
+ })
+ return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
+ "error": "Failed to fetch anime episodes",
+ })
+ }
+
+ // Return only the episodes data
+ return c.JSON(anime.Episodes)
+}
+
+func getAnimeMapping(c *fiber.Ctx) (*entities.AnimeMapping, error) {
+ isAnilist := c.Query("provider") == "anilist"
+ malID := c.Params("id")
+ if malID == "" {
+ return nil, c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
+ "error": "Query parameter MAL ID is required",
+ })
+ }
+
+ var mapping *entities.AnimeMapping
+ var err error
+ if isAnilist {
+ mapping, err = database.GetAnimeMappingViaAnilistID(mappers.ForceInt(malID))
+ } else {
+ mapping, err = database.GetAnimeMappingViaMALID(mappers.ForceInt(malID))
+ }
+ if err != nil {
+ return nil, c.Status(fiber.StatusNotFound).JSON(fiber.Map{
+ "error": "Anime mapping not found",
+ })
+ }
+ return mapping, nil
+}
diff --git a/database/anime.go b/database/anime.go
index 44382bf..9d2f9e5 100644
--- a/database/anime.go
+++ b/database/anime.go
@@ -10,6 +10,15 @@ func GetAnimeMappingViaMALID(malID int) (*entities.AnimeMapping, error) {
return &mapping, nil
}
+// GetAnimeMappingViaAnilistID retrieves an anime mapping by AniList ID
+func GetAnimeMappingViaAnilistID(anilistID int) (*entities.AnimeMapping, error) {
+ var mapping entities.AnimeMapping
+ if err := DB.Where("anilist = ?", anilistID).First(&mapping).Error; err != nil {
+ return nil, err
+ }
+ return &mapping, nil
+}
+
// GetAnimeMappingsByTVDBID retrieves all anime mappings that share the same TVDB ID
func GetAnimeMappingsByTVDBID(tvdbID int) ([]entities.AnimeMapping, error) {
var mappings []entities.AnimeMapping
diff --git a/database/anime_cache.go b/database/anime_cache.go
index b2d8d81..f26dc8b 100644
--- a/database/anime_cache.go
+++ b/database/anime_cache.go
@@ -465,7 +465,6 @@ func ConvertToTypesAnime(cachedAnime *entities.CachedAnime) *types.Anime {
if len(cachedAnime.Episodes) > 0 {
anime.Episodes.Episodes = make([]types.AnimeSingleEpisode, len(cachedAnime.Episodes))
- var subCount, dubCount int
for i, episode := range cachedAnime.Episodes {
episodeData := types.AnimeSingleEpisode{
Description: episode.Description,
@@ -489,10 +488,10 @@ func ConvertToTypesAnime(cachedAnime *entities.CachedAnime) *types.Anime {
anime.Episodes.Episodes[i] = episodeData
}
- anime.Episodes.Total = len(cachedAnime.Episodes)
- anime.Episodes.Aired = len(cachedAnime.Episodes)
- anime.Episodes.Subbed = subCount
- anime.Episodes.Dubbed = dubCount
+ anime.Episodes.Total = cachedAnime.TotalEpisodes
+ anime.Episodes.Aired = cachedAnime.AiredEpisodes
+ anime.Episodes.Subbed = cachedAnime.SubbedCount
+ anime.Episodes.Dubbed = cachedAnime.DubbedCount
}
// Convert characters
@@ -620,6 +619,10 @@ func convertToCachedAnime(animeData *types.Anime) *entities.CachedAnime {
Color: animeData.Color,
Season: animeData.Season,
Year: animeData.Year,
+ SubbedCount: animeData.Episodes.Subbed,
+ DubbedCount: animeData.Episodes.Dubbed,
+ TotalEpisodes: animeData.Episodes.Total,
+ AiredEpisodes: animeData.Episodes.Aired,
LastUpdated: time.Now(),
}
@@ -960,6 +963,10 @@ func saveCachedAnimeWithBatching(tx *gorm.DB, cachedAnime *entities.CachedAnime)
Color: cachedAnime.Color,
Season: cachedAnime.Season,
Year: cachedAnime.Year,
+ SubbedCount: cachedAnime.SubbedCount,
+ DubbedCount: cachedAnime.DubbedCount,
+ TotalEpisodes: cachedAnime.TotalEpisodes,
+ AiredEpisodes: cachedAnime.AiredEpisodes,
LastUpdated: cachedAnime.LastUpdated,
// Include small collections that won't cause variable limit issues
diff --git a/entities/anime.go b/entities/anime.go
index d758da6..167ae16 100644
--- a/entities/anime.go
+++ b/entities/anime.go
@@ -53,6 +53,10 @@ type CachedAnime struct {
Color string
Season string
Year int
+ SubbedCount int
+ DubbedCount int
+ TotalEpisodes int
+ AiredEpisodes int
LastUpdated time.Time
// Relationships
diff --git a/router/router.go b/router/router.go
index 911ec63..b55014a 100644
--- a/router/router.go
+++ b/router/router.go
@@ -11,8 +11,9 @@ func Initialize(router *fiber.App) {
router.Get("/health", controllers.HealthStatus)
// Anime routes
- animeRouter := router.Group("/anime")
- animeRouter.Get("/:mal_id", controllers.GetAnimeByMALID)
+ animeRouter := router.Group("/a")
+ animeRouter.Get("/:id", controllers.GetAnimeByMALID)
+ animeRouter.Get("/:id/episodes", controllers.GetAnimeEpisodesByMALID)
// 404 Default
router.Use(func(c *fiber.Ctx) error {
diff --git a/services/anime/helpers.go b/services/anime/helpers.go
index 3982ba0..3231676 100644
--- a/services/anime/helpers.go
+++ b/services/anime/helpers.go
@@ -53,6 +53,22 @@ func getEpisodeCount(malAnime *jikan.JikanAnimeResponse, anilistAnime *anilist.A
return episodes
}
+// getEpisodeCountWithAiredFallback determines the total episode count, using aired episodes as fallback for long-running series
+func getEpisodeCountWithAiredFallback(malAnime *jikan.JikanAnimeResponse, anilistAnime *anilist.AnilistAnimeResponse, airedCount int) int {
+ totalFromAPIs := getEpisodeCount(malAnime, anilistAnime)
+
+ // For long-running series, if the aired count is significantly higher than API-reported total,
+ // use the aired count as a more accurate total (since APIs often report season/arc counts)
+ if airedCount > totalFromAPIs && airedCount > 100 {
+ // This indicates a long-running series where APIs might be reporting seasonal data
+ // For ongoing series, total should be at least as high as aired episodes
+ return airedCount
+ }
+
+ // For normal series, use the maximum from APIs
+ return max(totalFromAPIs, airedCount)
+}
+
// sortSeasonsByAirDate sorts the seasons array chronologically by air date
func sortSeasonsByAirDate(seasons *[]types.AnimeSeason) {
// First, collect seasons with valid dates
@@ -219,7 +235,7 @@ func getNextAiringEpisode(anilistAnime *anilist.AnilistAnimeResponse) types.Anim
// If AniList doesn't provide a direct next episode, but we have airing schedule nodes
// Find the next episode that hasn't aired yet
- if anilistAnime.Data.Media.AiringSchedule.Nodes != nil && len(anilistAnime.Data.Media.AiringSchedule.Nodes) > 0 {
+ if len(anilistAnime.Data.Media.AiringSchedule.Nodes) > 0 {
var nextAiringEpisode types.AnimeAiringEpisode
for _, node := range anilistAnime.Data.Media.AiringSchedule.Nodes {
diff --git a/services/anime/service.go b/services/anime/service.go
index 17336d8..6f61680 100644
--- a/services/anime/service.go
+++ b/services/anime/service.go
@@ -375,7 +375,7 @@ func (s *Service) GetAnimeDetailsWithSource(mapping *entities.AnimeMapping, sour
Licensors: generateLicensors(anime.Data.Licensors),
Seasons: seasons,
Episodes: types.AnimeEpisodes{
- Total: getEpisodeCount(anime, anilistAnime),
+ Total: getEpisodeCountWithAiredFallback(anime, anilistAnime, len(episodes.Data)),
Aired: len(episodes.Data),
Subbed: subbedCount,
Dubbed: dubbedCount,