]> git.lizzy.rs Git - go-anidb.git/blobdiff - animecache.go
anidb: Log UDP traffic
[go-anidb.git] / animecache.go
index ad44bfa6f3ebef7d39ec8578d67ca91dc5c6493c..99b89015081833512858db5923f88c6d9bc93429 100644 (file)
@@ -14,6 +14,7 @@ import (
 
 func init() {
        gob.RegisterName("*github.com/Kovensky/go-anidb.Anime", &Anime{})
+       gob.RegisterName("github.com/Kovensky/go-anidb.AID", AID(0))
 }
 
 func (a *Anime) Touch() {
@@ -24,16 +25,27 @@ func (a *Anime) IsStale() bool {
        if a == nil {
                return true
        }
+       if a.Incomplete {
+               return time.Now().Sub(a.Cached) > AnimeIncompleteCacheDuration
+       }
        return time.Now().Sub(a.Cached) > AnimeCacheDuration
 }
 
 // Unique Anime IDentifier.
 type AID int
 
+// make AID Cacheable
+
+func (e AID) Touch()        {}
+func (e AID) IsStale() bool { return false }
+
 // Returns a cached Anime. Returns nil if there is no cached Anime with this AID.
 func (aid AID) Anime() *Anime {
-       a, _ := caches.Get(animeCache).Get(int(aid)).(*Anime)
-       return a
+       var a Anime
+       if cache.Get(&a, "aid", aid) == nil {
+               return &a
+       }
+       return nil
 }
 
 type httpAnimeResponse struct {
@@ -41,25 +53,31 @@ type httpAnimeResponse struct {
        err   error
 }
 
-// Retrieves an Anime from the cache if possible. If it isn't cached,
-// or if the cache is stale, queries both the UDP and HTTP APIs
-// for data.
-//
-// Note: This can take at least 4 seconds during heavy traffic.
+// Retrieves an Anime by its AID. Uses both the HTTP and UDP APIs,
+// but can work without the UDP API.
 func (adb *AniDB) AnimeByID(aid AID) <-chan *Anime {
+       keys := []cacheKey{"aid", aid}
        ch := make(chan *Anime, 1)
 
-       anime := aid.Anime()
-       if !anime.IsStale() {
-               ch <- anime
+       if aid < 1 {
+               ch <- nil
                close(ch)
-               return ch
        }
 
-       ac := caches.Get(animeCache)
        ic := make(chan Cacheable, 1)
        go func() { ch <- (<-ic).(*Anime); close(ch) }()
-       if ac.Intent(int(aid), ic) {
+       if intentMap.Intent(ic, keys...) {
+               return ch
+       }
+
+       if !cache.CheckValid(keys...) {
+               intentMap.NotifyClose((*Anime)(nil), keys...)
+               return ch
+       }
+
+       anime := aid.Anime()
+       if !anime.IsStale() {
+               intentMap.NotifyClose(anime, keys...)
                return ch
        }
 
@@ -94,34 +112,51 @@ func (adb *AniDB) AnimeByID(aid AID) <-chan *Anime {
                                        ok = false
                                        break Loop
                                }
-                               if a := anime.populateFromHTTP(resp.anime); a == nil {
+                               if !anime.populateFromHTTP(resp.anime) {
+                                       // HTTP ok but parsing not ok
+                                       if anime.PrimaryTitle == "" {
+                                               cache.MarkInvalid(keys...)
+                                       }
+
+                                       if resp.anime.Error == "Anime not found" {
+                                               // deleted AID?
+                                               cache.Delete(keys...)
+                                       }
+
                                        ok = false
                                        break Loop
-                               } else {
-                                       anime = a
                                }
+
                                httpChan = nil
                        case reply := <-udpChan:
-                               anime.Incomplete = !anime.populateFromUDP(reply)
+                               if reply.Code() == 330 {
+                                       cache.MarkInvalid(keys...)
+                                       // deleted AID?
+                                       cache.Delete(keys...)
+
+                                       ok = false
+                                       break Loop
+                               } else {
+                                       anime.Incomplete = !anime.populateFromUDP(reply)
+                               }
                                udpChan = nil
                        }
                }
                if anime.PrimaryTitle != "" {
                        if ok {
-                               ac.Set(int(aid), anime)
-                       } else {
-                               ac.Flush(int(aid), anime)
+                               cache.Set(anime, keys...)
                        }
+                       intentMap.NotifyClose(anime, keys...)
                } else {
-                       ac.Set(int(aid), (*Anime)(nil))
+                       intentMap.NotifyClose((*Anime)(nil), keys...)
                }
        }()
        return ch
 }
 
-func (a *Anime) populateFromHTTP(reply httpapi.Anime) *Anime {
+func (a *Anime) populateFromHTTP(reply httpapi.Anime) bool {
        if reply.Error != "" {
-               return (*Anime)(nil)
+               return false
        }
 
        if a.AID != AID(reply.ID) {
@@ -145,12 +180,6 @@ func (a *Anime) populateFromHTTP(reply httpapi.Anime) *Anime {
        for _, title := range reply.Titles {
                switch title.Type {
                case "main":
-                       if a.PrimaryTitle != "" {
-                               // We assume there's only ever one "main" title
-                               panic(
-                                       fmt.Sprintf("PrimaryTitle %q already set, new PrimaryTitle %q received!",
-                                               a.PrimaryTitle, title.Title))
-                       }
                        a.PrimaryTitle = title.Title
                case "official":
                        if a.OfficialTitles == nil {
@@ -203,7 +232,7 @@ func (a *Anime) populateFromHTTP(reply httpapi.Anime) *Anime {
                        titles[Language(title.Lang)] = title.Title
                }
 
-               e := Episode{
+               e := &Episode{
                        EID: EID(ep.ID),
                        AID: a.AID,
 
@@ -219,12 +248,12 @@ func (a *Anime) populateFromHTTP(reply httpapi.Anime) *Anime {
                        Titles: titles,
                }
                counts[e.Type]++
-               cacheEpisode(&e)
+               cacheEpisode(e)
 
                a.Episodes = append(a.Episodes, e)
        }
 
-       a.EpisodeCount = EpisodeCount{
+       a.EpisodeCount = misc.EpisodeCount{
                RegularCount: counts[misc.EpisodeTypeRegular],
                SpecialCount: counts[misc.EpisodeTypeSpecial],
                CreditsCount: counts[misc.EpisodeTypeCredits],
@@ -239,7 +268,7 @@ func (a *Anime) populateFromHTTP(reply httpapi.Anime) *Anime {
                }
        }
 
-       return a
+       return true
 }
 
 func (a *Anime) populateResources(list []httpapi.Resource) {