From 62735cf07cb8740db61fa886ddb02fb82e766952 Mon Sep 17 00:00:00 2001 From: bytedream Date: Mon, 16 May 2022 19:28:05 +0200 Subject: [PATCH 01/10] Change request url for some request & regex --- crunchyroll.go | 17 ++++++++--------- episode.go | 12 ++++-------- movie_listing.go | 18 ++++++------------ season.go | 16 +++++++--------- stream.go | 8 +++----- video.go | 24 ++++++++---------------- 6 files changed, 36 insertions(+), 59 deletions(-) diff --git a/crunchyroll.go b/crunchyroll.go index 6b1854f..60b4320 100644 --- a/crunchyroll.go +++ b/crunchyroll.go @@ -11,6 +11,7 @@ import ( "net/url" "regexp" "strconv" + "strings" ) // LOCALE represents a locale / language. @@ -33,7 +34,7 @@ const ( type Crunchyroll struct { // Client is the http.Client to perform all requests over. Client *http.Client - // Context can be used to stop requests with Client and is context.Background by default. + // Context can be used to stop requests with Client. Context context.Context // Locale specifies in which language all results should be returned / requested. Locale LOCALE @@ -45,9 +46,10 @@ type Crunchyroll struct { TokenType string AccessToken string + Bucket string + CountryCode string Premium bool - Channel string Policy string Signature string KeyPairID string @@ -141,13 +143,7 @@ func LoginWithSessionID(sessionID string, locale LOCALE, client *http.Client) (* if user == nil { return nil, errors.New("invalid session id, user is not logged in") } - if user.(map[string]interface{})["premium"] == "" { - crunchy.Config.Premium = false - crunchy.Config.Channel = "-" - } else { - crunchy.Config.Premium = true - crunchy.Config.Channel = "crunchyroll" - } + crunchy.Config.Premium = user.(map[string]interface{})["premium"] != "" var etpRt string for _, cookie := range resp.Cookies() { @@ -200,6 +196,9 @@ func LoginWithSessionID(sessionID string, locale LOCALE, client *http.Client) (* } cms := jsonBody["cms"].(map[string]interface{}) + // / is trimmed so that urls which require it must be in .../{bucket}/... like format. + // this just looks cleaner + crunchy.Config.Bucket = strings.TrimPrefix(cms["bucket"].(string), "/") crunchy.Config.Policy = cms["policy"].(string) crunchy.Config.Signature = cms["signature"].(string) crunchy.Config.KeyPairID = cms["key_pair_id"].(string) diff --git a/episode.go b/episode.go index a25844c..3c9d38f 100644 --- a/episode.go +++ b/episode.go @@ -77,10 +77,8 @@ type Episode struct { // 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", - crunchy.Config.CountryCode, - crunchy.Config.MaturityRating, - crunchy.Config.Channel, + resp, err := crunchy.request(fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/episodes/%s?locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s", + crunchy.Config.Bucket, id, crunchy.Locale, crunchy.Config.Signature, @@ -194,10 +192,8 @@ func (e *Episode) Streams() ([]*Stream, error) { return e.children, nil } - streams, err := fromVideoStreams(e.crunchy, fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/%s/%s/videos/%s/streams?locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s", - e.crunchy.Config.CountryCode, - e.crunchy.Config.MaturityRating, - e.crunchy.Config.Channel, + streams, err := fromVideoStreams(e.crunchy, fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/videos/%s/streams?locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s", + e.crunchy.Config.Bucket, e.StreamID, e.crunchy.Locale, e.crunchy.Config.Signature, diff --git a/movie_listing.go b/movie_listing.go index 63d7fab..80b7b46 100644 --- a/movie_listing.go +++ b/movie_listing.go @@ -40,10 +40,8 @@ type MovieListing struct { // MovieListingFromID returns a movie listing by its api id. func MovieListingFromID(crunchy *Crunchyroll, id string) (*MovieListing, error) { - resp, err := crunchy.request(fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/%s/%s/movie_listing/%s&locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s", - crunchy.Config.CountryCode, - crunchy.Config.MaturityRating, - crunchy.Config.Channel, + resp, err := crunchy.request(fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/movie_listing/%s&locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s", + crunchy.Config.Bucket, id, crunchy.Locale, crunchy.Config.Signature, @@ -69,10 +67,8 @@ func MovieListingFromID(crunchy *Crunchyroll, id string) (*MovieListing, error) // AudioLocale is same as Episode.AudioLocale. func (ml *MovieListing) AudioLocale() (LOCALE, error) { - resp, err := ml.crunchy.request(fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/%s/%s/videos/%s/streams?locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s", - ml.crunchy.Config.CountryCode, - ml.crunchy.Config.MaturityRating, - ml.crunchy.Config.Channel, + resp, err := ml.crunchy.request(fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/videos/%s/streams?locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s", + ml.crunchy.Config.Bucket, ml.ID, ml.crunchy.Locale, ml.crunchy.Config.Signature, @@ -90,10 +86,8 @@ func (ml *MovieListing) AudioLocale() (LOCALE, error) { // Streams returns all streams which are available for the movie listing. func (ml *MovieListing) Streams() ([]*Stream, error) { - return fromVideoStreams(ml.crunchy, fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/%s/%s/videos/%s/streams?locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s", - ml.crunchy.Config.CountryCode, - ml.crunchy.Config.MaturityRating, - ml.crunchy.Config.Channel, + return fromVideoStreams(ml.crunchy, fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/videos/%s/streams?locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s", + ml.crunchy.Config.Bucket, ml.ID, ml.crunchy.Locale, ml.crunchy.Config.Signature, diff --git a/season.go b/season.go index 825a816..b83d214 100644 --- a/season.go +++ b/season.go @@ -44,10 +44,8 @@ type Season struct { // SeasonFromID returns a season by its api id. func SeasonFromID(crunchy *Crunchyroll, id string) (*Season, error) { - resp, err := crunchy.Client.Get(fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/%s/%s/seasons/%s?locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s", - crunchy.Config.CountryCode, - crunchy.Config.MaturityRating, - crunchy.Config.Channel, + resp, err := crunchy.Client.Get(fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/seasons?series_id=%s&locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s", + crunchy.Config.Bucket, id, crunchy.Locale, crunchy.Config.Signature, @@ -86,10 +84,8 @@ func (s *Season) Episodes() (episodes []*Episode, err error) { return s.children, nil } - resp, err := s.crunchy.request(fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/%s/%s/episodes?season_id=%s&locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s", - s.crunchy.Config.CountryCode, - s.crunchy.Config.MaturityRating, - s.crunchy.Config.Channel, + resp, err := s.crunchy.request(fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/episodes?season_id=%s&locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s", + s.crunchy.Config.Bucket, s.ID, s.crunchy.Locale, s.crunchy.Config.Signature, @@ -111,8 +107,10 @@ func (s *Season) Episodes() (episodes []*Episode, err error) { } if episode.Playback != "" { streamHref := item.(map[string]interface{})["__links__"].(map[string]interface{})["streams"].(map[string]interface{})["href"].(string) - if match := regexp.MustCompile(`(?m)^/cms/v2/\S+videos/(\w+)/streams$`).FindAllStringSubmatch(streamHref, -1); len(match) > 0 { + if match := regexp.MustCompile(`(?m)(\w+)/streams$`).FindAllStringSubmatch(streamHref, -1); len(match) > 0 { episode.StreamID = match[0][1] + } else { + fmt.Println() } } episodes = append(episodes, episode) diff --git a/stream.go b/stream.go index d8957e6..b1193ed 100644 --- a/stream.go +++ b/stream.go @@ -25,10 +25,8 @@ type Stream struct { // StreamsFromID returns a stream by its api id. func StreamsFromID(crunchy *Crunchyroll, id string) ([]*Stream, error) { - return fromVideoStreams(crunchy, fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/%s/%s/videos/%s/streams?locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s", - crunchy.Config.CountryCode, - crunchy.Config.MaturityRating, - crunchy.Config.Channel, + return fromVideoStreams(crunchy, fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/videos/%s/streams?locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s", + crunchy.Config.Bucket, id, crunchy.Locale, crunchy.Config.Signature, @@ -105,7 +103,7 @@ func fromVideoStreams(crunchy *Crunchyroll, endpoint string) (streams []*Stream, var id string var formatType FormatType href := jsonBody["__links__"].(map[string]interface{})["resource"].(map[string]interface{})["href"].(string) - if match := regexp.MustCompile(`(?sm)^/cms/v2/\S+/crunchyroll/(\w+)/(\w+)$`).FindAllStringSubmatch(href, -1); len(match) > 0 { + if match := regexp.MustCompile(`(?sm)/(\w+)/(\w+)$`).FindAllStringSubmatch(href, -1); len(match) > 0 { formatType = FormatType(match[0][1]) id = match[0][2] } diff --git a/video.go b/video.go index 00b7734..8a7ee76 100644 --- a/video.go +++ b/video.go @@ -69,10 +69,8 @@ type Movie struct { // MovieFromID returns a movie by its api id. func MovieFromID(crunchy *Crunchyroll, id string) (*Movie, error) { - resp, err := crunchy.request(fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/%s/%s/movies/%s&locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s", - crunchy.Config.CountryCode, - crunchy.Config.MaturityRating, - crunchy.Config.Channel, + resp, err := crunchy.request(fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/movies/%s&locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s", + crunchy.Config.Bucket, id, crunchy.Locale, crunchy.Config.Signature, @@ -102,10 +100,8 @@ func (m *Movie) MovieListing() (movieListings []*MovieListing, err error) { return m.children, nil } - resp, err := m.crunchy.request(fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/%s/%s/movies?movie_listing_id=%s&locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s", - m.crunchy.Config.CountryCode, - m.crunchy.Config.MaturityRating, - m.crunchy.Config.Channel, + resp, err := m.crunchy.request(fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/movies?movie_listing_id=%s&locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s", + m.crunchy.Config.Bucket, m.ID, m.crunchy.Locale, m.crunchy.Config.Signature, @@ -165,10 +161,8 @@ type Series struct { // SeriesFromID returns a series by its api id. func SeriesFromID(crunchy *Crunchyroll, id string) (*Series, error) { - resp, err := crunchy.request(fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/%s/%s/movies?movie_listing_id=%s&locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s", - crunchy.Config.CountryCode, - crunchy.Config.MaturityRating, - crunchy.Config.Channel, + resp, err := crunchy.request(fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/movies?movie_listing_id=%s&locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s", + crunchy.Config.Bucket, id, crunchy.Locale, crunchy.Config.Signature, @@ -198,10 +192,8 @@ func (s *Series) Seasons() (seasons []*Season, err error) { return s.children, nil } - resp, err := s.crunchy.request(fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/%s/%s/seasons?series_id=%s&locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s", - s.crunchy.Config.CountryCode, - s.crunchy.Config.MaturityRating, - s.crunchy.Config.Channel, + resp, err := s.crunchy.request(fmt.Sprintf("https://beta-api.crunchyroll.com/cms/v2/%s/seasons?series_id=%s&locale=%s&Signature=%s&Policy=%s&Key-Pair-Id=%s", + s.crunchy.Config.Bucket, s.ID, s.crunchy.Locale, s.crunchy.Config.Signature, From f51bdeaec7dcc05c4927b377ec113e1dbefc41c6 Mon Sep 17 00:00:00 2001 From: bytedream Date: Mon, 16 May 2022 20:03:52 +0200 Subject: [PATCH 02/10] Add error return in some login failure cases (#30) --- crunchyroll.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crunchyroll.go b/crunchyroll.go index 60b4320..d3b1ce8 100644 --- a/crunchyroll.go +++ b/crunchyroll.go @@ -97,6 +97,13 @@ func LoginWithCredentials(user string, password string, locale LOCALE, client *h if loginResp.StatusCode != http.StatusOK { return nil, fmt.Errorf("failed to auth with credentials: %s", loginResp.Status) + } else { + var loginRespBody map[string]interface{} + json.NewDecoder(loginResp.Body).Decode(&loginRespBody) + + if loginRespBody["error"].(bool) { + return nil, fmt.Errorf("an unexpected login error occoured: %s", loginRespBody["message"]) + } } return LoginWithSessionID(sessionID, locale, client) From 5b3466d06d555639aa27f3c13f54ac50b6e1740d Mon Sep 17 00:00:00 2001 From: bytedream Date: Mon, 16 May 2022 22:06:44 +0200 Subject: [PATCH 03/10] Add stream not available with non-premium error notice --- stream.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/stream.go b/stream.go index b1193ed..0fa54b9 100644 --- a/stream.go +++ b/stream.go @@ -80,8 +80,12 @@ func fromVideoStreams(crunchy *Crunchyroll, endpoint string) (streams []*Stream, json.NewDecoder(resp.Body).Decode(&jsonBody) if len(jsonBody) == 0 { - // this may get thrown when the crunchyroll account has just a normal account and not one with premium - return nil, errors.New("no stream available") + // this may get thrown when the crunchyroll account is just a normal account and not one with premium + if !crunchy.Config.Premium { + return nil, fmt.Errorf("no stream available, this might be the result of using a non-premium account") + } else { + return nil, errors.New("no stream available") + } } audioLocale := jsonBody["audio_locale"].(string) From 6c476df24effcad9d6297c453535ce7f9bcf72db Mon Sep 17 00:00:00 2001 From: bytedream Date: Mon, 16 May 2022 22:07:44 +0200 Subject: [PATCH 04/10] Set beta url notice only if account is premium --- cmd/crunchyroll-go/cmd/archive.go | 6 ++++-- cmd/crunchyroll-go/cmd/download.go | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/cmd/crunchyroll-go/cmd/archive.go b/cmd/crunchyroll-go/cmd/archive.go index 6f3f6a0..63e5d5a 100644 --- a/cmd/crunchyroll-go/cmd/archive.go +++ b/cmd/crunchyroll-go/cmd/archive.go @@ -182,8 +182,10 @@ func archive(urls []string) error { episodes, err := archiveExtractEpisodes(url) if err != nil { out.StopProgress("Failed to parse url %d", i+1) - out.Debug("If the error says no episodes could be found but the passed url is correct and a crunchyroll classic url, " + - "try the corresponding crunchyroll beta url instead and try again. See https://github.com/ByteDream/crunchyroll-go/issues/22 for more information") + if crunchy.Config.Premium { + out.Debug("If the error says no episodes could be found but the passed url is correct and a crunchyroll classic url, " + + "try the corresponding crunchyroll beta url instead and try again. See https://github.com/ByteDream/crunchyroll-go/issues/22 for more information") + } return err } out.StopProgress("Parsed url %d", i+1) diff --git a/cmd/crunchyroll-go/cmd/download.go b/cmd/crunchyroll-go/cmd/download.go index 1ce202c..1254048 100644 --- a/cmd/crunchyroll-go/cmd/download.go +++ b/cmd/crunchyroll-go/cmd/download.go @@ -128,8 +128,10 @@ func download(urls []string) error { episodes, err := downloadExtractEpisodes(url) if err != nil { out.StopProgress("Failed to parse url %d", i+1) - out.Debug("If the error says no episodes could be found but the passed url is correct and a crunchyroll classic url, " + - "try the corresponding crunchyroll beta url instead and try again. See https://github.com/ByteDream/crunchyroll-go/issues/22 for more information") + if crunchy.Config.Premium { + out.Debug("If the error says no episodes could be found but the passed url is correct and a crunchyroll classic url, " + + "try the corresponding crunchyroll beta url instead and try again. See https://github.com/ByteDream/crunchyroll-go/issues/22 for more information") + } return err } out.StopProgress("Parsed url %d", i+1) From 0ffae4ddda15c72020343837367c6ea9be829dd9 Mon Sep 17 00:00:00 2001 From: bytedream Date: Mon, 16 May 2022 22:24:21 +0200 Subject: [PATCH 05/10] Add available function to check if episode streams are available --- episode.go | 7 +++++++ url.go | 5 +++++ utils/sort.go | 3 +++ 3 files changed, 15 insertions(+) diff --git a/episode.go b/episode.go index 3c9d38f..7d477ce 100644 --- a/episode.go +++ b/episode.go @@ -112,6 +112,8 @@ func EpisodeFromID(crunchy *Crunchyroll, id string) (*Episode, error) { // 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 // this method on the first episode of the season. +// Will fail if no streams are available, thus use Available to +// prevent any misleading errors. func (e *Episode) AudioLocale() (LOCALE, error) { streams, err := e.Streams() if err != nil { @@ -120,6 +122,11 @@ func (e *Episode) AudioLocale() (LOCALE, error) { return streams[0].AudioLocale, nil } +// Available returns if downloadable streams for this episodes are available. +func (e *Episode) Available() bool { + return e.crunchy.Config.Premium || !e.IsPremiumOnly +} + // GetFormat returns the format which matches the given resolution and subtitle locale. func (e *Episode) GetFormat(resolution string, subtitle LOCALE, hardsub bool) (*Format, error) { streams, err := e.Streams() diff --git a/url.go b/url.go index 32603fc..c84a3b9 100644 --- a/url.go +++ b/url.go @@ -49,6 +49,11 @@ func (c *Crunchyroll) ExtractEpisodesFromUrl(url string, audio ...LOCALE) ([]*Ep } for _, episode := range episodes { + // if no episode streams are available, calling episode.AudioLocale + // will result in an unwanted error + if !episode.Available() { + continue + } locale, err := episode.AudioLocale() if err != nil { return nil, err diff --git a/utils/sort.go b/utils/sort.go index a44717d..e06946c 100644 --- a/utils/sort.go +++ b/utils/sort.go @@ -52,6 +52,9 @@ func SortEpisodesByAudio(episodes []*crunchyroll.Episode) (map[crunchyroll.LOCAL var wg sync.WaitGroup var lock sync.Mutex for _, episode := range episodes { + if !episode.Available() { + continue + } episode := episode wg.Add(1) go func() { From 43be2eee146550f9934231bbd1525cf735e41ab1 Mon Sep 17 00:00:00 2001 From: bytedream Date: Mon, 16 May 2022 22:25:33 +0200 Subject: [PATCH 06/10] Fix typo & add audio locale todo for non-premium accounts --- season.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/season.go b/season.go index b83d214..f5ec08e 100644 --- a/season.go +++ b/season.go @@ -37,7 +37,7 @@ type Season struct { AvailabilityNotes string `json:"availability_notes"` - // the locales are always empty, idk why this may change in the future + // the locales are always empty, idk why, this may change in the future AudioLocales []LOCALE SubtitleLocales []LOCALE } @@ -71,6 +71,7 @@ func SeasonFromID(crunchy *Crunchyroll, id string) (*Season, error) { // AudioLocale returns the audio locale of the season. func (s *Season) AudioLocale() (LOCALE, error) { + // TODO: Add a function like Episode.Available to prevent this from returning an unwanted error when the account is non-premium episodes, err := s.Episodes() if err != nil { return "", err From f635bf1a2e1783e88fec015a25627d58c5698108 Mon Sep 17 00:00:00 2001 From: bytedream Date: Wed, 18 May 2022 21:47:17 +0200 Subject: [PATCH 07/10] Add available function to check if season streams are available --- episode.go | 4 ++-- season.go | 12 +++++++++++- url.go | 1 + 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/episode.go b/episode.go index 7d477ce..3a2e403 100644 --- a/episode.go +++ b/episode.go @@ -112,8 +112,8 @@ func EpisodeFromID(crunchy *Crunchyroll, id string) (*Episode, error) { // 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 // this method on the first episode of the season. -// Will fail if no streams are available, thus use Available to -// prevent any misleading errors. +// Will fail if no streams are available, thus use Episode.Available +// to prevent any misleading errors. func (e *Episode) AudioLocale() (LOCALE, error) { streams, err := e.Streams() if err != nil { diff --git a/season.go b/season.go index f5ec08e..0bb8623 100644 --- a/season.go +++ b/season.go @@ -70,8 +70,9 @@ func SeasonFromID(crunchy *Crunchyroll, id string) (*Season, error) { } // AudioLocale returns the audio locale of the season. +// Will fail if no streams are available, thus use Season.Available +// to prevent any misleading errors. func (s *Season) AudioLocale() (LOCALE, error) { - // TODO: Add a function like Episode.Available to prevent this from returning an unwanted error when the account is non-premium episodes, err := s.Episodes() if err != nil { return "", err @@ -79,6 +80,15 @@ func (s *Season) AudioLocale() (LOCALE, error) { return episodes[0].AudioLocale() } +// Available returns if downloadable streams for this season are available. +func (s *Season) Available() (bool, error) { + episodes, err := s.Episodes() + if err != nil { + return false, err + } + return episodes[0].Available(), nil +} + // Episodes returns all episodes which are available for the season. func (s *Season) Episodes() (episodes []*Episode, err error) { if s.children != nil { diff --git a/url.go b/url.go index c84a3b9..0f5bb25 100644 --- a/url.go +++ b/url.go @@ -21,6 +21,7 @@ func (c *Crunchyroll) ExtractEpisodesFromUrl(url string, audio ...LOCALE) ([]*Ep } for _, season := range seasons { if audio != nil { + locale, err := season.AudioLocale() if err != nil { return nil, err From 7be803d485ca88994b1169b24e6c5adf88236c36 Mon Sep 17 00:00:00 2001 From: bytedream Date: Wed, 18 May 2022 21:54:31 +0200 Subject: [PATCH 08/10] Add extended error message if account is non-premium --- url.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/url.go b/url.go index 0f5bb25..86874eb 100644 --- a/url.go +++ b/url.go @@ -13,6 +13,7 @@ func (c *Crunchyroll) ExtractEpisodesFromUrl(url string, audio ...LOCALE) ([]*Ep } var eps []*Episode + var notAvailableContinue bool if series != nil { seasons, err := series.Seasons() @@ -21,6 +22,12 @@ func (c *Crunchyroll) ExtractEpisodesFromUrl(url string, audio ...LOCALE) ([]*Ep } for _, season := range seasons { if audio != nil { + if available, err := season.Available(); err != nil { + return nil, err + } else if !available { + notAvailableContinue = true + continue + } locale, err := season.AudioLocale() if err != nil { @@ -53,6 +60,7 @@ func (c *Crunchyroll) ExtractEpisodesFromUrl(url string, audio ...LOCALE) ([]*Ep // if no episode streams are available, calling episode.AudioLocale // will result in an unwanted error if !episode.Available() { + notAvailableContinue = true continue } locale, err := episode.AudioLocale() @@ -77,7 +85,11 @@ func (c *Crunchyroll) ExtractEpisodesFromUrl(url string, audio ...LOCALE) ([]*Ep } if len(eps) == 0 { - return nil, fmt.Errorf("could not find any matching episode") + if notAvailableContinue { + return nil, fmt.Errorf("could not find any matching episode which is accessable with an non-premium account") + } else { + return nil, fmt.Errorf("could not find any matching episode") + } } return eps, nil From d4e095a576f1617082ad9952b626c916800e5382 Mon Sep 17 00:00:00 2001 From: bytedream Date: Wed, 18 May 2022 21:56:29 +0200 Subject: [PATCH 09/10] Fix typo --- url.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/url.go b/url.go index 86874eb..95f3951 100644 --- a/url.go +++ b/url.go @@ -86,7 +86,7 @@ func (c *Crunchyroll) ExtractEpisodesFromUrl(url string, audio ...LOCALE) ([]*Ep if len(eps) == 0 { if notAvailableContinue { - return nil, fmt.Errorf("could not find any matching episode which is accessable with an non-premium account") + return nil, fmt.Errorf("could not find any matching episode which is accessable with a non-premium account") } else { return nil, fmt.Errorf("could not find any matching episode") } From 137f3779ea2e2182ab1c15ed35c89fd931587732 Mon Sep 17 00:00:00 2001 From: bytedream Date: Fri, 10 Jun 2022 16:12:58 +0200 Subject: [PATCH 10/10] Fix merge issues again, I love this shit --- crunchyroll.go | 41 +++++++++++++---------------------------- stream.go | 2 +- 2 files changed, 14 insertions(+), 29 deletions(-) diff --git a/crunchyroll.go b/crunchyroll.go index 73cba57..be5af03 100644 --- a/crunchyroll.go +++ b/crunchyroll.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/json" + "errors" "fmt" "io" "net/http" @@ -110,6 +111,9 @@ type loginResponse struct { Scope string `json:"scope"` Country string `json:"country"` AccountID string `json:"account_id"` + + Error bool `json:"error"` + Message string `json:"message"` } // LoginWithCredentials logs in via crunchyroll username or email and password. @@ -133,16 +137,13 @@ func LoginWithCredentials(user string, password string, locale LOCALE, client *h } defer resp.Body.Close() - if loginResp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("failed to auth with credentials: %s", loginResp.Status) - } else { - var loginRespBody map[string]interface{} - json.NewDecoder(loginResp.Body).Decode(&loginRespBody) - - if loginRespBody["error"].(bool) { - return nil, fmt.Errorf("an unexpected login error occoured: %s", loginRespBody["message"]) - } - } + var loginResp loginResponse + json.NewDecoder(resp.Body).Decode(&loginResp) + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("failed to auth with credentials: %s", resp.Status) + } else if loginResp.Error { + return nil, fmt.Errorf("an unexpected login error occoured: %s", loginResp.Message) + } var etpRt string for _, cookie := range resp.Cookies() { @@ -180,18 +181,16 @@ func LoginWithSessionID(sessionID string, locale LOCALE, client *http.Client) (* if err = json.NewDecoder(resp.Body).Decode(&jsonBody); err != nil { return nil, fmt.Errorf("failed to parse start session with session id response: %w", err) } - + if isError, ok := jsonBody["error"]; ok && isError.(bool) { return nil, fmt.Errorf("invalid session id (%s): %s", jsonBody["message"].(string), jsonBody["code"]) } data := jsonBody["data"].(map[string]interface{}) - crunchy.Config.CountryCode = data["country_code"].(string) user := data["user"] if user == nil { return nil, errors.New("invalid session id, user is not logged in") } - crunchy.Config.Premium = user.(map[string]interface{})["premium"] != "" var etpRt string for _, cookie := range resp.Cookies() { @@ -260,21 +259,7 @@ func postLogin(loginResp loginResponse, etpRt string, locale LOCALE, client *htt cms := jsonBody["cms"].(map[string]any) crunchy.Config.Bucket = strings.TrimPrefix(cms["bucket"].(string), "/") - if strings.HasSuffix(crunchy.Config.Bucket, "crunchyroll") { - crunchy.Config.Premium = true - crunchy.Config.Channel = "crunchyroll" - } else { - crunchy.Config.Premium = false - crunchy.Config.Channel = "-" - } - - if strings.Contains(cms["bucket"].(string), "crunchyroll") { - crunchy.Config.Premium = true - crunchy.Config.Channel = "crunchyroll" - } else { - crunchy.Config.Premium = false - crunchy.Config.Channel = "-" - } + crunchy.Config.Premium = strings.HasSuffix(crunchy.Config.Bucket, "crunchyroll") // / is trimmed so that urls which require it must be in .../{bucket}/... like format. // this just looks cleaner diff --git a/stream.go b/stream.go index 042d5f7..e5ce54f 100644 --- a/stream.go +++ b/stream.go @@ -84,7 +84,7 @@ func fromVideoStreams(crunchy *Crunchyroll, endpoint string) (streams []*Stream, if !crunchy.Config.Premium { return nil, fmt.Errorf("no stream available, this might be the result of using a non-premium account") } else { - return nil, errors.New("no stream available") + return nil, fmt.Errorf("no stream available") } }