]> git.lizzy.rs Git - go-anidb.git/commitdiff
anidb: Implement MYLIST for single files
authorDiogo Franco (Kovensky) <diogomfranco@gmail.com>
Wed, 17 Jul 2013 21:04:59 +0000 (18:04 -0300)
committerDiogo Franco (Kovensky) <diogomfranco@gmail.com>
Wed, 17 Jul 2013 21:04:59 +0000 (18:04 -0300)
mylist.go
mylistcache.go [new file with mode: 0644]

index 8f7288cd3d54ebddea3e8a5a6e15a39f047144e9..de3e187b834cd6f7711c97fe4c00f4bb6e3f7a32 100644 (file)
--- a/mylist.go
+++ b/mylist.go
@@ -1,3 +1,55 @@
 package anidb
 
+import (
+       "time"
+)
+
 type LID int64
+
+type MyListState int
+
+const (
+       MyListStateUnknown = MyListState(iota)
+       MyListStateHDD
+       MyListStateCD
+       MyListStateDeleted
+)
+
+type FileState int
+
+const (
+       FileStateOriginal = FileState(iota)
+       FileStateCorrupted
+       FileStateEdited
+
+       FileStateOther = 100
+)
+const (
+       FileStateSelfRip = FileState(10 + iota)
+       FileStateDVD
+       FileStateVHS
+       FileStateTV
+       FileStateTheaters
+       FileStateStreamed
+)
+
+type MyListEntry struct {
+       LID LID
+
+       FID FID
+       EID EID
+       AID AID
+       GID GID
+
+       DateAdded   time.Time
+       DateWatched time.Time
+
+       State       FileState
+       MyListState MyListState
+
+       Storage string
+       Source  string
+       Other   string
+
+       Cached time.Time
+}
diff --git a/mylistcache.go b/mylistcache.go
new file mode 100644 (file)
index 0000000..d7ad4eb
--- /dev/null
@@ -0,0 +1,217 @@
+package anidb
+
+import (
+       "github.com/Kovensky/go-anidb/udp"
+       "github.com/Kovensky/go-fscache"
+       "strconv"
+       "strings"
+       "time"
+)
+
+func (e *MyListEntry) setCachedTS(ts time.Time) {
+       e.Cached = ts
+}
+
+func (e *MyListEntry) IsStale() bool {
+       if e == nil {
+               return true
+       }
+
+       max := MyListCacheDuration
+       if !e.DateWatched.IsZero() {
+               max = MyListWatchedCacheDuration
+       }
+       return time.Now().Sub(e.Cached) > max
+}
+
+var _ cacheable = &MyListEntry{}
+
+func (lid LID) MyListEntry() *MyListEntry {
+       var e MyListEntry
+       if CacheGet(&e, "lid", lid) == nil {
+               return &e
+       }
+       return nil
+}
+
+func (adb *AniDB) MyListByFile(f *File) <-chan *MyListEntry {
+       ch := make(chan *MyListEntry, 1)
+
+       if f == nil {
+               ch <- nil
+               close(ch)
+               return ch
+       }
+
+       go func() {
+               user := <-adb.GetCurrentUser()
+
+               var entry *MyListEntry
+
+               if lid := f.LID[user.UID]; lid != 0 {
+                       entry = <-adb.MyListByLID(lid)
+               }
+               if entry == nil {
+                       entry = <-adb.MyListByFID(f.FID)
+               }
+               ch <- entry
+               close(ch)
+       }()
+       return ch
+}
+
+func (adb *AniDB) MyListByLID(lid LID) <-chan *MyListEntry {
+       key := []fscache.CacheKey{"mylist", lid}
+       ch := make(chan *MyListEntry, 1)
+
+       if lid < 1 {
+               ch <- nil
+               close(ch)
+               return ch
+       }
+
+       ic := make(chan notification, 1)
+       go func() { ch <- (<-ic).(*MyListEntry); close(ch) }()
+       if intentMap.Intent(ic, key...) {
+               return ch
+       }
+
+       if !Cache.IsValid(InvalidKeyCacheDuration, key...) {
+               intentMap.NotifyClose((*MyListEntry)(nil), key...)
+               return ch
+       }
+
+       entry := lid.MyListEntry()
+       if !entry.IsStale() {
+               intentMap.NotifyClose(entry, key...)
+               return ch
+       }
+
+       go func() {
+               reply := <-adb.udp.SendRecv("MYLIST", paramMap{"lid": lid})
+
+               switch reply.Code() {
+               case 221:
+                       entry = adb.parseMylistReply(reply) // caches
+               case 312:
+                       panic("Multiple MYLIST entries when querying for single LID")
+               case 321:
+                       Cache.SetInvalid(key...)
+               }
+
+               intentMap.NotifyClose(entry, key...)
+       }()
+       return ch
+}
+
+func (adb *AniDB) MyListByFID(fid FID) <-chan *MyListEntry {
+       ch := make(chan *MyListEntry, 1)
+
+       if fid < 1 {
+               ch <- nil
+               close(ch)
+               return ch
+       }
+
+       // This is an odd one: we lack enough data at first to create the cache key
+       go func() {
+               user := <-adb.GetCurrentUser()
+               if user == nil || user.UID < 1 {
+                       ch <- nil
+                       close(ch)
+                       return
+               }
+
+               key := []fscache.CacheKey{"mylist", "by-fid", fid, user.UID}
+
+               ic := make(chan notification, 1)
+               go func() { ch <- (<-ic).(*MyListEntry); close(ch) }()
+               if intentMap.Intent(ic, key...) {
+                       return
+               }
+
+               if !Cache.IsValid(InvalidKeyCacheDuration, key...) {
+                       intentMap.NotifyClose((*MyListEntry)(nil), key...)
+                       return
+               }
+
+               lid := LID(0)
+               switch ts, err := Cache.Get(&lid, key...); {
+               case err == nil && time.Now().Sub(ts) < LIDCacheDuration:
+                       intentMap.NotifyClose(<-adb.MyListByLID(lid), key...)
+                       return
+               }
+
+               reply := <-adb.udp.SendRecv("MYLIST", paramMap{"fid": fid})
+
+               var entry *MyListEntry
+
+               switch reply.Code() {
+               case 221:
+                       entry = adb.parseMylistReply(reply) // caches
+               case 312:
+                       panic("Multiple MYLIST entries when querying for single FID")
+               case 321:
+                       Cache.SetInvalid(key...)
+               }
+
+               intentMap.NotifyClose(entry, key...)
+       }()
+       return ch
+}
+
+func (adb *AniDB) parseMylistReply(reply udpapi.APIReply) *MyListEntry {
+       if reply.Error() != nil {
+               return nil
+       }
+
+       parts := strings.Split(reply.Lines()[1], "|")
+       ints := make([]int64, len(parts))
+       for i := range parts {
+               ints[i], _ = strconv.ParseInt(parts[i], 10, 64)
+       }
+
+       da := time.Unix(ints[5], 0)
+       if ints[5] == 0 {
+               da = time.Time{}
+       }
+       dw := time.Unix(ints[7], 0)
+       if ints[7] == 0 {
+               dw = time.Time{}
+       }
+
+       e := &MyListEntry{
+               LID: LID(ints[0]),
+
+               FID: FID(ints[1]),
+               EID: EID(ints[2]),
+               AID: AID(ints[3]),
+               GID: GID(ints[4]),
+
+               DateAdded:   da,
+               DateWatched: dw,
+
+               State:       FileState(ints[11]),
+               MyListState: MyListState(ints[6]),
+
+               Storage: parts[8],
+               Source:  parts[9],
+               Other:   parts[10],
+       }
+
+       user := <-adb.GetCurrentUser()
+
+       if user != nil {
+               if f := e.FID.File(); f != nil {
+                       f.LID[user.UID] = e.LID
+                       cacheFile(f)
+               }
+
+               CacheSet(e, "mylist", "by-fid", e.FID, user.UID)
+       }
+
+       // TODO: Add mylist info to Anime, also update there
+       CacheSet(e, "mylist", e.LID)
+
+       return e
+}