]> git.lizzy.rs Git - go-anidb.git/blob - mylistanimecache.go
anidb: Support querying for mylist of an Anime
[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, 1)
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                         switch r.MyListState {
81                         case MyListStateUnknown:
82                                 entry.UnknownState = list
83                         case MyListStateHDD:
84                                 entry.OnHDD = list
85                         case MyListStateCD:
86                                 entry.OnCD = list
87                         case MyListStateDeleted:
88                                 entry.Deleted = list
89                         }
90
91                         if !r.DateWatched.IsZero() {
92                                 entry.WatchedEpisodes = list
93                         }
94
95                         entry.EpisodesPerGroup = map[GID]misc.EpisodeList{
96                                 r.GID: list,
97                         }
98                 case 312:
99                         entry = adb.parseMylistAnime(reply)
100                         entry.AID = aid
101                 case 321:
102                         Cache.SetInvalid(key...)
103                 }
104
105                 CacheSet(entry, key...)
106                 intentMap.NotifyClose(entry, key...)
107         }()
108         return ch
109 }
110
111 func (adb *AniDB) UserMyListAnime(uid UID, aid AID) <-chan *MyListAnime {
112         key := []fscache.CacheKey{"mylist-anime", uid, aid}
113         ch := make(chan *MyListAnime, 1)
114
115         if uid < 1 || aid < 1 {
116                 ch <- nil
117                 close(ch)
118                 return ch
119         }
120
121         ic := make(chan notification, 1)
122         go func() { ch <- (<-ic).(*MyListAnime); close(ch) }()
123         if intentMap.Intent(ic, key...) {
124                 return ch
125         }
126
127         if !Cache.IsValid(InvalidKeyCacheDuration, key...) {
128                 intentMap.NotifyClose((*MyListAnime)(nil), key...)
129                 return ch
130         }
131
132         entry := uid.MyListAnime(aid)
133         if !entry.IsStale() {
134                 intentMap.NotifyClose(entry, key...)
135                 return ch
136         }
137
138         go func() {
139                 user := <-adb.GetCurrentUser()
140
141                 if user.UID != uid { // we can't query other users' lists from API
142                         intentMap.NotifyClose(entry, key...)
143                         return
144                 }
145
146                 intentMap.NotifyClose(<-adb.MyListAnime(aid), key...)
147         }()
148         return ch
149 }
150
151 func (adb *AniDB) parseMylistAnime(reply udpapi.APIReply) *MyListAnime {
152         if reply.Code() != 312 {
153                 return nil
154         }
155
156         parts := strings.Split(reply.Lines()[1], "|")
157
158         // Everything from index 7 on is pairs of group name on odd positions and episode list on even
159         var groupParts []string
160         if len(parts) > 7 {
161                 groupParts = parts[7:]
162         }
163
164         groupMap := make(GroupEpisodes, len(groupParts)/2)
165
166         for i := 0; i+1 < len(groupParts); i += 2 {
167                 g := <-adb.GroupByName(groupParts[i])
168                 if g == nil {
169                         continue
170                 }
171
172                 groupMap[g.GID] = misc.ParseEpisodeList(groupParts[i+1])
173         }
174
175         return &MyListAnime{
176                 UnknownState: misc.ParseEpisodeList(parts[2]),
177                 OnHDD:        misc.ParseEpisodeList(parts[3]),
178                 OnCD:         misc.ParseEpisodeList(parts[4]),
179                 Deleted:      misc.ParseEpisodeList(parts[5]),
180
181                 WatchedEpisodes: misc.ParseEpisodeList(parts[6]),
182
183                 EpisodesPerGroup: groupMap,
184         }
185 }