aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--database/anime.go305
-rw-r--r--database/anime_cache.go745
-rw-r--r--database/migrate.go40
-rw-r--r--entities/anime.go222
-rw-r--r--services/anime/service.go39
-rw-r--r--tasks/anime_update.task.go87
6 files changed, 609 insertions, 829 deletions
diff --git a/database/anime.go b/database/anime.go
index 9d2f9e5..02bac41 100644
--- a/database/anime.go
+++ b/database/anime.go
@@ -1,29 +1,316 @@
package database
-import "metachan/entities"
+import (
+ "fmt"
+ "metachan/entities"
+ "metachan/types"
+ "strings"
+ "time"
+
+ "gorm.io/gorm"
+)
+
+func GetAnimeByMALID(malID int) (*types.Anime, error) {
+ var anime entities.Anime
+ result := DB.Preload("Episodes").
+ Preload("Episodes.Titles").
+ Preload("Characters").
+ Preload("Characters.VoiceActors").
+ Preload("AiringSchedule").
+ Preload("NextAiringEpisode").
+ Where("mal_id = ?", malID).First(&anime)
+
+ if result.Error != nil {
+ return nil, result.Error
+ }
+
+ return ConvertToTypesAnime(&anime), nil
+}
+
+func SaveAnimeToDatabase(animeData *types.Anime) error {
+ if animeData == nil {
+ return fmt.Errorf("anime data is nil")
+ }
+
+ var tx *gorm.DB = DB.Begin()
+ if tx.Error != nil {
+ return tx.Error
+ }
+
+ defer func() {
+ if r := recover(); r != nil {
+ tx.Rollback()
+ }
+ }()
+
+ var existingAnime entities.Anime
+ result := tx.Where("mal_id = ?", animeData.MALID).First(&existingAnime)
+ if result.Error == nil {
+ if err := tx.Delete(&existingAnime).Error; err != nil {
+ tx.Rollback()
+ return err
+ }
+ }
+
+ anime := &entities.Anime{
+ MALID: animeData.MALID,
+ TitleRomaji: animeData.Titles.Romaji,
+ TitleEnglish: animeData.Titles.English,
+ TitleJapanese: animeData.Titles.Japanese,
+ TitleSynonyms: strings.Join(animeData.Titles.Synonyms, ","),
+ Synopsis: animeData.Synopsis,
+ Type: string(animeData.Type),
+ Source: animeData.Source,
+ Airing: animeData.Airing,
+ Status: animeData.Status,
+ Duration: animeData.Duration,
+ 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(),
+ }
+
+ if len(animeData.Episodes.Episodes) > 0 {
+ anime.Episodes = make([]entities.AnimeSingleEpisode, len(animeData.Episodes.Episodes))
+ for i, episode := range animeData.Episodes.Episodes {
+ titles := &entities.EpisodeTitles{
+ English: episode.Titles.English,
+ Japanese: episode.Titles.Japanese,
+ Romaji: episode.Titles.Romaji,
+ }
+
+ anime.Episodes[i] = entities.AnimeSingleEpisode{
+ EpisodeID: episode.ID,
+ Description: episode.Description,
+ Aired: episode.Aired,
+ Score: episode.Score,
+ Filler: episode.Filler,
+ Recap: episode.Recap,
+ ForumURL: episode.ForumURL,
+ URL: episode.URL,
+ ThumbnailURL: episode.ThumbnailURL,
+ Titles: titles,
+ }
+ }
+ }
+
+ // Save characters data
+ if len(animeData.Characters) > 0 {
+ anime.Characters = make([]entities.AnimeCharacter, len(animeData.Characters))
+ for i, character := range animeData.Characters {
+ anime.Characters[i] = entities.AnimeCharacter{
+ MALID: character.MALID,
+ URL: character.URL,
+ ImageURL: character.ImageURL,
+ Name: character.Name,
+ Role: character.Role,
+ }
+
+ // Save voice actors for this character
+ if len(character.VoiceActors) > 0 {
+ anime.Characters[i].VoiceActors = make([]entities.AnimeVoiceActor, len(character.VoiceActors))
+ for j, va := range character.VoiceActors {
+ anime.Characters[i].VoiceActors[j] = entities.AnimeVoiceActor{
+ MALID: va.MALID,
+ URL: va.URL,
+ Image: va.Image,
+ Name: va.Name,
+ Language: va.Language,
+ }
+ }
+ }
+ }
+ }
+
+ // Save airing schedule data
+ if len(animeData.AiringSchedule) > 0 {
+ anime.AiringSchedule = make([]entities.ScheduleEpisode, len(animeData.AiringSchedule))
+ for i, schedule := range animeData.AiringSchedule {
+ anime.AiringSchedule[i] = entities.ScheduleEpisode{
+ AiringAt: schedule.AiringAt,
+ Episode: schedule.Episode,
+ IsNext: false, // We'll set this based on next airing episode if available
+ }
+ }
+ }
+
+ // Set next airing episode data
+ if animeData.NextAiringEpisode.Episode > 0 {
+ anime.NextAiringEpisode = &entities.NextEpisode{
+ AiringAt: animeData.NextAiringEpisode.AiringAt,
+ Episode: animeData.NextAiringEpisode.Episode,
+ }
+
+ // Mark the next airing episode in the schedule as IsNext
+ for i := range anime.AiringSchedule {
+ if anime.AiringSchedule[i].Episode == animeData.NextAiringEpisode.Episode &&
+ anime.AiringSchedule[i].AiringAt == animeData.NextAiringEpisode.AiringAt {
+ anime.AiringSchedule[i].IsNext = true
+ break
+ }
+ }
+ }
+
+ if err := tx.Create(anime).Error; err != nil {
+ tx.Rollback()
+ return err
+ }
+
+ return tx.Commit().Error
+}
+
+func ConvertToTypesAnime(anime *entities.Anime) *types.Anime {
+ if anime == nil {
+ return nil
+ }
+
+ result := &types.Anime{
+ MALID: anime.MALID,
+ Titles: types.AnimeTitles{
+ Romaji: anime.TitleRomaji,
+ English: anime.TitleEnglish,
+ Japanese: anime.TitleJapanese,
+ Synonyms: strings.Split(anime.TitleSynonyms, ","),
+ },
+ Synopsis: anime.Synopsis,
+ Type: types.AniSyncType(anime.Type),
+ Source: anime.Source,
+ Airing: anime.Airing,
+ Status: anime.Status,
+ Duration: anime.Duration,
+ Color: anime.Color,
+ Season: anime.Season,
+ Year: anime.Year,
+ Episodes: types.AnimeEpisodes{
+ Total: anime.TotalEpisodes,
+ Aired: anime.AiredEpisodes,
+ Subbed: anime.SubbedCount,
+ Dubbed: anime.DubbedCount,
+ },
+ }
+
+ if len(anime.Episodes) > 0 {
+ result.Episodes.Episodes = make([]types.AnimeSingleEpisode, len(anime.Episodes))
+ for i, episode := range anime.Episodes {
+ episodeData := types.AnimeSingleEpisode{
+ ID: episode.EpisodeID,
+ Description: episode.Description,
+ Aired: episode.Aired,
+ Score: episode.Score,
+ Filler: episode.Filler,
+ Recap: episode.Recap,
+ ForumURL: episode.ForumURL,
+ URL: episode.URL,
+ ThumbnailURL: episode.ThumbnailURL,
+ }
+
+ if episode.Titles != nil {
+ episodeData.Titles = types.EpisodeTitles{
+ English: episode.Titles.English,
+ Japanese: episode.Titles.Japanese,
+ Romaji: episode.Titles.Romaji,
+ }
+ }
+
+ result.Episodes.Episodes[i] = episodeData
+ }
+ }
+
+ // Convert characters
+ if len(anime.Characters) > 0 {
+ result.Characters = make([]types.AnimeCharacter, len(anime.Characters))
+ for i, character := range anime.Characters {
+ result.Characters[i] = types.AnimeCharacter{
+ MALID: character.MALID,
+ URL: character.URL,
+ ImageURL: character.ImageURL,
+ Name: character.Name,
+ Role: character.Role,
+ }
+
+ // Convert voice actors for this character
+ if len(character.VoiceActors) > 0 {
+ result.Characters[i].VoiceActors = make([]types.AnimeVoiceActor, len(character.VoiceActors))
+ for j, va := range character.VoiceActors {
+ result.Characters[i].VoiceActors[j] = types.AnimeVoiceActor{
+ MALID: va.MALID,
+ URL: va.URL,
+ Image: va.Image,
+ Name: va.Name,
+ Language: va.Language,
+ }
+ }
+ }
+ }
+ }
+
+ // Convert airing schedule
+ if len(anime.AiringSchedule) > 0 {
+ result.AiringSchedule = make([]types.AnimeAiringEpisode, len(anime.AiringSchedule))
+ for i, schedule := range anime.AiringSchedule {
+ result.AiringSchedule[i] = types.AnimeAiringEpisode{
+ AiringAt: schedule.AiringAt,
+ Episode: schedule.Episode,
+ }
+ }
+ }
+
+ // Convert next airing episode
+ if anime.NextAiringEpisode != nil {
+ result.NextAiringEpisode = types.AnimeAiringEpisode{
+ AiringAt: anime.NextAiringEpisode.AiringAt,
+ Episode: anime.NextAiringEpisode.Episode,
+ }
+ }
+
+ var mapping entities.AnimeMapping
+ if err := DB.Where("mal = ?", anime.MALID).First(&mapping).Error; err == nil {
+ result.Mappings = types.AnimeMappings{
+ AniDB: mapping.AniDB,
+ Anilist: mapping.Anilist,
+ AnimeCountdown: mapping.AnimeCountdown,
+ AnimePlanet: mapping.AnimePlanet,
+ AniSearch: mapping.AniSearch,
+ IMDB: mapping.IMDB,
+ Kitsu: mapping.Kitsu,
+ LiveChart: mapping.LiveChart,
+ NotifyMoe: mapping.NotifyMoe,
+ Simkl: mapping.Simkl,
+ TMDB: mapping.TMDB,
+ TVDB: mapping.TVDB,
+ }
+ }
+
+ return result
+}
func GetAnimeMappingViaMALID(malID int) (*entities.AnimeMapping, error) {
var mapping entities.AnimeMapping
- if err := DB.Where("mal = ?", malID).First(&mapping).Error; err != nil {
- return nil, err
+ result := DB.Where("mal = ?", malID).First(&mapping)
+ if result.Error != nil {
+ return nil, result.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
+ result := DB.Where("anilist = ?", anilistID).First(&mapping)
+ if result.Error != nil {
+ return nil, result.Error
}
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
- if err := DB.Where("tvdb = ?", tvdbID).Find(&mappings).Error; err != nil {
- return nil, err
+ result := DB.Where("tvdb = ?", tvdbID).Find(&mappings)
+ if result.Error != nil {
+ return nil, result.Error
}
return mappings, nil
}
diff --git a/database/anime_cache.go b/database/anime_cache.go
deleted file mode 100644
index 835f271..0000000
--- a/database/anime_cache.go
+++ /dev/null
@@ -1,745 +0,0 @@
-package database
-
-import (
- "fmt"
- "metachan/entities"
- "metachan/types"
- "strings"
- "time"
-
- "gorm.io/gorm"
-)
-
-const (
- CacheExpirationTime = 24 * time.Hour
-)
-
-func GetCachedAnimeByMALID(malID int) (*entities.CachedAnime, error) {
- var anime entities.CachedAnime
- err := DB.
- Preload("Images").
- Preload("Logos").
- Preload("Covers").
- Preload("Scores").
- Preload("AiringStatus").
- Preload("AiringStatus.From").
- Preload("AiringStatus.To").
- Preload("Broadcast").
- Preload("NextAiringEpisode").
- Preload("Genres").
- Preload("Producers").
- Preload("Studios").
- Preload("Licensors").
- Preload("Episodes").
- Preload("Episodes.Titles").
- Preload("Characters").
- Preload("Characters.VoiceActors").
- Preload("AiringSchedule").
- Preload("Seasons").
- Where("mal_id = ?", malID).
- First(&anime).Error
-
- if err != nil {
- return nil, err
- }
- return &anime, nil
-}
-
-func IsCacheValid(anime *entities.CachedAnime) bool {
- if anime == nil {
- return false
- }
- return time.Since(anime.LastUpdated) < CacheExpirationTime
-}
-
-func SaveAnimeToCache(animeData *types.Anime) error {
- if animeData == nil {
- return fmt.Errorf("anime data is nil")
- }
-
- var tx *gorm.DB = DB.Begin()
- if tx.Error != nil {
- return tx.Error
- }
-
- defer func() {
- if r := recover(); r != nil {
- tx.Rollback()
- }
- }()
-
- if err := tx.Exec("DELETE FROM cached_animes WHERE mal_id = ?", animeData.MALID).Error; err != nil {
- tx.Rollback()
- return err
- }
-
- anime := &entities.CachedAnime{
- MALID: animeData.MALID,
- TitleRomaji: animeData.Titles.Romaji,
- TitleEnglish: animeData.Titles.English,
- TitleJapanese: animeData.Titles.Japanese,
- TitleSynonyms: strings.Join(animeData.Titles.Synonyms, ","),
- Synopsis: animeData.Synopsis,
- Type: string(animeData.Type),
- Source: animeData.Source,
- Airing: animeData.Airing,
- Status: animeData.Status,
- Duration: animeData.Duration,
- 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(),
- }
-
- if err := tx.Create(anime).Error; err != nil {
- tx.Rollback()
- return err
- }
-
- // Now create related records with proper foreign key references
- if animeData.Images.Small != "" || animeData.Images.Large != "" || animeData.Images.Original != "" {
- images := &entities.CachedAnimeImages{
- AnimeID: anime.ID,
- Small: animeData.Images.Small,
- Large: animeData.Images.Large,
- Original: animeData.Images.Original,
- }
- if err := tx.Create(images).Error; err != nil {
- tx.Rollback()
- return err
- }
- }
-
- if animeData.Logos.Small != "" || animeData.Logos.Medium != "" || animeData.Logos.Large != "" || animeData.Logos.XLarge != "" || animeData.Logos.Original != "" {
- logos := &entities.CachedAnimeLogos{
- AnimeID: anime.ID,
- Small: animeData.Logos.Small,
- Medium: animeData.Logos.Medium,
- Large: animeData.Logos.Large,
- XLarge: animeData.Logos.XLarge,
- Original: animeData.Logos.Original,
- }
- if err := tx.Create(logos).Error; err != nil {
- tx.Rollback()
- return err
- }
- }
-
- if animeData.Covers.Small != "" || animeData.Covers.Large != "" || animeData.Covers.Original != "" {
- covers := &entities.CachedAnimeCovers{
- AnimeID: anime.ID,
- Small: animeData.Covers.Small,
- Large: animeData.Covers.Large,
- Original: animeData.Covers.Original,
- }
- if err := tx.Create(covers).Error; err != nil {
- tx.Rollback()
- return err
- }
- }
-
- if animeData.Scores.Score > 0 || animeData.Scores.ScoredBy > 0 {
- scores := &entities.CachedAnimeScores{
- AnimeID: anime.ID,
- Score: animeData.Scores.Score,
- ScoredBy: animeData.Scores.ScoredBy,
- Rank: animeData.Scores.Rank,
- Popularity: animeData.Scores.Popularity,
- Members: animeData.Scores.Members,
- Favorites: animeData.Scores.Favorites,
- }
- if err := tx.Create(scores).Error; err != nil {
- tx.Rollback()
- return err
- }
- }
-
- if animeData.AiringStatus.From.Year > 0 || animeData.AiringStatus.From.String != "" || animeData.AiringStatus.To.Year > 0 || animeData.AiringStatus.To.String != "" || animeData.AiringStatus.String != "" {
- var fromDateID, toDateID *uint
-
- if animeData.AiringStatus.From.Year > 0 || animeData.AiringStatus.From.String != "" {
- fromDate := &entities.CachedAiringStatusDates{
- Day: animeData.AiringStatus.From.Day,
- Month: animeData.AiringStatus.From.Month,
- Year: animeData.AiringStatus.From.Year,
- String: animeData.AiringStatus.From.String,
- }
- if err := tx.Create(fromDate).Error; err != nil {
- tx.Rollback()
- return err
- }
- fromDateID = &fromDate.ID
- }
-
- if animeData.AiringStatus.To.Year > 0 || animeData.AiringStatus.To.String != "" {
- toDate := &entities.CachedAiringStatusDates{
- Day: animeData.AiringStatus.To.Day,
- Month: animeData.AiringStatus.To.Month,
- Year: animeData.AiringStatus.To.Year,
- String: animeData.AiringStatus.To.String,
- }
- if err := tx.Create(toDate).Error; err != nil {
- tx.Rollback()
- return err
- }
- toDateID = &toDate.ID
- }
-
- airingStatus := &entities.CachedAiringStatus{
- AnimeID: anime.ID,
- FromID: fromDateID,
- ToID: toDateID,
- String: animeData.AiringStatus.String,
- }
-
- if err := tx.Create(airingStatus).Error; err != nil {
- tx.Rollback()
- return err
- }
- }
-
- if animeData.Broadcast.Day != "" || animeData.Broadcast.Time != "" {
- broadcast := &entities.CachedAnimeBroadcast{
- AnimeID: anime.ID,
- Day: animeData.Broadcast.Day,
- Time: animeData.Broadcast.Time,
- Timezone: animeData.Broadcast.Timezone,
- String: animeData.Broadcast.String,
- }
- if err := tx.Create(broadcast).Error; err != nil {
- tx.Rollback()
- return err
- }
- }
-
- if animeData.NextAiringEpisode.AiringAt > 0 && animeData.NextAiringEpisode.Episode > 0 {
- nextEpisode := &entities.CachedNextEpisode{
- AnimeID: anime.ID,
- AiringAt: animeData.NextAiringEpisode.AiringAt,
- Episode: animeData.NextAiringEpisode.Episode,
- }
- if err := tx.Create(nextEpisode).Error; err != nil {
- tx.Rollback()
- return err
- }
- }
-
- // Create array records
- for _, genre := range animeData.Genres {
- genreRecord := &entities.CachedAnimeGenre{
- AnimeID: anime.ID,
- Name: genre.Name,
- GenreID: genre.GenreID,
- URL: genre.URL,
- }
- if err := tx.Create(genreRecord).Error; err != nil {
- tx.Rollback()
- return err
- }
- }
-
- for _, producer := range animeData.Producers {
- producerRecord := &entities.CachedAnimeProducer{
- AnimeID: anime.ID,
- Name: producer.Name,
- ProducerID: producer.ProducerID,
- URL: producer.URL,
- }
- if err := tx.Create(producerRecord).Error; err != nil {
- tx.Rollback()
- return err
- }
- }
-
- for _, studio := range animeData.Studios {
- studioRecord := &entities.CachedAnimeStudio{
- AnimeID: anime.ID,
- Name: studio.Name,
- StudioID: studio.StudioID,
- URL: studio.URL,
- }
- if err := tx.Create(studioRecord).Error; err != nil {
- tx.Rollback()
- return err
- }
- }
-
- for _, licensor := range animeData.Licensors {
- licensorRecord := &entities.CachedAnimeLicensor{
- AnimeID: anime.ID,
- Name: licensor.Name,
- ProducerID: licensor.ProducerID,
- URL: licensor.URL,
- }
- if err := tx.Create(licensorRecord).Error; err != nil {
- tx.Rollback()
- return err
- }
- }
-
- for _, episode := range animeData.AiringSchedule {
- scheduleRecord := &entities.CachedScheduleEpisode{
- AnimeID: anime.ID,
- AiringAt: episode.AiringAt,
- Episode: episode.Episode,
- }
- if err := tx.Create(scheduleRecord).Error; err != nil {
- tx.Rollback()
- return err
- }
- }
-
- for _, episode := range animeData.Episodes.Episodes {
- titles := &entities.CachedEpisodeTitles{
- English: episode.Titles.English,
- Japanese: episode.Titles.Japanese,
- Romaji: episode.Titles.Romaji,
- }
- if err := tx.Create(titles).Error; err != nil {
- tx.Rollback()
- return err
- }
-
- episodeRecord := &entities.CachedAnimeSingleEpisode{
- EpisodeID: episode.ID,
- AnimeID: anime.ID,
- TitlesID: titles.ID,
- Description: episode.Description,
- Aired: episode.Aired,
- Score: episode.Score,
- Filler: episode.Filler,
- Recap: episode.Recap,
- ForumURL: episode.ForumURL,
- URL: episode.URL,
- ThumbnailURL: episode.ThumbnailURL,
- }
- if err := tx.Create(episodeRecord).Error; err != nil {
- tx.Rollback()
- return err
- }
- }
-
- for _, character := range animeData.Characters {
- charRecord := &entities.CachedAnimeCharacter{
- AnimeID: anime.ID,
- MALID: character.MALID,
- URL: character.URL,
- ImageURL: character.ImageURL,
- Name: character.Name,
- Role: character.Role,
- }
-
- if err := tx.Create(charRecord).Error; err != nil {
- tx.Rollback()
- return err
- }
-
- for _, va := range character.VoiceActors {
- vaRecord := &entities.CachedAnimeVoiceActor{
- CharacterID: charRecord.ID,
- MALID: va.MALID,
- URL: va.URL,
- Image: va.Image,
- Name: va.Name,
- Language: va.Language,
- }
- if err := tx.Create(vaRecord).Error; err != nil {
- tx.Rollback()
- return err
- }
- }
- }
-
- for _, season := range animeData.Seasons {
- seasonRecord := &entities.CachedAnimeSeason{
- ParentAnimeID: anime.ID,
- MALID: season.MALID,
- TitleRomaji: season.Titles.Romaji,
- TitleEnglish: season.Titles.English,
- TitleJapanese: season.Titles.Japanese,
- TitleSynonyms: strings.Join(season.Titles.Synonyms, ","),
- Synopsis: season.Synopsis,
- Type: string(season.Type),
- Source: season.Source,
- Airing: season.Airing,
- Status: season.Status,
- Duration: season.Duration,
- Season: season.Season,
- Year: season.Year,
- Current: season.Current,
- }
-
- if err := tx.Create(seasonRecord).Error; err != nil {
- tx.Rollback()
- return err
- }
-
- if season.Images.Small != "" || season.Images.Large != "" || season.Images.Original != "" {
- seasonImages := &entities.CachedAnimeImages{
- AnimeID: seasonRecord.ID,
- Small: season.Images.Small,
- Large: season.Images.Large,
- Original: season.Images.Original,
- }
- if err := tx.Create(seasonImages).Error; err != nil {
- tx.Rollback()
- return err
- }
- }
-
- if season.Scores.Score > 0 || season.Scores.ScoredBy > 0 {
- seasonScores := &entities.CachedAnimeScores{
- AnimeID: seasonRecord.ID,
- Score: season.Scores.Score,
- ScoredBy: season.Scores.ScoredBy,
- Rank: season.Scores.Rank,
- Popularity: season.Scores.Popularity,
- Members: season.Scores.Members,
- Favorites: season.Scores.Favorites,
- }
- if err := tx.Create(seasonScores).Error; err != nil {
- tx.Rollback()
- return err
- }
- }
- }
-
- if animeData.Mappings.AniDB > 0 || animeData.Mappings.Anilist > 0 || animeData.Mappings.AnimeCountdown > 0 || animeData.Mappings.AnimePlanet != "" || animeData.Mappings.AniSearch > 0 || animeData.Mappings.IMDB != "" || animeData.Mappings.Kitsu > 0 || animeData.Mappings.LiveChart > 0 || animeData.Mappings.NotifyMoe != "" || animeData.Mappings.Simkl > 0 || animeData.Mappings.TMDB > 0 || animeData.Mappings.TVDB > 0 {
- mapping := &entities.AnimeMapping{
- AniDB: animeData.Mappings.AniDB,
- Anilist: animeData.Mappings.Anilist,
- AnimeCountdown: animeData.Mappings.AnimeCountdown,
- AnimePlanet: animeData.Mappings.AnimePlanet,
- AniSearch: animeData.Mappings.AniSearch,
- IMDB: animeData.Mappings.IMDB,
- Kitsu: animeData.Mappings.Kitsu,
- LiveChart: animeData.Mappings.LiveChart,
- MAL: animeData.MALID,
- NotifyMoe: animeData.Mappings.NotifyMoe,
- Simkl: animeData.Mappings.Simkl,
- TMDB: animeData.Mappings.TMDB,
- TVDB: animeData.Mappings.TVDB,
- }
-
- if err := tx.Create(mapping).Error; err != nil {
- tx.Rollback()
- return err
- }
- }
-
- return tx.Commit().Error
-}
-
-func ConvertToTypesAnime(cached *entities.CachedAnime) *types.Anime {
- if cached == nil {
- return nil
- }
-
- anime := &types.Anime{
- MALID: cached.MALID,
- Titles: types.AnimeTitles{
- Romaji: cached.TitleRomaji,
- English: cached.TitleEnglish,
- Japanese: cached.TitleJapanese,
- Synonyms: strings.Split(cached.TitleSynonyms, ","),
- },
- Synopsis: cached.Synopsis,
- Type: types.AniSyncType(cached.Type),
- Source: cached.Source,
- Airing: cached.Airing,
- Status: cached.Status,
- Duration: cached.Duration,
- Color: cached.Color,
- Season: cached.Season,
- Year: cached.Year,
- Episodes: types.AnimeEpisodes{
- Total: cached.TotalEpisodes,
- Aired: cached.AiredEpisodes,
- Subbed: cached.SubbedCount,
- Dubbed: cached.DubbedCount,
- },
- }
-
- if cached.Images != nil {
- anime.Images = types.AnimeImages{
- Small: cached.Images.Small,
- Large: cached.Images.Large,
- Original: cached.Images.Original,
- }
- }
-
- if cached.Logos != nil {
- anime.Logos = types.AnimeLogos{
- Small: cached.Logos.Small,
- Medium: cached.Logos.Medium,
- Large: cached.Logos.Large,
- XLarge: cached.Logos.XLarge,
- Original: cached.Logos.Original,
- }
- }
-
- if cached.Covers != nil {
- anime.Covers = types.AnimeImages{
- Small: cached.Covers.Small,
- Large: cached.Covers.Large,
- Original: cached.Covers.Original,
- }
- }
-
- if cached.Scores != nil {
- anime.Scores = types.AnimeScores{
- Score: cached.Scores.Score,
- ScoredBy: cached.Scores.ScoredBy,
- Rank: cached.Scores.Rank,
- Popularity: cached.Scores.Popularity,
- Members: cached.Scores.Members,
- Favorites: cached.Scores.Favorites,
- }
- }
-
- if cached.AiringStatus != nil {
- airingStatus := types.AiringStatus{
- String: cached.AiringStatus.String,
- }
-
- if cached.AiringStatus.From != nil {
- airingStatus.From = types.AiringStatusDates{
- Day: cached.AiringStatus.From.Day,
- Month: cached.AiringStatus.From.Month,
- Year: cached.AiringStatus.From.Year,
- String: cached.AiringStatus.From.String,
- }
- }
-
- if cached.AiringStatus.To != nil {
- airingStatus.To = types.AiringStatusDates{
- Day: cached.AiringStatus.To.Day,
- Month: cached.AiringStatus.To.Month,
- Year: cached.AiringStatus.To.Year,
- String: cached.AiringStatus.To.String,
- }
- }
-
- anime.AiringStatus = airingStatus
- }
-
- if cached.Broadcast != nil {
- anime.Broadcast = types.AnimeBroadcast{
- Day: cached.Broadcast.Day,
- Time: cached.Broadcast.Time,
- Timezone: cached.Broadcast.Timezone,
- String: cached.Broadcast.String,
- }
- }
-
- if len(cached.Genres) > 0 {
- anime.Genres = make([]types.AnimeGenres, len(cached.Genres))
- for i, genre := range cached.Genres {
- anime.Genres[i] = types.AnimeGenres{
- Name: genre.Name,
- GenreID: genre.GenreID,
- URL: genre.URL,
- }
- }
- }
-
- if len(cached.Producers) > 0 {
- anime.Producers = make([]types.AnimeProducer, len(cached.Producers))
- for i, producer := range cached.Producers {
- anime.Producers[i] = types.AnimeProducer{
- Name: producer.Name,
- ProducerID: producer.ProducerID,
- URL: producer.URL,
- }
- }
- }
-
- if len(cached.Studios) > 0 {
- anime.Studios = make([]types.AnimeStudio, len(cached.Studios))
- for i, studio := range cached.Studios {
- anime.Studios[i] = types.AnimeStudio{
- Name: studio.Name,
- StudioID: studio.StudioID,
- URL: studio.URL,
- }
- }
- }
-
- if len(cached.Licensors) > 0 {
- anime.Licensors = make([]types.AnimeLicensor, len(cached.Licensors))
- for i, licensor := range cached.Licensors {
- anime.Licensors[i] = types.AnimeLicensor{
- Name: licensor.Name,
- ProducerID: licensor.ProducerID,
- URL: licensor.URL,
- }
- }
- }
-
- if cached.NextAiringEpisode != nil {
- anime.NextAiringEpisode = types.AnimeAiringEpisode{
- AiringAt: cached.NextAiringEpisode.AiringAt,
- Episode: cached.NextAiringEpisode.Episode,
- }
- }
-
- if len(cached.AiringSchedule) > 0 {
- anime.AiringSchedule = make([]types.AnimeAiringEpisode, len(cached.AiringSchedule))
- for i, episode := range cached.AiringSchedule {
- anime.AiringSchedule[i] = types.AnimeAiringEpisode{
- AiringAt: episode.AiringAt,
- Episode: episode.Episode,
- }
- }
- }
-
- if len(cached.Episodes) > 0 {
- anime.Episodes.Episodes = make([]types.AnimeSingleEpisode, len(cached.Episodes))
- for i, episode := range cached.Episodes {
- episodeData := types.AnimeSingleEpisode{
- ID: episode.EpisodeID,
- Description: episode.Description,
- Aired: episode.Aired,
- Score: episode.Score,
- Filler: episode.Filler,
- Recap: episode.Recap,
- ForumURL: episode.ForumURL,
- URL: episode.URL,
- ThumbnailURL: episode.ThumbnailURL,
- }
-
- if episode.Titles != nil {
- episodeData.Titles = types.EpisodeTitles{
- English: episode.Titles.English,
- Japanese: episode.Titles.Japanese,
- Romaji: episode.Titles.Romaji,
- }
- }
-
- anime.Episodes.Episodes[i] = episodeData
- }
- }
-
- if len(cached.Characters) > 0 {
- anime.Characters = make([]types.AnimeCharacter, len(cached.Characters))
- for i, character := range cached.Characters {
- animeCharacter := types.AnimeCharacter{
- MALID: character.MALID,
- URL: character.URL,
- ImageURL: character.ImageURL,
- Name: character.Name,
- Role: character.Role,
- }
-
- if len(character.VoiceActors) > 0 {
- animeCharacter.VoiceActors = make([]types.AnimeVoiceActor, len(character.VoiceActors))
- for j, va := range character.VoiceActors {
- animeCharacter.VoiceActors[j] = types.AnimeVoiceActor{
- MALID: va.MALID,
- URL: va.URL,
- Image: va.Image,
- Name: va.Name,
- Language: va.Language,
- }
- }
- }
-
- anime.Characters[i] = animeCharacter
- }
- }
-
- if len(cached.Seasons) > 0 {
- anime.Seasons = make([]types.AnimeSeason, len(cached.Seasons))
- for i, season := range cached.Seasons {
- animeSeason := types.AnimeSeason{
- MALID: season.MALID,
- Synopsis: season.Synopsis,
- Type: types.AniSyncType(season.Type),
- Source: season.Source,
- Airing: season.Airing,
- Status: season.Status,
- Duration: season.Duration,
- Season: season.Season,
- Year: season.Year,
- Current: season.Current,
- Titles: types.AnimeTitles{
- Romaji: season.TitleRomaji,
- English: season.TitleEnglish,
- Japanese: season.TitleJapanese,
- Synonyms: strings.Split(season.TitleSynonyms, ","),
- },
- }
-
- if season.Images != nil {
- animeSeason.Images = types.AnimeImages{
- Small: season.Images.Small,
- Large: season.Images.Large,
- Original: season.Images.Original,
- }
- }
-
- if season.Scores != nil {
- animeSeason.Scores = types.AnimeScores{
- Score: season.Scores.Score,
- ScoredBy: season.Scores.ScoredBy,
- Rank: season.Scores.Rank,
- Popularity: season.Scores.Popularity,
- Members: season.Scores.Members,
- Favorites: season.Scores.Favorites,
- }
- }
-
- if season.AiringStatus != nil {
- airingStatus := types.AiringStatus{
- String: season.AiringStatus.String,
- }
-
- if season.AiringStatus.From != nil {
- airingStatus.From = types.AiringStatusDates{
- Day: season.AiringStatus.From.Day,
- Month: season.AiringStatus.From.Month,
- Year: season.AiringStatus.From.Year,
- String: season.AiringStatus.From.String,
- }
- }
-
- if season.AiringStatus.To != nil {
- airingStatus.To = types.AiringStatusDates{
- Day: season.AiringStatus.To.Day,
- Month: season.AiringStatus.To.Month,
- Year: season.AiringStatus.To.Year,
- String: season.AiringStatus.To.String,
- }
- }
-
- animeSeason.AiringStatus = airingStatus
- }
-
- anime.Seasons[i] = animeSeason
- }
- }
-
- var mapping entities.AnimeMapping
- if err := DB.Where("mal = ?", cached.MALID).First(&mapping).Error; err == nil {
- anime.Mappings = types.AnimeMappings{
- AniDB: mapping.AniDB,
- Anilist: mapping.Anilist,
- AnimeCountdown: mapping.AnimeCountdown,
- AnimePlanet: mapping.AnimePlanet,
- AniSearch: mapping.AniSearch,
- IMDB: mapping.IMDB,
- Kitsu: mapping.Kitsu,
- LiveChart: mapping.LiveChart,
- NotifyMoe: mapping.NotifyMoe,
- Simkl: mapping.Simkl,
- TMDB: mapping.TMDB,
- TVDB: mapping.TVDB,
- }
- }
-
- return anime
-}
diff --git a/database/migrate.go b/database/migrate.go
index d73eea9..89cdb7d 100644
--- a/database/migrate.go
+++ b/database/migrate.go
@@ -12,26 +12,26 @@ func AutoMigrate() {
&entities.TaskLog{},
&entities.AnimeMapping{},
- // Cache entities
- &entities.CachedAnime{},
- &entities.CachedAnimeImages{},
- &entities.CachedAnimeLogos{},
- &entities.CachedAnimeCovers{},
- &entities.CachedAnimeScores{},
- &entities.CachedAiringStatusDates{},
- &entities.CachedAiringStatus{},
- &entities.CachedAnimeBroadcast{},
- &entities.CachedAnimeGenre{},
- &entities.CachedAnimeProducer{},
- &entities.CachedAnimeStudio{},
- &entities.CachedAnimeLicensor{},
- &entities.CachedNextEpisode{},
- &entities.CachedScheduleEpisode{},
- &entities.CachedEpisodeTitles{},
- &entities.CachedAnimeSingleEpisode{},
- &entities.CachedAnimeCharacter{},
- &entities.CachedAnimeVoiceActor{},
- &entities.CachedAnimeSeason{},
+ // Anime entities
+ &entities.Anime{},
+ &entities.AnimeImages{},
+ &entities.AnimeLogos{},
+ &entities.AnimeCovers{},
+ &entities.AnimeScores{},
+ &entities.AiringStatusDates{},
+ &entities.AiringStatus{},
+ &entities.AnimeBroadcast{},
+ &entities.AnimeGenre{},
+ &entities.AnimeProducer{},
+ &entities.AnimeStudio{},
+ &entities.AnimeLicensor{},
+ &entities.NextEpisode{},
+ &entities.ScheduleEpisode{},
+ &entities.EpisodeTitles{},
+ &entities.AnimeSingleEpisode{},
+ &entities.AnimeCharacter{},
+ &entities.AnimeVoiceActor{},
+ &entities.AnimeSeason{},
)
if err != nil {
logger.Log(fmt.Sprintf("Failed to migrate database: %v", err), logger.LogOptions{
diff --git a/entities/anime.go b/entities/anime.go
index 0014979..a2a9988 100644
--- a/entities/anime.go
+++ b/entities/anime.go
@@ -36,6 +36,228 @@ type AnimeMapping struct {
MALAnilistComposite *string `gorm:"uniqueIndex"`
}
+type Anime struct {
+ gorm.Model
+ MALID int `gorm:"uniqueIndex"`
+ TitleRomaji string
+ TitleEnglish string
+ TitleJapanese string
+ TitleSynonyms string
+ Synopsis string `gorm:"type:text"`
+ Type string
+ Source string
+ Airing bool
+ Status string
+ Duration string
+ Color string
+ Season string
+ Year int
+ SubbedCount int
+ DubbedCount int
+ TotalEpisodes int
+ AiredEpisodes int
+ LastUpdated time.Time
+
+ Images *AnimeImages `gorm:"foreignKey:AnimeID"`
+ Logos *AnimeLogos `gorm:"foreignKey:AnimeID"`
+ Covers *AnimeCovers `gorm:"foreignKey:AnimeID"`
+ Scores *AnimeScores `gorm:"foreignKey:AnimeID"`
+ AiringStatus *AiringStatus `gorm:"foreignKey:AnimeID"`
+ Broadcast *AnimeBroadcast `gorm:"foreignKey:AnimeID"`
+ NextAiringEpisode *NextEpisode `gorm:"foreignKey:AnimeID"`
+
+ Genres []AnimeGenre `gorm:"foreignKey:AnimeID"`
+ Producers []AnimeProducer `gorm:"foreignKey:AnimeID"`
+ Studios []AnimeStudio `gorm:"foreignKey:AnimeID"`
+ Licensors []AnimeLicensor `gorm:"foreignKey:AnimeID"`
+ Episodes []AnimeSingleEpisode `gorm:"foreignKey:AnimeID"`
+ Characters []AnimeCharacter `gorm:"foreignKey:AnimeID"`
+ AiringSchedule []ScheduleEpisode `gorm:"foreignKey:AnimeID;constraint:OnDelete:CASCADE"`
+ Seasons []AnimeSeason `gorm:"foreignKey:ParentAnimeID"`
+}
+
+type AnimeImages struct {
+ gorm.Model
+ AnimeID uint
+ Small string
+ Large string
+ Original string
+}
+
+type AnimeCovers struct {
+ gorm.Model
+ AnimeID uint
+ Small string
+ Large string
+ Original string
+}
+
+type AnimeLogos struct {
+ gorm.Model
+ AnimeID uint
+ Small string
+ Medium string
+ Large string
+ XLarge string
+ Original string
+}
+
+type AnimeScores struct {
+ gorm.Model
+ AnimeID uint
+ Score float64
+ ScoredBy int
+ Rank int
+ Popularity int
+ Members int
+ Favorites int
+}
+
+type AiringStatusDates struct {
+ gorm.Model
+ Day int
+ Month int
+ Year int
+ String string
+}
+
+type AiringStatus struct {
+ gorm.Model
+ AnimeID uint
+ FromID *uint
+ ToID *uint
+ String string
+
+ From *AiringStatusDates `gorm:"foreignKey:FromID"`
+ To *AiringStatusDates `gorm:"foreignKey:ToID"`
+}
+
+type AnimeBroadcast struct {
+ gorm.Model
+ AnimeID uint
+ Day string
+ Time string
+ Timezone string
+ String string
+}
+
+type AnimeGenre struct {
+ gorm.Model
+ AnimeID uint
+ Name string
+ GenreID int
+ URL string
+}
+
+type AnimeProducer struct {
+ gorm.Model
+ AnimeID uint
+ Name string
+ ProducerID int
+ URL string
+}
+
+type AnimeStudio struct {
+ gorm.Model
+ AnimeID uint
+ Name string
+ StudioID int
+ URL string
+}
+
+type AnimeLicensor struct {
+ gorm.Model
+ AnimeID uint
+ Name string
+ ProducerID int
+ URL string
+}
+
+type ScheduleEpisode struct {
+ gorm.Model
+ AnimeID uint
+ AiringAt int
+ Episode int
+ IsNext bool `gorm:"index"`
+}
+
+type EpisodeTitles struct {
+ gorm.Model
+ EpisodeID uint
+ English string
+ Japanese string
+ Romaji string
+}
+
+type AnimeSingleEpisode struct {
+ gorm.Model
+ EpisodeID string `gorm:"uniqueIndex;size:32"`
+ AnimeID uint
+ TitlesID uint
+ Description string `gorm:"type:text"`
+ Aired string
+ Score float64
+ Filler bool
+ Recap bool
+ ForumURL string
+ URL string
+ ThumbnailURL string
+
+ Titles *EpisodeTitles `gorm:"foreignKey:TitlesID"`
+}
+
+type AnimeSeason struct {
+ gorm.Model
+ ParentAnimeID uint
+ MALID int
+ TitleRomaji string
+ TitleEnglish string
+ TitleJapanese string
+ TitleSynonyms string
+ Synopsis string `gorm:"type:text"`
+ Type string
+ Source string
+ Airing bool
+ Status string
+ Duration string
+ Season string
+ Year int
+ Current bool
+
+ Images *AnimeImages `gorm:"foreignKey:AnimeID"`
+ Scores *AnimeScores `gorm:"foreignKey:AnimeID"`
+ AiringStatus *AiringStatus `gorm:"foreignKey:AnimeID"`
+}
+
+type AnimeVoiceActor struct {
+ gorm.Model
+ CharacterID uint
+ MALID int
+ URL string
+ Image string
+ Name string
+ Language string
+}
+
+type AnimeCharacter struct {
+ gorm.Model
+ AnimeID uint
+ MALID int
+ URL string
+ ImageURL string
+ Name string
+ Role string
+
+ VoiceActors []AnimeVoiceActor `gorm:"foreignKey:CharacterID"`
+}
+
+type NextEpisode struct {
+ gorm.Model
+ AnimeID uint
+ AiringAt int
+ Episode int
+}
+
// CachedAnime represents the main cached anime entity in database
type CachedAnime struct {
gorm.Model
diff --git a/services/anime/service.go b/services/anime/service.go
index 6f61680..f4a48e5 100644
--- a/services/anime/service.go
+++ b/services/anime/service.go
@@ -35,6 +35,10 @@ func NewService() *Service {
// GetAnimeDetailsWithSource fetches comprehensive anime details with source information
func (s *Service) GetAnimeDetailsWithSource(mapping *entities.AnimeMapping, source string) (*types.Anime, error) {
+ if mapping == nil {
+ return nil, fmt.Errorf("anime mapping is nil")
+ }
+
startTime := time.Now()
defer func() {
duration := time.Since(startTime)
@@ -48,18 +52,15 @@ func (s *Service) GetAnimeDetailsWithSource(mapping *entities.AnimeMapping, sour
// For updater source, always fetch fresh data and skip cache
if source != "updater" {
- // First, check if we have a valid cached version
- cachedAnime, err := database.GetCachedAnimeByMALID(malID)
- if err == nil && database.IsCacheValid(cachedAnime) {
- logger.Log(fmt.Sprintf("Cache hit for anime (MAL ID: %d), returning cached data", malID), logger.LogOptions{
+ // First, check if we have an existing version in the database
+ anime, err := database.GetAnimeByMALID(malID)
+ if err == nil {
+ logger.Log(fmt.Sprintf("Found existing anime data (MAL ID: %d), returning stored data", malID), logger.LogOptions{
Level: logger.Info,
- Prefix: "AnimeCache",
+ Prefix: "AnimeDB",
})
- // Convert the cached anime to the types.Anime format
- anime := database.ConvertToTypesAnime(cachedAnime)
-
- // Ensure mappings are attached properly (this is the fix for the issue)
+ // Ensure mappings are attached properly
anime.Mappings = types.AnimeMappings{
AniDB: mapping.AniDB,
Anilist: mapping.Anilist,
@@ -78,14 +79,14 @@ func (s *Service) GetAnimeDetailsWithSource(mapping *entities.AnimeMapping, sour
return anime, nil
}
} else {
- logger.Log(fmt.Sprintf("Bypassing cache for anime (MAL ID: %d) - source: %s", malID, source), logger.LogOptions{
+ logger.Log(fmt.Sprintf("Bypassing database check for anime (MAL ID: %d) - source: %s", malID, source), logger.LogOptions{
Level: logger.Info,
Prefix: "AnimeAPI",
})
}
// Rest of the implementation is the same as GetAnimeDetails
- logger.Log(fmt.Sprintf("Cache miss for anime (MAL ID: %d), fetching fresh data", malID), logger.LogOptions{
+ logger.Log(fmt.Sprintf("No existing data for anime (MAL ID: %d), fetching fresh data", malID), logger.LogOptions{
Level: logger.Info,
Prefix: "AnimeAPI",
})
@@ -432,25 +433,25 @@ func (s *Service) GetAnimeDetailsWithSource(mapping *entities.AnimeMapping, sour
})
}
- // Save the anime to cache only if TMDB didn't fail
+ // Save the anime to database only if TMDB didn't fail
if tmdbError == nil {
go func() {
- if err := database.SaveAnimeToCache(animeDetails); err != nil {
- logger.Log(fmt.Sprintf("Failed to save anime to cache: %v", err), logger.LogOptions{
+ if err := database.SaveAnimeToDatabase(animeDetails); err != nil {
+ logger.Log(fmt.Sprintf("Failed to save anime to database: %v", err), logger.LogOptions{
Level: logger.Error,
- Prefix: "AnimeCache",
+ Prefix: "AnimeDB",
})
} else {
- logger.Log(fmt.Sprintf("Successfully saved anime (MAL ID: %d) to cache", malID), logger.LogOptions{
+ logger.Log(fmt.Sprintf("Successfully saved anime (MAL ID: %d) to database", malID), logger.LogOptions{
Level: logger.Debug,
- Prefix: "AnimeCache",
+ Prefix: "AnimeDB",
})
}
}()
} else {
- logger.Log(fmt.Sprintf("Skipping anime cache due to TMDB error: %v", tmdbError), logger.LogOptions{
+ logger.Log(fmt.Sprintf("Skipping anime database save due to TMDB error: %v", tmdbError), logger.LogOptions{
Level: logger.Warn,
- Prefix: "AnimeCache",
+ Prefix: "AnimeDB",
})
}
diff --git a/tasks/anime_update.task.go b/tasks/anime_update.task.go
index 6d56378..38cc77a 100644
--- a/tasks/anime_update.task.go
+++ b/tasks/anime_update.task.go
@@ -29,7 +29,7 @@ const (
// animeUpdateJob represents a single anime update job
type animeUpdateJob struct {
- series entities.CachedAnime
+ series entities.Anime
reason string
}
@@ -41,7 +41,7 @@ func AnimeUpdate() error {
})
// Find all currently airing anime
- var airingSeries []entities.CachedAnime
+ var airingSeries []entities.Anime
result := database.DB.
Where("airing = ?", true).
Preload("NextAiringEpisode").
@@ -176,65 +176,80 @@ func AnimeUpdate() error {
return nil
}
-// updateAnime handles updating a single anime
-func updateAnime(animeService *anime.Service, series entities.CachedAnime, reason string) {
- // Get the mapping for this anime
+// updateAnime updates a single anime series
+func updateAnime(animeService *anime.Service, series entities.Anime, reason string) {
+ title := series.TitleRomaji
+ if series.TitleEnglish != "" {
+ title = series.TitleEnglish
+ }
+
+ logger.Log(fmt.Sprintf("Updating anime: %s (MAL ID: %d) - %s", title, series.MALID, reason), logger.LogOptions{
+ Level: logger.Info,
+ Prefix: "AnimeUpdate",
+ })
+
+ // Get anime mapping for the service call
mapping, err := database.GetAnimeMappingViaMALID(series.MALID)
if err != nil {
- logger.Log(fmt.Sprintf("Failed to get mapping for MALID %d: %v", series.MALID, err), logger.LogOptions{
+ logger.Log(fmt.Sprintf("Error getting anime mapping for %s (MAL ID: %d): %v", title, series.MALID, err), logger.LogOptions{
Level: logger.Error,
Prefix: "AnimeUpdate",
})
return
}
- logger.Log(fmt.Sprintf("Updating anime %s (MALID: %d) - Reason: %s",
- series.TitleRomaji, series.MALID, reason), logger.LogOptions{
- Level: logger.Info,
- Prefix: "AnimeUpdate",
- })
-
- // Get fresh anime data
- updatedAnime, err := animeService.GetAnimeDetailsWithSource(mapping, UpdaterSource)
+ // Get updated anime data from API
+ updatedAnime, err := animeService.GetAnimeDetailsWithSource(mapping, "mal")
if err != nil {
- logger.Log(fmt.Sprintf("Failed to get fresh data for MALID %d: %v", series.MALID, err), logger.LogOptions{
+ logger.Log(fmt.Sprintf("Error getting updated anime data for %s (MAL ID: %d): %v", title, series.MALID, err), logger.LogOptions{
Level: logger.Error,
Prefix: "AnimeUpdate",
})
return
}
- // Check if significant changes occurred to save the update
- saved := false
- oldCachedAnime, err := database.GetCachedAnimeByMALID(series.MALID)
+ logger.Log(fmt.Sprintf("Successfully updated anime: %s (MAL ID: %d)", title, series.MALID), logger.LogOptions{
+ Level: logger.Info,
+ Prefix: "AnimeUpdate",
+ })
+
+ // Check if the updated anime data has significant changes that warrant saving
+ if shouldSaveUpdate(&series, updatedAnime) {
+ // Check if anime is still airing
+ if updatedAnime.Status != "RELEASING" && updatedAnime.Status != "AIRING" {
+ // Update the anime data to reflect that it's no longer airing
+ updatedAnime.Airing = false
+ }
- if err != nil || shouldSaveUpdate(oldCachedAnime, updatedAnime) {
- saved = true
- // Save the updated data to cache
- if err := database.SaveAnimeToCache(updatedAnime); err != nil {
- logger.Log(fmt.Sprintf("Failed to save updated data for MALID %d: %v", series.MALID, err), logger.LogOptions{
+ if err := database.SaveAnimeToDatabase(updatedAnime); err != nil {
+ logger.Log(fmt.Sprintf("Error saving updated anime data for %s (MAL ID: %d): %v", title, series.MALID, err), logger.LogOptions{
Level: logger.Error,
Prefix: "AnimeUpdate",
})
- return
- }
- }
+ } else {
+ logger.Log(fmt.Sprintf("Successfully saved updated data for %s (MAL ID: %d)", title, series.MALID), logger.LogOptions{
+ Level: logger.Info,
+ Prefix: "AnimeUpdate",
+ })
- status := "skipped (no significant changes)"
- if saved {
- status = "saved to database"
+ if !updatedAnime.Airing {
+ logger.Log(fmt.Sprintf("Anime %s (MAL ID: %d) is no longer airing. Status: %s", title, series.MALID, updatedAnime.Status), logger.LogOptions{
+ Level: logger.Info,
+ Prefix: "AnimeUpdate",
+ })
+ }
+ }
+ } else {
+ logger.Log(fmt.Sprintf("No significant changes detected for %s (MAL ID: %d), skipping database update", title, series.MALID), logger.LogOptions{
+ Level: logger.Debug,
+ Prefix: "AnimeUpdate",
+ })
}
-
- logger.Log(fmt.Sprintf("Update for %s (MALID: %d) complete - %s",
- series.TitleRomaji, series.MALID, status), logger.LogOptions{
- Level: logger.Info,
- Prefix: "AnimeUpdate",
- })
}
// shouldSaveUpdate determines if the updated anime data has significant changes
// that warrant saving it to the database
-func shouldSaveUpdate(oldAnime *entities.CachedAnime, newAnime *types.Anime) bool {
+func shouldSaveUpdate(oldAnime *entities.Anime, newAnime *types.Anime) bool {
if oldAnime == nil {
return true
}