]> git.lizzy.rs Git - go-anidb.git/blob - groupcache.go
Modernize
[go-anidb.git] / groupcache.go
1 package anidb
2
3 import (
4         "github.com/EliasFleckenstein03/go-anidb/http"
5         "github.com/EliasFleckenstein03/go-anidb/udp"
6         "github.com/EliasFleckenstein03/go-fscache"
7         "strconv"
8         "strings"
9         "time"
10 )
11
12 var _ cacheable = &Group{}
13
14 func (g *Group) setCachedTS(ts time.Time) {
15         g.Cached = ts
16 }
17
18 func (g *Group) IsStale() bool {
19         if g == nil {
20                 return true
21         }
22         return time.Now().Sub(g.Cached) > GroupCacheDuration
23 }
24
25 // Unique Group IDentifier
26 type GID int
27
28 func cacheGroup(g *Group) {
29         CacheSet(g.GID, "gid", "by-name", g.Name)
30         CacheSet(g.GID, "gid", "by-shortname", g.ShortName)
31         CacheSet(g, "gid", g.GID)
32 }
33
34 // Retrieves the Group from the cache.
35 func (gid GID) Group() *Group {
36         var g Group
37         if CacheGet(&g, "gid", gid) == nil {
38                 return &g
39         }
40         return nil
41 }
42
43 // Retrieves a Group by its GID. Uses the UDP API.
44 func (adb *AniDB) GroupByID(gid GID) <-chan *Group {
45         key := []fscache.CacheKey{"gid", gid}
46         ch := make(chan *Group, 1)
47
48         if gid < 1 {
49                 ch <- nil
50                 close(ch)
51                 return ch
52         }
53
54         ic := make(chan notification, 1)
55         go func() { ch <- (<-ic).(*Group); close(ch) }()
56         if intentMap.Intent(ic, key...) {
57                 return ch
58         }
59
60         if !Cache.IsValid(InvalidKeyCacheDuration, key...) {
61                 intentMap.NotifyClose((*Group)(nil), key...)
62                 return ch
63         }
64
65         g := gid.Group()
66         if !g.IsStale() {
67                 intentMap.NotifyClose(g, key...)
68                 return ch
69         }
70
71         go func() {
72                 reply := <-adb.udp.SendRecv("GROUP",
73                         paramMap{"gid": gid})
74
75                 if reply.Error() == nil {
76                         g = parseGroupReply(reply)
77
78                         cacheGroup(g)
79                 } else if reply.Code() == 350 {
80                         Cache.SetInvalid(key...)
81                 }
82
83                 intentMap.NotifyClose(g, key...)
84         }()
85         return ch
86 }
87
88 // Retrieves a Group by its name. Either full or short names are matched.
89 // Uses the UDP API.
90 func (adb *AniDB) GroupByName(gname string) <-chan *Group {
91         key := []fscache.CacheKey{"gid", "by-name", gname}
92         altKey := []fscache.CacheKey{"gid", "by-shortname", gname}
93         ch := make(chan *Group, 1)
94
95         if gname == "" {
96                 ch <- nil
97                 close(ch)
98                 return ch
99         }
100
101         ic := make(chan notification, 1)
102         go func() {
103                 gid := (<-ic).(GID)
104                 if gid > 0 {
105                         ch <- <-adb.GroupByID(gid)
106                 }
107                 close(ch)
108         }()
109         if intentMap.Intent(ic, key...) {
110                 return ch
111         }
112
113         if !Cache.IsValid(InvalidKeyCacheDuration, key...) {
114                 intentMap.NotifyClose(GID(0), key...)
115                 return ch
116         }
117
118         gid := GID(0)
119
120         switch ts, err := Cache.Get(&gid, key...); {
121         case err == nil && time.Now().Sub(ts) < GroupCacheDuration:
122                 intentMap.NotifyClose(gid, key...)
123                 return ch
124         default:
125                 switch ts, err = Cache.Get(&gid, altKey...); {
126                 case err == nil && time.Now().Sub(ts) < GroupCacheDuration:
127                         intentMap.NotifyClose(gid, key...)
128                         return ch
129                 }
130         }
131
132         go func() {
133                 reply := <-adb.udp.SendRecv("GROUP",
134                         paramMap{"gname": gname})
135
136                 var g *Group
137                 if reply.Error() == nil {
138                         g = parseGroupReply(reply)
139
140                         gid = g.GID
141
142                         cacheGroup(g)
143                 } else if reply.Code() == 350 {
144                         Cache.SetInvalid(key...)
145                         Cache.SetInvalid(altKey...)
146                 }
147
148                 intentMap.NotifyClose(gid, key...)
149         }()
150         return ch
151 }
152
153 func parseGroupReply(reply udpapi.APIReply) *Group {
154         parts := strings.Split(reply.Lines()[1], "|")
155         ints := make([]int64, len(parts))
156         for i := range parts {
157                 ints[i], _ = strconv.ParseInt(parts[i], 10, 32)
158         }
159
160         irc := ""
161         if parts[7] != "" {
162                 irc = "irc://" + parts[8] + "/" + parts[7][1:]
163         }
164
165         pic := ""
166         if parts[10] != "" {
167                 pic = httpapi.AniDBImageBaseURL + parts[10]
168         }
169
170         rellist := strings.Split(parts[16], "'")
171         relations := make(map[GID]GroupRelationType, len(rellist))
172         for _, rel := range rellist {
173                 r := strings.Split(rel, ",")
174                 if len(r) < 2 {
175                         continue
176                 }
177                 gid, _ := strconv.ParseInt(r[0], 10, 32)
178                 typ, _ := strconv.ParseInt(r[1], 10, 32)
179
180                 relations[GID(gid)] = GroupRelationType(typ)
181         }
182
183         ft := time.Unix(ints[11], 0)
184         if ints[11] == 0 {
185                 ft = time.Time{}
186         }
187         dt := time.Unix(ints[12], 0)
188         if ints[12] == 0 {
189                 dt = time.Time{}
190         }
191         lr := time.Unix(ints[14], 0)
192         if ints[14] == 0 {
193                 lr = time.Time{}
194         }
195         la := time.Unix(ints[15], 0)
196         if ints[15] == 0 {
197                 la = time.Time{}
198         }
199
200         return &Group{
201                 GID: GID(ints[0]),
202
203                 Name:      parts[5],
204                 ShortName: parts[6],
205
206                 IRC:     irc,
207                 URL:     parts[9],
208                 Picture: pic,
209
210                 Founded:   ft,
211                 Disbanded: dt,
212                 // ignore ints[13]
213                 LastRelease:  lr,
214                 LastActivity: la,
215
216                 Rating: Rating{
217                         Rating:    float32(ints[1]) / 100,
218                         VoteCount: int(ints[2]),
219                 },
220                 AnimeCount: int(ints[3]),
221                 FileCount:  int(ints[4]),
222
223                 RelatedGroups: relations,
224
225                 Cached: time.Now(),
226         }
227 }