diff --git a/arn/AnimeEpisodes.go b/arn/AnimeEpisodes.go new file mode 100644 index 00000000..08563f4d --- /dev/null +++ b/arn/AnimeEpisodes.go @@ -0,0 +1,157 @@ +package arn + +import ( + "sort" + "strconv" + "strings" + "sync" + + "github.com/aerogo/nano" +) + +// AnimeEpisodes is a list of episodes for an anime. +type AnimeEpisodes struct { + AnimeID AnimeID `json:"animeId" mainID:"true"` + Items []*Episode `json:"items" editable:"true"` + + sync.Mutex +} + +// Link returns the link for that object. +func (episodes *AnimeEpisodes) Link() string { + return "/anime/" + episodes.AnimeID + "/episodes" +} + +// Sort sorts the episodes by episode number. +func (episodes *AnimeEpisodes) Sort() { + episodes.Lock() + defer episodes.Unlock() + + sort.Slice(episodes.Items, func(i, j int) bool { + return episodes.Items[i].Number < episodes.Items[j].Number + }) +} + +// Find finds the given episode number. +func (episodes *AnimeEpisodes) Find(episodeNumber int) (*Episode, int) { + episodes.Lock() + defer episodes.Unlock() + + for index, episode := range episodes.Items { + if episode.Number == episodeNumber { + return episode, index + } + } + + return nil, -1 +} + +// Merge combines the data of both episode slices to one. +func (episodes *AnimeEpisodes) Merge(b []*Episode) { + if b == nil { + return + } + + episodes.Lock() + defer episodes.Unlock() + + for index, episode := range b { + if index >= len(episodes.Items) { + episodes.Items = append(episodes.Items, episode) + } else { + episodes.Items[index].Merge(episode) + } + } +} + +// Last returns the last n items. +func (episodes *AnimeEpisodes) Last(count int) []*Episode { + return episodes.Items[len(episodes.Items)-count:] +} + +// AvailableCount counts the number of available episodes. +func (episodes *AnimeEpisodes) AvailableCount() int { + episodes.Lock() + defer episodes.Unlock() + + available := 0 + + for _, episode := range episodes.Items { + if len(episode.Links) > 0 { + available++ + } + } + + return available +} + +// Anime returns the anime the episodes refer to. +func (episodes *AnimeEpisodes) Anime() *Anime { + anime, _ := GetAnime(episodes.AnimeID) + return anime +} + +// String implements the default string serialization. +func (episodes *AnimeEpisodes) String() string { + return episodes.Anime().String() +} + +// GetID returns the anime ID. +func (episodes *AnimeEpisodes) GetID() string { + return episodes.AnimeID +} + +// TypeName returns the type name. +func (episodes *AnimeEpisodes) TypeName() string { + return "AnimeEpisodes" +} + +// Self returns the object itself. +func (episodes *AnimeEpisodes) Self() Loggable { + return episodes +} + +// ListString returns a text representation of the anime episodes. +func (episodes *AnimeEpisodes) ListString() string { + episodes.Lock() + defer episodes.Unlock() + + b := strings.Builder{} + + for _, episode := range episodes.Items { + b.WriteString(strconv.Itoa(episode.Number)) + b.WriteString(" | ") + b.WriteString(episode.Title.Japanese) + b.WriteString(" | ") + b.WriteString(episode.AiringDate.StartDateHuman()) + b.WriteByte('\n') + } + + return strings.TrimRight(b.String(), "\n") +} + +// StreamAnimeEpisodes returns a stream of all anime episodes. +func StreamAnimeEpisodes() <-chan *AnimeEpisodes { + channel := make(chan *AnimeEpisodes, nano.ChannelBufferSize) + + go func() { + for obj := range DB.All("AnimeEpisodes") { + channel <- obj.(*AnimeEpisodes) + } + + close(channel) + }() + + return channel +} + +// GetAnimeEpisodes ... +func GetAnimeEpisodes(id string) (*AnimeEpisodes, error) { + obj, err := DB.Get("AnimeEpisodes", id) + + if err != nil { + return nil, err + } + + return obj.(*AnimeEpisodes), nil +} diff --git a/arn/Database.go b/arn/Database.go index ca62fe7e..c9bbca0e 100644 --- a/arn/Database.go +++ b/arn/Database.go @@ -20,6 +20,7 @@ var DB = Node.Namespace("arn").RegisterTypes( (*Analytics)(nil), (*Anime)(nil), (*AnimeCharacters)(nil), + (*AnimeEpisodes)(nil), (*AnimeRelations)(nil), (*AnimeList)(nil), (*Character)(nil),