From: Diogo Franco (Kovensky) Date: Tue, 16 Jul 2013 16:57:49 +0000 (-0300) Subject: anidb: Support FilesByGroup/FilesByGID X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=cecf0a395b431927ca917ffe2b3dddd953a5b6ad;p=go-anidb.git anidb: Support FilesByGroup/FilesByGID Takes an Episode, for its recorded AID and episode number data. Also includes helper method FIDsByGID. --- diff --git a/fileepcache.go b/fileepcache.go new file mode 100644 index 0000000..3002959 --- /dev/null +++ b/fileepcache.go @@ -0,0 +1,151 @@ +package anidb + +import ( + "strconv" + "strings" + "time" +) + +type fidList struct { + FIDs []FID + Time time.Time +} + +func (l *fidList) Touch() { l.Time = time.Now() } +func (l *fidList) IsStale() bool { return time.Now().Sub(l.Time) > FileCacheDuration } + +// Gets the Files that the given Group has released for the given +// Episode. Convenience method that calls FilesByGID. +func (adb *AniDB) FilesByGroup(ep *Episode, g *Group) <-chan *File { + ch := make(chan *File, 1) + if ep == nil || g == nil { + ch <- nil + close(ch) + return ch + } + return adb.FilesByGID(ep, g.GID) +} + +// Gets the Files that the Group (given by its ID) has released +// for the given Episode. The returned channel may return multiple +// (or no) Files. Uses the UDP API. +// +// On API error (offline, etc), the first *File returned is nil, +// followed by cached files (which may also be nil). +func (adb *AniDB) FilesByGID(ep *Episode, gid GID) <-chan *File { + ch := make(chan *File, 10) + + fidChan := adb.FIDsByGID(ep, gid) + + go func() { + chs := []<-chan *File{} + for fid := range fidChan { + chs = append(chs, adb.FileByID(fid)) + } + for _, c := range chs { + for f := range c { + ch <- f + } + } + close(ch) + }() + return ch +} + +// Gets the FIDs that the Group (given by its ID) has released +// for the given Episode. The returned channel may return multiple +// (or no) FIDs. Uses the UDP API. +// +// On API error (offline, etc), the first *File returned is nil, +// followed by cached files (which may also be nil). +func (adb *AniDB) FIDsByGID(ep *Episode, gid GID) <-chan FID { + keys := []cacheKey{"fid", "by-ep-gid", ep.EID, gid} + + ch := make(chan FID, 10) + + if ep == nil || gid < 1 { + ch <- 0 + close(ch) + return ch + } + + ic := make(chan Cacheable, 1) + go func() { + for c := range ic { + ch <- c.(FID) + } + close(ch) + }() + if intentMap.Intent(ic, keys...) { + return ch + } + + if !cache.CheckValid(keys...) { + intentMap.Close(keys...) + return ch + } + + var fids fidList + if cache.Get(&fids, keys...) == nil { + is := intentMap.LockIntent(keys...) + go func() { + defer intentMap.Free(is, keys...) + defer is.Close() + + for _, fid := range fids.FIDs { + is.Notify(fid) + } + }() + return ch + } + + go func() { + reply := <-adb.udp.SendRecv("FILE", + paramMap{ + "aid": ep.AID, + "gid": gid, + "epno": ep.Episode.String(), + "fmask": fileFmask, + "amask": fileAmask, + }) + + is := intentMap.LockIntent(keys...) + defer intentMap.Free(is, keys...) + + switch reply.Code() { + case 220: + f := parseFileResponse(reply) + + fids.FIDs = []FID{f.FID} + cache.Set(&fids, keys...) + + cache.Set(&fidCache{FID: f.FID}, "fid", "by-ed2k", f.Ed2kHash, f.Filesize) + cache.Set(f, "fid", f.FID) + + is.NotifyClose(f.FID) + return + case 322: + parts := strings.Split(reply.Lines()[1], "|") + fids.FIDs = make([]FID, len(parts)) + for i := range parts { + id, _ := strconv.ParseInt(parts[i], 10, 32) + fids.FIDs[i] = FID(id) + } + + cache.Set(&fids, keys...) + case 320: + cache.MarkInvalid(keys...) + cache.Delete(keys...) + is.Close() + return + default: + is.Notify(FID(0)) + } + + defer is.Close() + for _, fid := range fids.FIDs { + is.Notify(fid) + } + }() + return ch +}