]> git.lizzy.rs Git - go-anidb.git/blob - episodecache.go
Modernize
[go-anidb.git] / episodecache.go
1 package anidb
2
3 import (
4         "github.com/EliasFleckenstein03/go-fscache"
5         "strconv"
6         "strings"
7         "time"
8 )
9
10 var _ cacheable = &Episode{}
11
12 func (e *Episode) setCachedTS(ts time.Time) {
13         e.Cached = ts
14 }
15
16 func (e *Episode) IsStale() bool {
17         if e == nil {
18                 return true
19         }
20         return time.Now().Sub(e.Cached) > EpisodeCacheDuration
21 }
22
23 // Unique Episode IDentifier.
24 type EID int
25
26 // Retrieves the Episode corresponding to this EID from the cache.
27 func (eid EID) Episode() *Episode {
28         var e Episode
29         if CacheGet(&e, "eid", eid) == nil {
30                 return &e
31         }
32         return nil
33 }
34
35 func cacheEpisode(ep *Episode) {
36         CacheSet(ep.AID, "aid", "by-eid", ep.EID)
37         CacheSet(ep, "eid", ep.EID)
38 }
39
40 // Retrieves an Episode by its EID.
41 //
42 // If we know which AID owns this EID, then it's equivalent
43 // to an Anime query. Otherwise, uses both the HTTP and UDP
44 // APIs to retrieve it.
45 func (adb *AniDB) EpisodeByID(eid EID) <-chan *Episode {
46         key := []fscache.CacheKey{"eid", eid}
47         ch := make(chan *Episode, 1)
48
49         if eid < 1 {
50                 ch <- nil
51                 close(ch)
52                 return ch
53         }
54
55         ic := make(chan notification, 1)
56         go func() { ch <- (<-ic).(*Episode); close(ch) }()
57         if intentMap.Intent(ic, key...) {
58                 return ch
59         }
60
61         if !Cache.IsValid(InvalidKeyCacheDuration, key...) {
62                 intentMap.NotifyClose((*Episode)(nil), key...)
63                 return ch
64         }
65
66         e := eid.Episode()
67         if !e.IsStale() {
68                 intentMap.NotifyClose(e, key...)
69                 return ch
70         }
71
72         go func() {
73                 // The UDP API data is worse than the HTTP API anime data and
74                 // might even get truncated on some pathological cases;
75                 // try and get from the corresponding Anime, which uses the HTTP
76                 // API episode list.
77
78                 aid := AID(0)
79                 _, err := Cache.Get(&aid, "aid", "by-eid", eid)
80                 ok := err == nil
81
82                 udpDone := false
83
84                 for i := 0; i < 2; i++ {
85                         if !ok && udpDone {
86                                 // couldn't get anime and we already ran the EPISODE query
87                                 break
88                         }
89
90                         if !ok {
91                                 // We don't know what the AID is yet.
92                                 reply := <-adb.udp.SendRecv("EPISODE", paramMap{"eid": eid})
93
94                                 if reply.Error() == nil {
95                                         parts := strings.Split(reply.Lines()[1], "|")
96
97                                         if id, err := strconv.ParseInt(parts[1], 10, 32); err == nil {
98                                                 ok = true
99                                                 aid = AID(id)
100                                         } else {
101                                                 break
102                                         }
103                                 } else if reply.Code() == 340 {
104                                         Cache.SetInvalid(key...)
105                                         break
106                                 } else {
107                                         break
108                                 }
109                                 udpDone = true
110                         }
111                         a := <-adb.AnimeByID(AID(aid)) // updates the episode cache as well
112                         ep := a.EpisodeByEID(eid)
113
114                         if ep != nil {
115                                 e = ep
116                                 break
117                         } else {
118                                 // the EID<->AID map broke
119                                 ok = false
120                                 Cache.Delete("aid", "by-eid", eid)
121                         }
122                 }
123                 intentMap.NotifyClose(e, key...)
124         }()
125         return ch
126 }