feat: bump availability version to 4 and drop jikan-only cache paths

This commit is contained in:
2026-07-06 07:09:06 +02:00
parent 1110cb1809
commit d06d6de4c4

View File

@@ -4,32 +4,28 @@ import (
"context" "context"
"database/sql" "database/sql"
"encoding/json" "encoding/json"
"strings"
"time" "time"
"mal/integrations/jikan"
"mal/internal/db" "mal/internal/db"
"mal/internal/domain" "mal/internal/domain"
"mal/internal/observability" "mal/internal/observability"
) )
const episodeAvailabilityPayloadVersion = 2 const episodeAvailabilityPayloadVersion = 4
func (s *EpisodeService) store(ctx context.Context, anime domain.Anime, jikanEpisodes []jikan.Episode, availability domain.EpisodeAvailability, source string, now time.Time, providerSuccess bool) (domain.CanonicalEpisodeList, error) { func (s *EpisodeService) store(ctx context.Context, anime domain.Anime, availability domain.EpisodeAvailability, source string, now time.Time) (domain.CanonicalEpisodeList, error) {
nextRefreshSQL := nextRefreshAt(anime, now) nextRefreshSQL := nextRefreshAt(anime, now)
episodes := mergeEpisodesForAnime(anime, jikanEpisodes, availability, now, providerSuccess) episodes := mergeEpisodes(nil, availability, 0)
payload := domain.CanonicalEpisodeList{ payload := domain.CanonicalEpisodeList{
AnimeID: anime.MalID, AnimeID: anime.MalID,
Episodes: episodes, Episodes: episodes,
Source: source, Source: source,
AvailabilityVersion: episodeAvailabilityPayloadVersion, AvailabilityVersion: episodeAvailabilityPayloadVersion,
ReleaseChecked: !providerSuccess, ReleaseChecked: false,
LastAttemptAt: now.Format(time.RFC3339), LastAttemptAt: now.Format(time.RFC3339),
FailureCount: 0, FailureCount: 0,
} }
if providerSuccess { payload.LastSuccessAt = now.Format(time.RFC3339)
payload.LastSuccessAt = now.Format(time.RFC3339)
}
if nextRefreshSQL.Valid { if nextRefreshSQL.Valid {
payload.NextRefreshAt = nextRefreshSQL.Time.Format(time.RFC3339) payload.NextRefreshAt = nextRefreshSQL.Time.Format(time.RFC3339)
} }
@@ -39,7 +35,7 @@ func (s *EpisodeService) store(ctx context.Context, anime domain.Anime, jikanEpi
return domain.CanonicalEpisodeList{}, err return domain.CanonicalEpisodeList{}, err
} }
if !s.writeEpisodeAvailabilityCache(ctx, anime, source, body, now, providerSuccess, nextRefreshSQL) { if !s.writeEpisodeAvailabilityCache(ctx, anime, source, body, now, true, nextRefreshSQL) {
return payload, nil return payload, nil
} }
@@ -242,8 +238,11 @@ func (s *EpisodeService) decodeCachedPayload(anime domain.Anime, raw string) (do
) )
return domain.CanonicalEpisodeList{}, false return domain.CanonicalEpisodeList{}, false
} }
if payload.Source == "jikan_fallback" || payload.Source == "legacy_disabled" {
return domain.CanonicalEpisodeList{}, false
}
if isStaleAiringEpisodePayload(anime, payload) { if isStaleProviderEpisodePayload(payload) {
observability.Info( observability.Info(
"episodes_cached_payload_rejected_stale_version", "episodes_cached_payload_rejected_stale_version",
"episodes", "episodes",
@@ -258,20 +257,6 @@ func (s *EpisodeService) decodeCachedPayload(anime domain.Anime, raw string) (do
return domain.CanonicalEpisodeList{}, false return domain.CanonicalEpisodeList{}, false
} }
if isUncheckedAiringJikanFallback(anime, payload) {
observability.Info(
"episodes_cached_payload_rejected_unchecked_fallback",
"episodes",
"",
map[string]any{
"anime_id": anime.MalID,
"cached_episodes": len(payload.Episodes),
"source": payload.Source,
},
)
return domain.CanonicalEpisodeList{}, false
}
if !isCanonicalEpisodePayloadValid(payload, anime.Episodes) { if !isCanonicalEpisodePayloadValid(payload, anime.Episodes) {
observability.Info( observability.Info(
"episodes_cached_payload_rejected", "episodes_cached_payload_rejected",
@@ -289,17 +274,6 @@ func (s *EpisodeService) decodeCachedPayload(anime domain.Anime, raw string) (do
return payload, true return payload, true
} }
func isStaleAiringEpisodePayload(anime domain.Anime, payload domain.CanonicalEpisodeList) bool { func isStaleProviderEpisodePayload(payload domain.CanonicalEpisodeList) bool {
return isAiring(anime) && len(payload.Episodes) > 0 && payload.AvailabilityVersion < episodeAvailabilityPayloadVersion return payload.Source != "" && payload.Source != "jikan_fallback" && payload.Source != "legacy_disabled" && payload.AvailabilityVersion < episodeAvailabilityPayloadVersion
}
func isUncheckedAiringJikanFallback(anime domain.Anime, payload domain.CanonicalEpisodeList) bool {
if !isAiring(anime) || payload.ReleaseChecked || len(payload.Episodes) == 0 {
return false
}
return payload.Source == "jikan_fallback" || payload.Source == "legacy_disabled"
}
func isAiring(anime domain.Anime) bool {
return anime.Airing || strings.EqualFold(strings.TrimSpace(anime.Status), "Currently Airing")
} }