]> git.lizzy.rs Git - go-anidb.git/commitdiff
anidb: Support querying for mylist of an Anime
authorDiogo Franco (Kovensky) <diogomfranco@gmail.com>
Thu, 25 Jul 2013 22:22:59 +0000 (19:22 -0300)
committerDiogo Franco (Kovensky) <diogomfranco@gmail.com>
Thu, 25 Jul 2013 22:22:59 +0000 (19:22 -0300)
It's not very informative due to AniDB-side restrictions.

misc.go
mylistanime.go [new file with mode: 0644]
mylistanimecache.go [new file with mode: 0644]

diff --git a/misc.go b/misc.go
index 8393a0fd7b489791263a3bde9b9f766b3ebc98c6..b74f1c9e02b91ea5057013415240f466db362f18 100644 (file)
--- a/misc.go
+++ b/misc.go
@@ -15,6 +15,11 @@ var (
        GroupCacheDuration   = 4 * DefaultCacheDuration // They don't change that often.
        FileCacheDuration    = 8 * DefaultCacheDuration // These change even less often.
 
+       MyListCacheDuration        = 12 * time.Hour           // When the file isn't watched
+       MyListWatchedCacheDuration = 2 * DefaultCacheDuration // When the file is watched
+
+       LIDCacheDuration = 4 * DefaultCacheDuration
+
        UIDCacheDuration = 16 * DefaultCacheDuration // Can these even be changed?
 
        // Used for anime that have already finished airing.
diff --git a/mylistanime.go b/mylistanime.go
new file mode 100644 (file)
index 0000000..3dc407b
--- /dev/null
@@ -0,0 +1,50 @@
+package anidb
+
+import (
+       "encoding/json"
+       "github.com/Kovensky/go-anidb/misc"
+       "strconv"
+       "time"
+)
+
+type MyListAnime struct {
+       AID AID
+
+       UnknownState misc.EpisodeList
+       OnHDD        misc.EpisodeList
+       OnCD         misc.EpisodeList
+       Deleted      misc.EpisodeList
+
+       WatchedEpisodes misc.EpisodeList
+
+       EpisodesPerGroup GroupEpisodes
+
+       Cached time.Time
+}
+
+type GroupEpisodes map[GID]misc.EpisodeList
+
+func (ge GroupEpisodes) MarshalJSON() ([]byte, error) {
+       generic := make(map[string]misc.EpisodeList, len(ge))
+       for k, v := range ge {
+               generic[strconv.Itoa(int(k))] = v
+       }
+       return json.Marshal(generic)
+}
+
+func (ge GroupEpisodes) UnmarshalJSON(b []byte) error {
+       var generic map[string]misc.EpisodeList
+       if err := json.Unmarshal(b, &generic); err != nil {
+               return err
+       }
+       for k, v := range generic {
+               ik, err := strconv.ParseInt(k, 10, 32)
+               if err != nil {
+                       return err
+               }
+
+               ge[GID(ik)] = v
+       }
+
+       return nil
+}
diff --git a/mylistanimecache.go b/mylistanimecache.go
new file mode 100644 (file)
index 0000000..c760bf3
--- /dev/null
@@ -0,0 +1,185 @@
+package anidb
+
+import (
+       "github.com/Kovensky/go-anidb/misc"
+       "github.com/Kovensky/go-anidb/udp"
+       "github.com/Kovensky/go-fscache"
+       "strings"
+       "time"
+)
+
+func (a *MyListAnime) setCachedTS(ts time.Time) {
+       a.Cached = ts
+}
+
+func (a *MyListAnime) IsStale() bool {
+       if a == nil {
+               return true
+       }
+
+       return time.Now().Sub(a.Cached) > MyListCacheDuration
+}
+
+var _ cacheable = &MyListAnime{}
+
+func (uid UID) MyListAnime(aid AID) *MyListAnime {
+       var a MyListAnime
+       if CacheGet(&a, "mylist-anime", uid, aid) == nil {
+               return &a
+       }
+       return nil
+}
+
+func (adb *AniDB) MyListAnime(aid AID) <-chan *MyListAnime {
+       ch := make(chan *MyListAnime, 1)
+
+       if aid < 1 {
+               ch <- nil
+               close(ch)
+               return ch
+       }
+
+       go func() {
+               user := <-adb.GetCurrentUser()
+               if user == nil || user.UID < 1 {
+                       ch <- nil
+                       close(ch)
+                       return
+               }
+               key := []fscache.CacheKey{"mylist-anime", user.UID, aid}
+
+               ic := make(chan notification, 1)
+               go func() { ch <- (<-ic).(*MyListAnime); close(ch) }()
+               if intentMap.Intent(ic, key...) {
+                       return
+               }
+
+               if !Cache.IsValid(InvalidKeyCacheDuration, key...) {
+                       intentMap.NotifyClose((*MyListAnime)(nil), key...)
+                       return
+               }
+
+               entry := user.UID.MyListAnime(aid)
+               if !entry.IsStale() {
+                       intentMap.NotifyClose(entry, key...)
+                       return
+               }
+
+               reply := <-adb.udp.SendRecv("MYLIST", paramMap{"aid": aid})
+
+               switch reply.Code() {
+               case 221:
+                       r := adb.parseMylistReply(reply) // caches
+
+                       // we have only a single file added for this anime -- construct a fake 312 struct
+                       entry = &MyListAnime{AID: aid}
+
+                       ep := <-adb.EpisodeByID(r.EID)
+                       list := misc.EpisodeToList(&ep.Episode)
+
+                       switch r.MyListState {
+                       case MyListStateUnknown:
+                               entry.UnknownState = list
+                       case MyListStateHDD:
+                               entry.OnHDD = list
+                       case MyListStateCD:
+                               entry.OnCD = list
+                       case MyListStateDeleted:
+                               entry.Deleted = list
+                       }
+
+                       if !r.DateWatched.IsZero() {
+                               entry.WatchedEpisodes = list
+                       }
+
+                       entry.EpisodesPerGroup = map[GID]misc.EpisodeList{
+                               r.GID: list,
+                       }
+               case 312:
+                       entry = adb.parseMylistAnime(reply)
+                       entry.AID = aid
+               case 321:
+                       Cache.SetInvalid(key...)
+               }
+
+               CacheSet(entry, key...)
+               intentMap.NotifyClose(entry, key...)
+       }()
+       return ch
+}
+
+func (adb *AniDB) UserMyListAnime(uid UID, aid AID) <-chan *MyListAnime {
+       key := []fscache.CacheKey{"mylist-anime", uid, aid}
+       ch := make(chan *MyListAnime, 1)
+
+       if uid < 1 || aid < 1 {
+               ch <- nil
+               close(ch)
+               return ch
+       }
+
+       ic := make(chan notification, 1)
+       go func() { ch <- (<-ic).(*MyListAnime); close(ch) }()
+       if intentMap.Intent(ic, key...) {
+               return ch
+       }
+
+       if !Cache.IsValid(InvalidKeyCacheDuration, key...) {
+               intentMap.NotifyClose((*MyListAnime)(nil), key...)
+               return ch
+       }
+
+       entry := uid.MyListAnime(aid)
+       if !entry.IsStale() {
+               intentMap.NotifyClose(entry, key...)
+               return ch
+       }
+
+       go func() {
+               user := <-adb.GetCurrentUser()
+
+               if user.UID != uid { // we can't query other users' lists from API
+                       intentMap.NotifyClose(entry, key...)
+                       return
+               }
+
+               intentMap.NotifyClose(<-adb.MyListAnime(aid), key...)
+       }()
+       return ch
+}
+
+func (adb *AniDB) parseMylistAnime(reply udpapi.APIReply) *MyListAnime {
+       if reply.Code() != 312 {
+               return nil
+       }
+
+       parts := strings.Split(reply.Lines()[1], "|")
+
+       // Everything from index 7 on is pairs of group name on odd positions and episode list on even
+       var groupParts []string
+       if len(parts) > 7 {
+               groupParts = parts[7:]
+       }
+
+       groupMap := make(GroupEpisodes, len(groupParts)/2)
+
+       for i := 0; i+1 < len(groupParts); i += 2 {
+               g := <-adb.GroupByName(groupParts[i])
+               if g == nil {
+                       continue
+               }
+
+               groupMap[g.GID] = misc.ParseEpisodeList(groupParts[i+1])
+       }
+
+       return &MyListAnime{
+               UnknownState: misc.ParseEpisodeList(parts[2]),
+               OnHDD:        misc.ParseEpisodeList(parts[3]),
+               OnCD:         misc.ParseEpisodeList(parts[4]),
+               Deleted:      misc.ParseEpisodeList(parts[5]),
+
+               WatchedEpisodes: misc.ParseEpisodeList(parts[6]),
+
+               EpisodesPerGroup: groupMap,
+       }
+}