mirror of
https://github.com/crunchy-labs/crunchy-cli.git
synced 2026-01-21 12:12:00 -06:00
Add watchlist endpoint, add new request method & change SortType name and consts
This commit is contained in:
parent
141173d3c8
commit
c5f2b55f34
3 changed files with 199 additions and 12 deletions
|
|
@ -38,13 +38,28 @@ const (
|
||||||
MOVIELISTING = "movie_listing"
|
MOVIELISTING = "movie_listing"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SortType represents a sort type.
|
// BrowseSortType represents a sort type to sort Crunchyroll.Browse items after.
|
||||||
type SortType string
|
type BrowseSortType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
POPULARITY SortType = "popularity"
|
BROWSESORTPOPULARITY BrowseSortType = "popularity"
|
||||||
NEWLYADDED = "newly_added"
|
BROWSESORTNEWLYADDED = "newly_added"
|
||||||
ALPHABETICAL = "alphabetical"
|
BROWSESORTALPHABETICAL = "alphabetical"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WatchlistLanguageType represents a filter type to filter Crunchyroll.WatchList entries after.
|
||||||
|
type WatchlistLanguageType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
WATCHLISTLANGUAGESUBBED WatchlistLanguageType = iota + 1
|
||||||
|
WATCHLISTLANGUAGEDUBBED
|
||||||
|
)
|
||||||
|
|
||||||
|
type WatchlistContentType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
WATCHLISTCONTENTSERIES WatchlistContentType = "series"
|
||||||
|
WATCHLISTCONTENTMOVIES = "movie_listing"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Crunchyroll struct {
|
type Crunchyroll struct {
|
||||||
|
|
@ -95,7 +110,7 @@ type BrowseOptions struct {
|
||||||
Simulcast string `param:"season_tag"`
|
Simulcast string `param:"season_tag"`
|
||||||
|
|
||||||
// Sort specifies how the entries should be sorted.
|
// Sort specifies how the entries should be sorted.
|
||||||
Sort SortType `param:"sort_by"`
|
Sort BrowseSortType `param:"sort_by"`
|
||||||
|
|
||||||
// Start specifies the index from which the entries should be returned.
|
// Start specifies the index from which the entries should be returned.
|
||||||
Start uint `param:"start"`
|
Start uint `param:"start"`
|
||||||
|
|
@ -329,6 +344,10 @@ func (c *Crunchyroll) request(endpoint string, method string) (*http.Response, e
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return c.requestFull(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Crunchyroll) requestFull(req *http.Request) (*http.Response, error) {
|
||||||
req.Header.Add("Authorization", fmt.Sprintf("%s %s", c.Config.TokenType, c.Config.AccessToken))
|
req.Header.Add("Authorization", fmt.Sprintf("%s %s", c.Config.TokenType, c.Config.AccessToken))
|
||||||
|
|
||||||
return request(req, c.Client)
|
return request(req, c.Client)
|
||||||
|
|
@ -812,6 +831,68 @@ func (c *Crunchyroll) WatchHistory(page uint, size uint) (e []*HistoryEpisode, e
|
||||||
return e, nil
|
return e, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WatchlistOptions struct {
|
||||||
|
// OrderAsc specified whether the results should be order ascending or descending.
|
||||||
|
OrderAsc bool
|
||||||
|
|
||||||
|
// OnlyFavorites specifies whether only episodes which are marked as favorite should be returned.
|
||||||
|
OnlyFavorites bool
|
||||||
|
|
||||||
|
// LanguageType specifies whether returning episodes should be only subbed or dubbed.
|
||||||
|
LanguageType WatchlistLanguageType
|
||||||
|
|
||||||
|
// ContentType specified whether returning videos should only be series episodes or movies.
|
||||||
|
// But tbh all movies I've searched on crunchy were flagged as series too, so this
|
||||||
|
// parameter is kinda useless.
|
||||||
|
ContentType WatchlistContentType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Crunchyroll) Watchlist(options WatchlistOptions, limit uint) ([]*WatchlistEntry, error) {
|
||||||
|
values := url.Values{}
|
||||||
|
if options.OrderAsc {
|
||||||
|
values.Set("order", "asc")
|
||||||
|
} else {
|
||||||
|
values.Set("order", "desc")
|
||||||
|
}
|
||||||
|
if options.OnlyFavorites {
|
||||||
|
values.Set("only_favorites", "true")
|
||||||
|
}
|
||||||
|
switch options.LanguageType {
|
||||||
|
case WATCHLISTLANGUAGESUBBED:
|
||||||
|
values.Set("is_subbed", "true")
|
||||||
|
case WATCHLISTLANGUAGEDUBBED:
|
||||||
|
values.Set("is_dubbed", "true")
|
||||||
|
}
|
||||||
|
values.Set("n", strconv.Itoa(int(limit)))
|
||||||
|
values.Set("locale", string(c.Locale))
|
||||||
|
|
||||||
|
endpoint := fmt.Sprintf("https://beta.crunchyroll.com/content/v1/%s/watchlist?%s", c.Config.AccountID, values.Encode())
|
||||||
|
resp, err := c.request(endpoint, http.MethodGet)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
var jsonBody map[string]interface{}
|
||||||
|
json.NewDecoder(resp.Body).Decode(&jsonBody)
|
||||||
|
|
||||||
|
var watchlistEntries []*WatchlistEntry
|
||||||
|
if err := decodeMapToStruct(jsonBody["items"], &watchlistEntries); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range watchlistEntries {
|
||||||
|
switch entry.Panel.Type {
|
||||||
|
case WATCHLISTENTRYEPISODE:
|
||||||
|
entry.Panel.EpisodeMetadata.crunchy = c
|
||||||
|
case WATCHLISTENTRYSERIES:
|
||||||
|
entry.Panel.SeriesMetadata.crunchy = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return watchlistEntries, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Account returns information about the currently logged in crunchyroll account.
|
// Account returns information about the currently logged in crunchyroll account.
|
||||||
func (c *Crunchyroll) Account() (*Account, error) {
|
func (c *Crunchyroll) Account() (*Account, error) {
|
||||||
resp, err := c.request("https://beta.crunchyroll.com/accounts/v1/me", http.MethodGet)
|
resp, err := c.request("https://beta.crunchyroll.com/accounts/v1/me", http.MethodGet)
|
||||||
|
|
|
||||||
97
episode.go
97
episode.go
|
|
@ -1,6 +1,7 @@
|
||||||
package crunchyroll
|
package crunchyroll
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
@ -39,11 +40,14 @@ type Episode struct {
|
||||||
NextEpisodeID string `json:"next_episode_id"`
|
NextEpisodeID string `json:"next_episode_id"`
|
||||||
NextEpisodeTitle string `json:"next_episode_title"`
|
NextEpisodeTitle string `json:"next_episode_title"`
|
||||||
|
|
||||||
HDFlag bool `json:"hd_flag"`
|
HDFlag bool `json:"hd_flag"`
|
||||||
IsMature bool `json:"is_mature"`
|
MaturityRatings []string `json:"maturity_ratings"`
|
||||||
MatureBlocked bool `json:"mature_blocked"`
|
IsMature bool `json:"is_mature"`
|
||||||
|
MatureBlocked bool `json:"mature_blocked"`
|
||||||
|
|
||||||
EpisodeAirDate time.Time `json:"episode_air_date"`
|
EpisodeAirDate time.Time `json:"episode_air_date"`
|
||||||
|
FreeAvailableDate time.Time `json:"free_available_date"`
|
||||||
|
PremiumAvailableDate time.Time `json:"premium_available_date"`
|
||||||
|
|
||||||
IsSubbed bool `json:"is_subbed"`
|
IsSubbed bool `json:"is_subbed"`
|
||||||
IsDubbed bool `json:"is_dubbed"`
|
IsDubbed bool `json:"is_dubbed"`
|
||||||
|
|
@ -52,8 +56,9 @@ type Episode struct {
|
||||||
SeoDescription string `json:"seo_description"`
|
SeoDescription string `json:"seo_description"`
|
||||||
SeasonTags []string `json:"season_tags"`
|
SeasonTags []string `json:"season_tags"`
|
||||||
|
|
||||||
AvailableOffline bool `json:"available_offline"`
|
AvailableOffline bool `json:"available_offline"`
|
||||||
Slug string `json:"slug"`
|
MediaType MediaType `json:"media_type"`
|
||||||
|
Slug string `json:"slug"`
|
||||||
|
|
||||||
Images struct {
|
Images struct {
|
||||||
Thumbnail [][]struct {
|
Thumbnail [][]struct {
|
||||||
|
|
@ -87,6 +92,62 @@ type HistoryEpisode struct {
|
||||||
FullyWatched bool `json:"fully_watched"`
|
FullyWatched bool `json:"fully_watched"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WatchlistEntryType specifies which type a watchlist entry has.
|
||||||
|
type WatchlistEntryType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
WATCHLISTENTRYEPISODE = "episode"
|
||||||
|
WATCHLISTENTRYSERIES = "series"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WatchlistEntry contains information about an entry on the watchlist.
|
||||||
|
type WatchlistEntry struct {
|
||||||
|
Panel struct {
|
||||||
|
Title string `json:"title"`
|
||||||
|
PromoTitle string `json:"promo_title"`
|
||||||
|
Slug string `json:"slug"`
|
||||||
|
Playback string `json:"playback"`
|
||||||
|
PromoDescription string `json:"promo_description"`
|
||||||
|
Images struct {
|
||||||
|
Thumbnail [][]struct {
|
||||||
|
Height int `json:"height"`
|
||||||
|
Source string `json:"source"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Width int `json:"width"`
|
||||||
|
} `json:"thumbnail"`
|
||||||
|
PosterTall [][]struct {
|
||||||
|
Width int `json:"width"`
|
||||||
|
Height int `json:"height"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Source string `json:"source"`
|
||||||
|
} `json:"poster_tall"`
|
||||||
|
PosterWide [][]struct {
|
||||||
|
Width int `json:"width"`
|
||||||
|
Height int `json:"height"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Source string `json:"source"`
|
||||||
|
} `json:"poster_wide"`
|
||||||
|
} `json:"images"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
ChannelID string `json:"channel_id"`
|
||||||
|
Type WatchlistEntryType `json:"type"`
|
||||||
|
ExternalID string `json:"external_id"`
|
||||||
|
SlugTitle string `json:"slug_title"`
|
||||||
|
// not null if Type is WATCHLISTENTRYEPISODE
|
||||||
|
EpisodeMetadata *Episode `json:"episode_metadata"`
|
||||||
|
// not null if Type is WATCHLISTENTRYSERIES
|
||||||
|
SeriesMetadata *Series `json:"series_metadata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
New bool `json:"new"`
|
||||||
|
NewContent bool `json:"new_content"`
|
||||||
|
IsFavorite bool `json:"is_favorite"`
|
||||||
|
NeverWatched bool `json:"never_watched"`
|
||||||
|
CompleteStatus bool `json:"complete_status"`
|
||||||
|
Playahead uint `json:"playahead"`
|
||||||
|
}
|
||||||
|
|
||||||
// EpisodeFromID returns an episode by its api id.
|
// EpisodeFromID returns an episode by its api id.
|
||||||
func EpisodeFromID(crunchy *Crunchyroll, id string) (*Episode, error) {
|
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",
|
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",
|
||||||
|
|
@ -122,6 +183,30 @@ func EpisodeFromID(crunchy *Crunchyroll, id string) (*Episode, error) {
|
||||||
return episode, nil
|
return episode, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddToWatchlist adds the current episode to the watchlist.
|
||||||
|
// There is currently a bug, or as I like to say in context of the crunchyroll api, feature,
|
||||||
|
// that only series and not individual episode can be added to the watchlist. Even though
|
||||||
|
// I somehow got an episode to my watchlist on the crunchyroll website, it never worked with the
|
||||||
|
// api here. So this function actually adds the whole series to the watchlist.
|
||||||
|
func (e *Episode) AddToWatchlist() error {
|
||||||
|
endpoint := fmt.Sprintf("https://beta.crunchyroll.com/content/v1/watchlist/%s?locale=%s", e.crunchy.Config.AccountID, e.crunchy.Locale)
|
||||||
|
body, _ := json.Marshal(map[string]string{"content_id": e.SeriesID})
|
||||||
|
req, err := http.NewRequest(http.MethodPost, endpoint, bytes.NewBuffer(body))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
_, err = e.crunchy.requestFull(req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveFromWatchlist removes the current episode from the watchlist.
|
||||||
|
func (e *Episode) RemoveFromWatchlist() error {
|
||||||
|
endpoint := fmt.Sprintf("https://beta.crunchyroll.com/content/v1/watchlist/%s/%s?locale=%s", e.crunchy.Config.AccountID, e.SeriesID, e.crunchy.Locale)
|
||||||
|
_, err := e.crunchy.request(endpoint, http.MethodDelete)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// AudioLocale returns the audio locale of the episode.
|
// AudioLocale returns the audio locale of the episode.
|
||||||
// Every episode in a season (should) have the same audio locale,
|
// Every episode in a season (should) have the same audio locale,
|
||||||
// so if you want to get the audio locale of a season, just call
|
// so if you want to get the audio locale of a season, just call
|
||||||
|
|
|
||||||
21
video.go
21
video.go
|
|
@ -1,6 +1,7 @@
|
||||||
package crunchyroll
|
package crunchyroll
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
@ -193,6 +194,26 @@ func SeriesFromID(crunchy *Crunchyroll, id string) (*Series, error) {
|
||||||
return series, nil
|
return series, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddToWatchlist adds the current episode to the watchlist.
|
||||||
|
func (s *Series) AddToWatchlist() error {
|
||||||
|
endpoint := fmt.Sprintf("https://beta.crunchyroll.com/content/v1/watchlist/%s?locale=%s", s.crunchy.Config.AccountID, s.crunchy.Locale)
|
||||||
|
body, _ := json.Marshal(map[string]string{"content_id": s.ID})
|
||||||
|
req, err := http.NewRequest(http.MethodPost, endpoint, bytes.NewBuffer(body))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
_, err = s.crunchy.requestFull(req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveFromWatchlist removes the current episode from the watchlist.
|
||||||
|
func (s *Series) RemoveFromWatchlist() error {
|
||||||
|
endpoint := fmt.Sprintf("https://beta.crunchyroll.com/content/v1/watchlist/%s/%s?locale=%s", s.crunchy.Config.AccountID, s.ID, s.crunchy.Locale)
|
||||||
|
_, err := s.crunchy.request(endpoint, http.MethodDelete)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Seasons returns all seasons of a series.
|
// Seasons returns all seasons of a series.
|
||||||
func (s *Series) Seasons() (seasons []*Season, err error) {
|
func (s *Series) Seasons() (seasons []*Season, err error) {
|
||||||
if s.children != nil {
|
if s.children != nil {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue