]> git.lizzy.rs Git - go-anidb.git/blob - episodecache.go
anime: Make cached Anime expire earlier if they're Incomplete
[go-anidb.git] / episodecache.go
1 package anidb
2
3 import (
4         "encoding/gob"
5         "strconv"
6         "strings"
7         "sync"
8         "time"
9 )
10
11 func init() {
12         gob.RegisterName("*github.com/Kovensky/go-anidb.Episode", &Episode{})
13 }
14
15 func (e *Episode) Touch() {
16         e.Cached = time.Now()
17 }
18
19 func (e *Episode) IsStale() bool {
20         if e == nil {
21                 return true
22         }
23         return time.Now().Sub(e.Cached) > EpisodeCacheDuration
24 }
25
26 var eidAidMap = map[EID]AID{}
27 var eidAidLock = sync.RWMutex{}
28
29 // Unique Episode IDentifier.
30 type EID int
31
32 // Retrieves the Episode corresponding to this EID from the cache.
33 func (eid EID) Episode() *Episode {
34         e, _ := caches.Get(episodeCache).Get(int(eid)).(*Episode)
35         return e
36 }
37
38 func cacheEpisode(ep *Episode) {
39         eidAidLock.Lock()
40         defer eidAidLock.Unlock()
41
42         eidAidMap[ep.EID] = ep.AID
43         caches.Get(episodeCache).Set(int(ep.EID), ep)
44 }
45
46 // Retrieves the Episode from the cache if possible.
47 //
48 // If the result is stale, then queries the UDP API to
49 // know which AID owns this EID, then gets the episodes
50 // from the Anime.
51 func (adb *AniDB) EpisodeByID(eid EID) <-chan *Episode {
52         ch := make(chan *Episode, 1)
53
54         if e := eid.Episode(); e != nil && !e.IsStale() {
55                 ch <- e
56                 close(ch)
57                 return ch
58         }
59
60         ec := caches.Get(episodeCache)
61         ic := make(chan Cacheable, 1)
62         go func() { ch <- (<-ic).(*Episode); close(ch) }()
63         if ec.Intent(int(eid), ic) {
64                 return ch
65         }
66
67         go func() {
68                 // The UDP API data is worse than the HTTP API anime data,
69                 // try and get from the corresponding Anime
70
71                 eidAidLock.RLock()
72                 aid, ok := eidAidMap[eid]
73                 eidAidLock.RUnlock()
74
75                 udpDone := false
76
77                 var e *Episode
78                 for i := 0; i < 2; i++ {
79                         if !ok && udpDone {
80                                 // couldn't get anime and we already ran the EPISODE query
81                                 break
82                         }
83
84                         if !ok {
85                                 // We don't know what the AID is yet.
86                                 reply := <-adb.udp.SendRecv("EPISODE", paramMap{"eid": eid})
87
88                                 if reply.Error() == nil {
89                                         parts := strings.Split(reply.Lines()[1], "|")
90
91                                         if id, err := strconv.ParseInt(parts[1], 10, 32); err == nil {
92                                                 ok = true
93                                                 aid = AID(id)
94                                         }
95                                 } else {
96                                         break
97                                 }
98                                 udpDone = true
99                         }
100                         <-adb.AnimeByID(AID(aid)) // this caches episodes...
101                         e = eid.Episode()         // ...so this is now a cache hit
102
103                         if e != nil {
104                                 break
105                         } else {
106                                 // if this is somehow still a miss, then the EID<->AID map broke
107                                 eidAidLock.Lock()
108                                 delete(eidAidMap, eid)
109                                 eidAidLock.Unlock()
110
111                                 ok = false
112                         }
113                 }
114                 // Caching (and channel broadcasting) done by AnimeByID
115         }()
116         return ch
117 }