]> git.lizzy.rs Git - go-anidb.git/blob - fileepcache.go
anidb: Delete params s and tag from retries
[go-anidb.git] / fileepcache.go
1 package anidb
2
3 import (
4         "strconv"
5         "strings"
6         "time"
7 )
8
9 type fidList struct {
10         FIDs []FID
11         Time time.Time
12 }
13
14 func (l *fidList) Touch()        { l.Time = time.Now() }
15 func (l *fidList) IsStale() bool { return time.Now().Sub(l.Time) > FileCacheDuration }
16
17 // Gets the Files that the given Group has released for the given
18 // Episode. Convenience method that calls FilesByGID.
19 func (adb *AniDB) FilesByGroup(ep *Episode, g *Group) <-chan *File {
20         ch := make(chan *File, 1)
21         if ep == nil || g == nil {
22                 ch <- nil
23                 close(ch)
24                 return ch
25         }
26         return adb.FilesByGID(ep, g.GID)
27 }
28
29 // Gets the Files that the Group (given by its ID) has released
30 // for the given Episode. The returned channel may return multiple
31 // (or no) Files. Uses the UDP API.
32 //
33 // On API error (offline, etc), the first *File returned is nil,
34 // followed by cached files (which may also be nil).
35 func (adb *AniDB) FilesByGID(ep *Episode, gid GID) <-chan *File {
36         ch := make(chan *File, 10)
37
38         fidChan := adb.FIDsByGID(ep, gid)
39
40         go func() {
41                 chs := []<-chan *File{}
42                 for fid := range fidChan {
43                         chs = append(chs, adb.FileByID(fid))
44                 }
45                 for _, c := range chs {
46                         for f := range c {
47                                 ch <- f
48                         }
49                 }
50                 close(ch)
51         }()
52         return ch
53 }
54
55 // Gets the FIDs that the Group (given by its ID) has released
56 // for the given Episode. The returned channel may return multiple
57 // (or no) FIDs. Uses the UDP API.
58 //
59 // On API error (offline, etc), the first *File returned is nil,
60 // followed by cached files (which may also be nil).
61 func (adb *AniDB) FIDsByGID(ep *Episode, gid GID) <-chan FID {
62         keys := []cacheKey{"fid", "by-ep-gid", ep.EID, gid}
63
64         ch := make(chan FID, 10)
65
66         if ep == nil || gid < 1 {
67                 ch <- 0
68                 close(ch)
69                 return ch
70         }
71
72         ic := make(chan Cacheable, 1)
73         go func() {
74                 for c := range ic {
75                         ch <- c.(FID)
76                 }
77                 close(ch)
78         }()
79         if intentMap.Intent(ic, keys...) {
80                 return ch
81         }
82
83         if !cache.CheckValid(keys...) {
84                 intentMap.Close(keys...)
85                 return ch
86         }
87
88         var fids fidList
89         if cache.Get(&fids, keys...) == nil {
90                 is := intentMap.LockIntent(keys...)
91                 go func() {
92                         defer intentMap.Free(is, keys...)
93                         defer is.Close()
94
95                         for _, fid := range fids.FIDs {
96                                 is.Notify(fid)
97                         }
98                 }()
99                 return ch
100         }
101
102         go func() {
103                 reply := <-adb.udp.SendRecv("FILE",
104                         paramMap{
105                                 "aid":   ep.AID,
106                                 "gid":   gid,
107                                 "epno":  ep.Episode.String(),
108                                 "fmask": fileFmask,
109                                 "amask": fileAmask,
110                         })
111
112                 is := intentMap.LockIntent(keys...)
113                 defer intentMap.Free(is, keys...)
114
115                 switch reply.Code() {
116                 case 220:
117                         f := adb.parseFileResponse(reply, true)
118
119                         fids.FIDs = []FID{f.FID}
120                         cache.Set(&fids, keys...)
121
122                         cache.Set(&fidCache{FID: f.FID}, "fid", "by-ed2k", f.Ed2kHash, f.Filesize)
123                         cache.Set(f, "fid", f.FID)
124
125                         is.NotifyClose(f.FID)
126                         return
127                 case 322:
128                         parts := strings.Split(reply.Lines()[1], "|")
129                         fids.FIDs = make([]FID, len(parts))
130                         for i := range parts {
131                                 id, _ := strconv.ParseInt(parts[i], 10, 32)
132                                 fids.FIDs[i] = FID(id)
133                         }
134
135                         cache.Set(&fids, keys...)
136                 case 320:
137                         cache.MarkInvalid(keys...)
138                         cache.Delete(keys...)
139                         is.Close()
140                         return
141                 default:
142                         is.Notify(FID(0))
143                 }
144
145                 defer is.Close()
146                 for _, fid := range fids.FIDs {
147                         is.Notify(fid)
148                 }
149         }()
150         return ch
151 }