]> git.lizzy.rs Git - go-anidb.git/blob - mylistanimecache.go
anidb: Make ReAuth try to use the cache before sending USER query
[go-anidb.git] / mylistanimecache.go
1 package anidb
2
3 import (
4         "github.com/Kovensky/go-anidb/misc"
5         "github.com/Kovensky/go-anidb/udp"
6         "github.com/Kovensky/go-fscache"
7         "strings"
8         "time"
9 )
10
11 func (a *MyListAnime) setCachedTS(ts time.Time) {
12         a.Cached = ts
13 }
14
15 func (a *MyListAnime) IsStale() bool {
16         if a == nil {
17                 return true
18         }
19
20         return time.Now().Sub(a.Cached) > MyListCacheDuration
21 }
22
23 var _ cacheable = &MyListAnime{}
24
25 func (uid UID) MyListAnime(aid AID) *MyListAnime {
26         var a MyListAnime
27         if CacheGet(&a, "mylist-anime", uid, aid) == nil {
28                 return &a
29         }
30         return nil
31 }
32
33 func (adb *AniDB) MyListAnime(aid AID) <-chan *MyListAnime {
34         ch := make(chan *MyListAnime, 1)
35
36         if aid < 1 {
37                 ch <- nil
38                 close(ch)
39                 return ch
40         }
41
42         go func() {
43                 user := <-adb.GetCurrentUser()
44                 if user == nil || user.UID < 1 {
45                         ch <- nil
46                         close(ch)
47                         return
48                 }
49                 key := []fscache.CacheKey{"mylist-anime", user.UID, aid}
50
51                 ic := make(chan notification, 2)
52                 go func() { ch <- (<-ic).(*MyListAnime); close(ch) }()
53                 if intentMap.Intent(ic, key...) {
54                         return
55                 }
56
57                 if !Cache.IsValid(InvalidKeyCacheDuration, key...) {
58                         intentMap.NotifyClose((*MyListAnime)(nil), key...)
59                         return
60                 }
61
62                 entry := user.UID.MyListAnime(aid)
63                 if !entry.IsStale() {
64                         intentMap.NotifyClose(entry, key...)
65                         return
66                 }
67
68                 reply := <-adb.udp.SendRecv("MYLIST", paramMap{"aid": aid})
69
70                 switch reply.Code() {
71                 case 221:
72                         r := adb.parseMylistReply(reply) // caches
73
74                         // we have only a single file added for this anime -- construct a fake 312 struct
75                         entry = &MyListAnime{AID: aid}
76
77                         ep := <-adb.EpisodeByID(r.EID)
78                         list := misc.EpisodeToList(&ep.Episode)
79
80                         entry.EpisodesWithState = MyListStateMap{
81                                 r.MyListState: list,
82                         }
83
84                         if !r.DateWatched.IsZero() {
85                                 entry.WatchedEpisodes = list
86                         }
87
88                         entry.EpisodesPerGroup = GroupEpisodes{
89                                 r.GID: list,
90                         }
91                 case 312:
92                         entry = adb.parseMylistAnime(reply)
93                         entry.AID = aid
94                 case 321:
95                         Cache.SetInvalid(key...)
96                 }
97
98                 CacheSet(entry, key...)
99                 intentMap.NotifyClose(entry, key...)
100         }()
101         return ch
102 }
103
104 func (adb *AniDB) UserMyListAnime(uid UID, aid AID) <-chan *MyListAnime {
105         key := []fscache.CacheKey{"mylist-anime", uid, aid}
106         ch := make(chan *MyListAnime, 1)
107
108         if uid < 1 || aid < 1 {
109                 ch <- nil
110                 close(ch)
111                 return ch
112         }
113
114         ic := make(chan notification, 1)
115         go func() { ch <- (<-ic).(*MyListAnime); close(ch) }()
116         if intentMap.Intent(ic, key...) {
117                 return ch
118         }
119
120         if !Cache.IsValid(InvalidKeyCacheDuration, key...) {
121                 intentMap.NotifyClose((*MyListAnime)(nil), key...)
122                 return ch
123         }
124
125         entry := uid.MyListAnime(aid)
126         if !entry.IsStale() {
127                 intentMap.NotifyClose(entry, key...)
128                 return ch
129         }
130
131         go func() {
132                 user := <-adb.GetCurrentUser()
133
134                 if user.UID != uid { // we can't query other users' lists from API
135                         intentMap.NotifyClose(entry, key...)
136                         return
137                 }
138
139                 intentMap.NotifyClose(<-adb.MyListAnime(aid), key...)
140         }()
141         return ch
142 }
143
144 func (adb *AniDB) parseMylistAnime(reply udpapi.APIReply) *MyListAnime {
145         if reply.Code() != 312 {
146                 return nil
147         }
148
149         parts := strings.Split(reply.Lines()[1], "|")
150
151         // Everything from index 7 on is pairs of group name on odd positions and episode list on even
152         var groupParts []string
153         if len(parts) > 7 {
154                 groupParts = parts[7:]
155         }
156
157         groupMap := make(GroupEpisodes, len(groupParts)/2)
158
159         for i := 0; i+1 < len(groupParts); i += 2 {
160                 g := <-adb.GroupByName(groupParts[i])
161                 if g == nil {
162                         continue
163                 }
164
165                 groupMap[g.GID] = misc.ParseEpisodeList(groupParts[i+1])
166         }
167
168         return &MyListAnime{
169                 EpisodesWithState: MyListStateMap{
170                         MyListStateUnknown: misc.ParseEpisodeList(parts[2]),
171                         MyListStateHDD:     misc.ParseEpisodeList(parts[3]),
172                         MyListStateCD:      misc.ParseEpisodeList(parts[4]),
173                         MyListStateDeleted: misc.ParseEpisodeList(parts[5]),
174                 },
175
176                 WatchedEpisodes: misc.ParseEpisodeList(parts[6]),
177
178                 EpisodesPerGroup: groupMap,
179         }
180 }