diff --git a/crunchyroll.go b/crunchyroll.go index a81f373..aefce1a 100644 --- a/crunchyroll.go +++ b/crunchyroll.go @@ -591,7 +591,7 @@ func (c *Crunchyroll) Simulcasts() (s []*Simulcast, err error) { } // News returns the top and latest news from crunchyroll for the current locale within the given limits. -func (c *Crunchyroll) News(topLimit uint, latestLimit uint) (t []*TopNews, l []*LatestNews, err error) { +func (c *Crunchyroll) News(topLimit uint, latestLimit uint) (t []*News, l []*News, err error) { newsFeedEndpoint := fmt.Sprintf("https://beta.crunchyroll.com/content/v1/news_feed?top_news_n=%d&latest_news_n=%d&locale=%s", topLimit, latestLimit, c.Locale) resp, err := c.request(newsFeedEndpoint) @@ -607,7 +607,7 @@ func (c *Crunchyroll) News(topLimit uint, latestLimit uint) (t []*TopNews, l []* topNews := jsonBody["top_news"].(map[string]interface{}) for _, item := range topNews["items"].([]interface{}) { - topNews := &TopNews{} + topNews := &News{} if err := decodeMapToStruct(item, topNews); err != nil { return nil, nil, err } @@ -617,7 +617,7 @@ func (c *Crunchyroll) News(topLimit uint, latestLimit uint) (t []*TopNews, l []* latestNews := jsonBody["latest_news"].(map[string]interface{}) for _, item := range latestNews["items"].([]interface{}) { - latestNews := &LatestNews{} + latestNews := &News{} if err := decodeMapToStruct(item, latestNews); err != nil { return nil, nil, err } @@ -672,7 +672,7 @@ func (c *Crunchyroll) Recommendations(limit uint) (s []*Series, m []*Movie, err return s, m, nil } -// UpNext returns the next episodes that you can continue watching based on your account within the given limit. +// UpNext returns the episodes that are up next based on your account within the given limit. func (c *Crunchyroll) UpNext(limit uint) (e []*Episode, err error) { upNextAccountEndpoint := fmt.Sprintf("https://beta-api.crunchyroll.com/content/v1/%s/up_next_account?n=%d&locale=%s", c.Config.AccountID, limit, c.Locale) @@ -703,7 +703,7 @@ func (c *Crunchyroll) UpNext(limit uint) (e []*Episode, err error) { return e, nil } -// SimilarTo returns similar series and movies to the one specified by id within the given limits. +// SimilarTo returns similar series and movies according to crunchyroll to the one specified by id within the given limits. func (c *Crunchyroll) SimilarTo(id string, limit uint) (s []*Series, m []*Movie, err error) { similarToEndpoint := fmt.Sprintf("https://beta-api.crunchyroll.com/content/v1/%s/similar_to?guid=%s&n=%d&locale=%s", c.Config.AccountID, id, limit, c.Locale) @@ -746,3 +746,41 @@ func (c *Crunchyroll) SimilarTo(id string, limit uint) (s []*Series, m []*Movie, return s, m, nil } + +// WatchHistory returns the history of watched episodes based on your account from the given page with the given size. +func (c *Crunchyroll) WatchHistory(page uint, size uint) (e []*HistoryEpisode, err error) { + watchHistoryEndpoint := fmt.Sprintf("https://beta-api.crunchyroll.com/content/v1/watch-history/%s?page=%d&page_size=%d&locale=%s", + c.Config.AccountID, page, size, c.Locale) + resp, err := c.request(watchHistoryEndpoint) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + var jsonBody map[string]interface{} + if err = json.NewDecoder(resp.Body).Decode(&jsonBody); err != nil { + return nil, fmt.Errorf("failed to parse 'watch-history' response: %w", err) + } + + for _, item := range jsonBody["items"].([]interface{}) { + panel := item.(map[string]interface{})["panel"] + + episode := &Episode{ + crunchy: c, + } + if err := decodeMapToStruct(panel, episode); err != nil { + return nil, err + } + + historyEpisode := &HistoryEpisode{ + Episode: episode, + } + if err := decodeMapToStruct(item, historyEpisode); err != nil { + return nil, err + } + + e = append(e, historyEpisode) + } + + return e, nil +} diff --git a/episode.go b/episode.go index a25844c..07f6705 100644 --- a/episode.go +++ b/episode.go @@ -75,6 +75,18 @@ type Episode struct { StreamID string } +// HistoryEpisode contains additional information about an episode if the account has watched or started to watch the episode. +type HistoryEpisode struct { + *Episode + + ID string `json:"id"` + DatePlayed string `json:"date_played"` + ParentID string `json:"parent_id"` + ParentType MediaType `json:"parent_type"` + Playhead uint `json:"playhead"` + FullyWatched bool `json:"fully_watched"` +} + // EpisodeFromID returns an episode by its api id. func EpisodeFromID(crunchy *Crunchyroll, id string) (*Episode, error) { resp, err := crunchy.request(fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/%s/%s/episodes/%s?locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s", diff --git a/news.go b/news.go index 3985cb4..d90dd65 100644 --- a/news.go +++ b/news.go @@ -9,11 +9,3 @@ type News struct { PublishDate string `json:"publish_date"` Description string `json:"description"` } - -type TopNews struct { - News -} - -type LatestNews struct { - News -}