4 "github.com/EliasFleckenstein03/go-anidb/udp"
5 "github.com/EliasFleckenstein03/go-fscache"
11 func (e *MyListEntry) setCachedTS(ts time.Time) {
15 func (e *MyListEntry) IsStale() bool {
20 max := MyListCacheDuration
21 if !e.DateWatched.IsZero() {
22 max = MyListWatchedCacheDuration
24 return time.Now().Sub(e.Cached) > max
27 var _ cacheable = &MyListEntry{}
29 func (uid UID) MyList(fid FID) *MyListEntry {
30 if f := fid.File(); f == nil {
32 } else if lid := f.LID[uid]; lid < 1 {
35 return f.LID[uid].MyListEntry()
39 func (lid LID) MyListEntry() *MyListEntry {
41 if CacheGet(&e, "mylist", lid) == nil {
47 func (adb *AniDB) MyListByFile(f *File) <-chan *MyListEntry {
48 ch := make(chan *MyListEntry, 1)
57 user := <-adb.GetCurrentUser()
59 var entry *MyListEntry
61 if lid := f.LID[user.UID]; lid != 0 {
62 entry = <-adb.MyListByLID(lid)
65 entry = <-adb.MyListByFID(f.FID)
73 func (adb *AniDB) MyListByLID(lid LID) <-chan *MyListEntry {
74 key := []fscache.CacheKey{"mylist", lid}
75 ch := make(chan *MyListEntry, 1)
83 ic := make(chan notification, 1)
84 go func() { ch <- (<-ic).(*MyListEntry); close(ch) }()
85 if intentMap.Intent(ic, key...) {
89 if !Cache.IsValid(InvalidKeyCacheDuration, key...) {
90 intentMap.NotifyClose((*MyListEntry)(nil), key...)
94 entry := lid.MyListEntry()
96 intentMap.NotifyClose(entry, key...)
101 reply := <-adb.udp.SendRecv("MYLIST", paramMap{"lid": lid})
103 switch reply.Code() {
105 entry = adb.parseMylistReply(reply) // caches
107 panic("Multiple MYLIST entries when querying for single LID")
109 Cache.SetInvalid(key...)
112 intentMap.NotifyClose(entry, key...)
117 func (adb *AniDB) MyListByFID(fid FID) <-chan *MyListEntry {
118 ch := make(chan *MyListEntry, 1)
126 // This is an odd one: we lack enough data at first to create the cache key
128 user := <-adb.GetCurrentUser()
129 if user == nil || user.UID < 1 {
135 key := []fscache.CacheKey{"mylist", "by-fid", fid, user.UID}
137 ic := make(chan notification, 1)
138 go func() { ch <- (<-ic).(*MyListEntry); close(ch) }()
139 if intentMap.Intent(ic, key...) {
143 if !Cache.IsValid(InvalidKeyCacheDuration, key...) {
144 intentMap.NotifyClose((*MyListEntry)(nil), key...)
149 switch ts, err := Cache.Get(&lid, key...); {
150 case err == nil && time.Now().Sub(ts) < LIDCacheDuration:
151 intentMap.NotifyClose(<-adb.MyListByLID(lid), key...)
155 reply := <-adb.udp.SendRecv("MYLIST", paramMap{"fid": fid})
157 var entry *MyListEntry
159 switch reply.Code() {
161 entry = adb.parseMylistReply(reply) // caches
163 panic("Multiple MYLIST entries when querying for single FID")
165 Cache.SetInvalid(key...)
168 intentMap.NotifyClose(entry, key...)
173 func (adb *AniDB) parseMylistReply(reply udpapi.APIReply) *MyListEntry {
174 // 221: MYLIST ok, 310: MYLISTADD conflict (same return format as 221)
175 if reply.Code() != 221 && reply.Code() != 310 {
179 parts := strings.Split(reply.Lines()[1], "|")
180 ints := make([]int64, len(parts))
181 for i := range parts {
182 ints[i], _ = strconv.ParseInt(parts[i], 10, 64)
185 da := time.Unix(ints[5], 0)
189 dw := time.Unix(ints[7], 0)
205 State: FileState(ints[11]),
206 MyListState: MyListState(ints[6]),
213 user := <-adb.GetCurrentUser()
216 if f := e.FID.File(); f != nil {
217 f.LID[user.UID] = e.LID
218 Cache.Set(f, "fid", f.FID)
219 Cache.Chtime(f.Cached, "fid", f.FID)
222 mla := <-adb.MyListAnime(f.AID)
224 key := []fscache.CacheKey{"mylist-anime", user.UID, f.AID}
226 intentMap.Intent(nil, key...)
232 if mla.Cached.Before(now) {
233 el := mla.EpisodesWithState[e.MyListState]
234 el.Add(f.EpisodeNumber)
235 mla.EpisodesWithState[e.MyListState] = el
237 if e.DateWatched.IsZero() {
238 mla.WatchedEpisodes.Sub(f.EpisodeNumber)
240 mla.WatchedEpisodes.Add(f.EpisodeNumber)
243 eg := mla.EpisodesPerGroup[f.GID]
244 eg.Add(f.EpisodeNumber)
245 mla.EpisodesPerGroup[f.GID] = eg
247 if mla.Cached.IsZero() {
248 // as attractive as such an ancient mtime would be,
249 // few filesystems can represent it; just make it old enough
250 mla.Cached = time.Unix(0, 0)
253 Cache.Set(mla, key...)
254 Cache.Chtime(mla.Cached, key...)
257 // this unfortunately races if Intent returns true:
258 // only the first NotifyClose call actually notifies
259 go intentMap.NotifyClose(mla, key...)
262 CacheSet(e, "mylist", "by-fid", e.FID, user.UID)
265 CacheSet(e, "mylist", e.LID)