From 62735cf07cb8740db61fa886ddb02fb82e766952 Mon Sep 17 00:00:00 2001 From: bytedream Date: Mon, 16 May 2022 19:28:05 +0200 Subject: [PATCH 01/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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 a4ec163275ab6fb6961cc6e208d959ee02399a80 Mon Sep 17 00:00:00 2001 From: bytedream Date: Fri, 20 May 2022 22:57:07 +0200 Subject: [PATCH 10/17] Add basic encrypted login credentials support --- cmd/crunchyroll-go/cmd/login.go | 75 ++++++++++++++++++++++++++++--- cmd/crunchyroll-go/cmd/unix.go | 48 ++++++++++++++++++++ cmd/crunchyroll-go/cmd/utils.go | 52 ++++++++++++++++++--- cmd/crunchyroll-go/cmd/windows.go | 41 +++++++++++++++++ 4 files changed, 205 insertions(+), 11 deletions(-) create mode 100644 cmd/crunchyroll-go/cmd/unix.go create mode 100644 cmd/crunchyroll-go/cmd/windows.go diff --git a/cmd/crunchyroll-go/cmd/login.go b/cmd/crunchyroll-go/cmd/login.go index c9fc923..565dac0 100644 --- a/cmd/crunchyroll-go/cmd/login.go +++ b/cmd/crunchyroll-go/cmd/login.go @@ -1,15 +1,22 @@ package cmd import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "crypto/sha256" "fmt" "github.com/ByteDream/crunchyroll-go/v2" "github.com/spf13/cobra" + "io" "os" "path/filepath" ) var ( loginPersistentFlag bool + loginEncryptFlag bool loginSessionIDFlag bool ) @@ -33,6 +40,10 @@ func init() { "persistent", false, "If the given credential should be stored persistent") + loginCmd.Flags().BoolVar(&loginEncryptFlag, + "encrypt", + false, + "Encrypt the given credentials (won't do anything if --session-id is given)") loginCmd.Flags().BoolVar(&loginSessionIDFlag, "session-id", @@ -49,20 +60,74 @@ func loginCredentials(user, password string) error { return err } + if err = os.WriteFile(filepath.Join(os.TempDir(), ".crunchy"), []byte(c.SessionID), 0600); err != nil { + return err + } + if loginPersistentFlag { if configDir, err := os.UserConfigDir(); err != nil { return fmt.Errorf("could not save credentials persistent: %w", err) } else { + var credentials []byte + + if loginEncryptFlag { + var passwd []byte + + for { + fmt.Print("Enter password: ") + passwd, err = readLineSilent() + if err != nil { + return err + } + fmt.Println() + + fmt.Print("Enter password again: ") + repasswd, err := readLineSilent() + if err != nil { + return err + } + fmt.Println() + + if !bytes.Equal(passwd, repasswd) { + fmt.Println("Passwords does not match, try again") + continue + } + + hashedPassword := sha256.Sum256(passwd) + block, err := aes.NewCipher(hashedPassword[:]) + if err != nil { + out.Err("Failed to create block: %w", err) + os.Exit(1) + } + gcm, err := cipher.NewGCM(block) + if err != nil { + out.Err("Failed to create gcm: %w", err) + os.Exit(1) + } + nonce := make([]byte, gcm.NonceSize()) + if _, err := io.ReadFull(rand.Reader, nonce); err != nil { + out.Err("Failed to fill nonce: %w", err) + os.Exit(1) + } + + b := gcm.Seal(nonce, nonce, []byte(fmt.Sprintf("%s\n%s", user, password)), nil) + credentials = append([]byte("aes:"), b...) + + break + } + } else { + credentials = []byte(fmt.Sprintf("%s\n%s", user, password)) + } + os.MkdirAll(filepath.Join(configDir, "crunchyroll-go"), 0755) - if err = os.WriteFile(filepath.Join(configDir, "crunchyroll-go", "crunchy"), []byte(fmt.Sprintf("%s\n%s", user, password)), 0600); err != nil { + if err = os.WriteFile(filepath.Join(configDir, "crunchyroll-go", "crunchy"), credentials, 0600); err != nil { return err } - out.Info("The login information will be stored permanently UNENCRYPTED on your drive (%s)", filepath.Join(configDir, "crunchyroll-go", "crunchy")) + if !loginEncryptFlag { + out.Info("The login information will be stored permanently UNENCRYPTED on your drive (%s)", filepath.Join(configDir, "crunchyroll-go", "crunchy")) + } } } - if err = os.WriteFile(filepath.Join(os.TempDir(), ".crunchy"), []byte(c.SessionID), 0600); err != nil { - return err - } if !loginPersistentFlag { out.Info("Due to security reasons, you have to login again on the next reboot") diff --git a/cmd/crunchyroll-go/cmd/unix.go b/cmd/crunchyroll-go/cmd/unix.go new file mode 100644 index 0000000..962088f --- /dev/null +++ b/cmd/crunchyroll-go/cmd/unix.go @@ -0,0 +1,48 @@ +//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos + +package cmd + +import ( + "bufio" + "os" + "os/exec" + "syscall" +) + +// https://github.com/bgentry/speakeasy/blob/master/speakeasy_unix.go +var stty string + +func init() { + var err error + if stty, err = exec.LookPath("stty"); err != nil { + panic(err) + } +} + +func readLineSilent() ([]byte, error) { + pid, err := setEcho(false) + if err != nil { + return nil, err + } + defer setEcho(true) + + syscall.Wait4(pid, nil, 0, nil) + + l, _, err := bufio.NewReader(os.Stdin).ReadLine() + return l, err +} + +func setEcho(on bool) (pid int, err error) { + fds := []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()} + + if on { + pid, err = syscall.ForkExec(stty, []string{"stty", "echo"}, &syscall.ProcAttr{Files: fds}) + } else { + pid, err = syscall.ForkExec(stty, []string{"stty", "-echo"}, &syscall.ProcAttr{Files: fds}) + } + + if err != nil { + return 0, err + } + return +} diff --git a/cmd/crunchyroll-go/cmd/utils.go b/cmd/crunchyroll-go/cmd/utils.go index 2a632ff..d7eba3d 100644 --- a/cmd/crunchyroll-go/cmd/utils.go +++ b/cmd/crunchyroll-go/cmd/utils.go @@ -1,6 +1,9 @@ package cmd import ( + "crypto/aes" + "crypto/cipher" + "crypto/sha256" "fmt" "github.com/ByteDream/crunchyroll-go/v2" "github.com/ByteDream/crunchyroll-go/v2/utils" @@ -167,13 +170,49 @@ func loadCrunchy() { } split := strings.SplitN(string(body), "\n", 2) if len(split) == 1 || split[1] == "" { - split[0] = url.QueryEscape(split[0]) - if crunchy, err = crunchyroll.LoginWithSessionID(split[0], systemLocale(true), client); err != nil { - out.StopProgress(err.Error()) - os.Exit(1) + if strings.HasPrefix(split[0], "aes:") { + encrypted := body[4:] + + out.StopProgress("Credentials are encrypted") + fmt.Print("Enter password to encrypt it: ") + passwd, err := readLineSilent() + fmt.Println() + if err != nil { + out.Err("Failed to read password; %w", err) + os.Exit(1) + } + out.SetProgress("Logging in") + + hashedPassword := sha256.Sum256(passwd) + block, err := aes.NewCipher(hashedPassword[:]) + if err != nil { + out.Err("Failed to create block: %w", err) + os.Exit(1) + } + gcm, err := cipher.NewGCM(block) + if err != nil { + out.Err("Failed to create gcm: %w", err) + os.Exit(1) + } + nonce, c := encrypted[:gcm.NonceSize()], encrypted[gcm.NonceSize():] + + b, err := gcm.Open(nil, nonce, c, nil) + if err != nil { + out.StopProgress("Invalid password") + os.Exit(1) + } + split = strings.SplitN(string(b), "\n", 2) + } else { + split[0] = url.QueryEscape(split[0]) + if crunchy, err = crunchyroll.LoginWithSessionID(split[0], systemLocale(true), client); err != nil { + out.StopProgress(err.Error()) + os.Exit(1) + } + out.Debug("Logged in with session id %s. BLANK THIS LINE OUT IF YOU'RE ASKED TO POST THE DEBUG OUTPUT SOMEWHERE", split[0]) } - out.Debug("Logged in with session id %s. BLANK THIS LINE OUT IF YOU'RE ASKED TO POST THE DEBUG OUTPUT SOMEWHERE", split[0]) - } else { + } + + if len(split) == 2 { if crunchy, err = crunchyroll.LoginWithCredentials(split[0], split[1], systemLocale(true), client); err != nil { out.StopProgress(err.Error()) os.Exit(1) @@ -183,6 +222,7 @@ func loadCrunchy() { // it seems like that crunchyroll has also a little cooldown if a user logs in too often in a short time os.WriteFile(filepath.Join(os.TempDir(), ".crunchy"), []byte(crunchy.SessionID), 0600) } + out.StopProgress("Logged in") return } diff --git a/cmd/crunchyroll-go/cmd/windows.go b/cmd/crunchyroll-go/cmd/windows.go new file mode 100644 index 0000000..d6eecb1 --- /dev/null +++ b/cmd/crunchyroll-go/cmd/windows.go @@ -0,0 +1,41 @@ +//go:build windows + +package cmd + +import ( + "bufio" + "os" + "syscall" +) + +// https://github.com/bgentry/speakeasy/blob/master/speakeasy_windows.go +func readLineSilent() ([]byte, error) { + var oldMode uint32 + + if err := syscall.GetConsoleMode(syscall.Stdin, &oldMode); err != nil { + return nil, err + } + + newMode := oldMode &^ 0x0004 + + err := setConsoleMode(syscall.Stdin, newMode) + defer setConsoleMode(syscall.Stdin, oldMode) + + if err != nil { + return nil, err + } + + l, _, err := bufio.NewReader(os.Stdin).ReadLine() + if err != nil { + return nil, err + } + return l, err +} + +func setConsoleMode(console syscall.Handle, mode uint32) error { + dll := syscall.MustLoadDLL("kernel32") + proc := dll.MustFindProc("SetConsoleMode") + _, _, err := proc.Call(uintptr(console), uintptr(mode)) + + return err +} From b9ff56c111e3a5aa1cfa7f730d04a173e3524210 Mon Sep 17 00:00:00 2001 From: bytedream Date: Tue, 31 May 2022 17:01:03 +0200 Subject: [PATCH 11/17] Extend encrypt flag description --- cmd/crunchyroll-go/cmd/login.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/crunchyroll-go/cmd/login.go b/cmd/crunchyroll-go/cmd/login.go index 565dac0..1c47340 100644 --- a/cmd/crunchyroll-go/cmd/login.go +++ b/cmd/crunchyroll-go/cmd/login.go @@ -43,7 +43,7 @@ func init() { loginCmd.Flags().BoolVar(&loginEncryptFlag, "encrypt", false, - "Encrypt the given credentials (won't do anything if --session-id is given)") + "Encrypt the given credentials (won't do anything if --session-id is given or --persistent is not given)") loginCmd.Flags().BoolVar(&loginSessionIDFlag, "session-id", From ce29e31164ba25396cbcbb3b619723e5ffa283d4 Mon Sep 17 00:00:00 2001 From: bytedream Date: Tue, 31 May 2022 17:04:39 +0200 Subject: [PATCH 12/17] Add encrypt flag notice when use login without it --- cmd/crunchyroll-go/cmd/login.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/crunchyroll-go/cmd/login.go b/cmd/crunchyroll-go/cmd/login.go index 1c47340..d31179e 100644 --- a/cmd/crunchyroll-go/cmd/login.go +++ b/cmd/crunchyroll-go/cmd/login.go @@ -124,7 +124,8 @@ func loginCredentials(user, password string) error { return err } if !loginEncryptFlag { - out.Info("The login information will be stored permanently UNENCRYPTED on your drive (%s)", filepath.Join(configDir, "crunchyroll-go", "crunchy")) + out.Info("The login information will be stored permanently UNENCRYPTED on your drive (%s). "+ + "To encrypt it, use the `--encrypt` flag", filepath.Join(configDir, "crunchyroll-go", "crunchy")) } } } From c963af1f11c2585494372243d31a7c508c466247 Mon Sep 17 00:00:00 2001 From: ByteDream <63594396+ByteDream@users.noreply.github.com> Date: Wed, 8 Jun 2022 14:10:46 +0200 Subject: [PATCH 13/17] Why github --- cmd/crunchyroll-go/cmd/utils.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/cmd/crunchyroll-go/cmd/utils.go b/cmd/crunchyroll-go/cmd/utils.go index 7fe498f..dd1254c 100644 --- a/cmd/crunchyroll-go/cmd/utils.go +++ b/cmd/crunchyroll-go/cmd/utils.go @@ -170,7 +170,6 @@ func loadCrunchy() { } split := strings.SplitN(string(body), "\n", 2) if len(split) == 1 || split[1] == "" { -<<<<<<< v3/feature/encrypted-credentials if strings.HasPrefix(split[0], "aes:") { encrypted := body[4:] @@ -214,7 +213,6 @@ func loadCrunchy() { } if len(split) == 2 { -======= split[0] = url.QueryEscape(split[0]) if crunchy, err = crunchyroll.LoginWithEtpRt(split[0], systemLocale(true), client); err != nil { out.StopProgress(err.Error()) @@ -222,7 +220,6 @@ func loadCrunchy() { } out.Debug("Logged in with etp rt cookie %s. BLANK THIS LINE OUT IF YOU'RE ASKED TO POST THE DEBUG OUTPUT SOMEWHERE", split[0]) } else { ->>>>>>> next/v3 if crunchy, err = crunchyroll.LoginWithCredentials(split[0], split[1], systemLocale(true), client); err != nil { out.StopProgress(err.Error()) os.Exit(1) From 735136077eeb1cbd916d5c95627898b90525237e Mon Sep 17 00:00:00 2001 From: ByteDream <63594396+ByteDream@users.noreply.github.com> Date: Wed, 8 Jun 2022 14:12:30 +0200 Subject: [PATCH 14/17] I removed it but ok github if you want i do it a second time, no problem --- cmd/crunchyroll-go/cmd/login.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cmd/crunchyroll-go/cmd/login.go b/cmd/crunchyroll-go/cmd/login.go index bfef794..0c00100 100644 --- a/cmd/crunchyroll-go/cmd/login.go +++ b/cmd/crunchyroll-go/cmd/login.go @@ -135,13 +135,10 @@ func loginCredentials(user, password string) error { "To encrypt it, use the `--encrypt` flag", filepath.Join(configDir, "crunchyroll-go", "crunchy")) } } - } -<<<<<<< v3/feature/encrypted-credentials -======= + if err = os.WriteFile(filepath.Join(os.TempDir(), ".crunchy"), []byte(c.EtpRt), 0600); err != nil { return err } ->>>>>>> next/v3 if !loginPersistentFlag { out.Info("Due to security reasons, you have to login again on the next reboot") From 69d2e10362929c415ecc050ac73535eedc7defdf Mon Sep 17 00:00:00 2001 From: bytedream Date: Wed, 8 Jun 2022 14:18:20 +0200 Subject: [PATCH 15/17] I fucking hate it --- cmd/crunchyroll-go/cmd/login.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/cmd/crunchyroll-go/cmd/login.go b/cmd/crunchyroll-go/cmd/login.go index 0c00100..d8cc5c3 100644 --- a/cmd/crunchyroll-go/cmd/login.go +++ b/cmd/crunchyroll-go/cmd/login.go @@ -67,10 +67,6 @@ func loginCredentials(user, password string) error { return err } - if err = os.WriteFile(filepath.Join(os.TempDir(), ".crunchy"), []byte(c.SessionID), 0600); err != nil { - return err - } - if loginPersistentFlag { if configDir, err := os.UserConfigDir(); err != nil { return fmt.Errorf("could not save credentials persistent: %w", err) @@ -135,7 +131,8 @@ func loginCredentials(user, password string) error { "To encrypt it, use the `--encrypt` flag", filepath.Join(configDir, "crunchyroll-go", "crunchy")) } } - + } + if err = os.WriteFile(filepath.Join(os.TempDir(), ".crunchy"), []byte(c.EtpRt), 0600); err != nil { return err } From 31d3065e7bcc30ac6693254be4ae4a68570894ce Mon Sep 17 00:00:00 2001 From: bytedream Date: Thu, 9 Jun 2022 10:31:16 +0200 Subject: [PATCH 16/17] Fix weird code which was probably caused by a wonderful merge --- cmd/crunchyroll-go/cmd/utils.go | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/cmd/crunchyroll-go/cmd/utils.go b/cmd/crunchyroll-go/cmd/utils.go index dd1254c..12d3d4e 100644 --- a/cmd/crunchyroll-go/cmd/utils.go +++ b/cmd/crunchyroll-go/cmd/utils.go @@ -150,7 +150,7 @@ func loadCrunchy() { out.StopProgress("Failed to read login information: %v", err) os.Exit(1) } - if crunchy, err = crunchyroll.LoginWithEtpRt(url.QueryEscape(string(body)), systemLocale(true), client); err != nil { + if crunchy, err = crunchyroll.LoginWithEtpRt(string(body), systemLocale(true), client); err != nil { out.Debug("Failed to login with temp etp rt cookie: %v", err) } else { out.Debug("Logged in with etp rt cookie %s. BLANK THIS LINE OUT IF YOU'RE ASKED TO POST THE DEBUG OUTPUT SOMEWHERE", body) @@ -202,34 +202,27 @@ func loadCrunchy() { os.Exit(1) } split = strings.SplitN(string(b), "\n", 2) - } else { - split[0] = url.QueryEscape(split[0]) - if crunchy, err = crunchyroll.LoginWithSessionID(split[0], systemLocale(true), client); err != nil { - out.StopProgress(err.Error()) - os.Exit(1) - } - out.Debug("Logged in with session id %s. BLANK THIS LINE OUT IF YOU'RE ASKED TO POST THE DEBUG OUTPUT SOMEWHERE", split[0]) } } if len(split) == 2 { - split[0] = url.QueryEscape(split[0]) + if crunchy, err = crunchyroll.LoginWithCredentials(split[0], split[1], systemLocale(true), client); err != nil { + out.StopProgress(err.Error()) + os.Exit(1) + } + out.Debug("Logged in with credentials") + } else { if crunchy, err = crunchyroll.LoginWithEtpRt(split[0], systemLocale(true), client); err != nil { out.StopProgress(err.Error()) os.Exit(1) } out.Debug("Logged in with etp rt cookie %s. BLANK THIS LINE OUT IF YOU'RE ASKED TO POST THE DEBUG OUTPUT SOMEWHERE", split[0]) - } else { - if crunchy, err = crunchyroll.LoginWithCredentials(split[0], split[1], systemLocale(true), client); err != nil { - out.StopProgress(err.Error()) - os.Exit(1) - } - out.Debug("Logged in with etp rt cookie %s. BLANK THIS LINE OUT IF YOU'RE ASKED TO POST THE DEBUG OUTPUT SOMEWHERE", crunchy.EtpRt) - // the etp rt is written to a temp file to reduce the amount of re-logging in. - // it seems like that crunchyroll has also a little cooldown if a user logs in too often in a short time - os.WriteFile(filepath.Join(os.TempDir(), ".crunchy"), []byte(crunchy.EtpRt), 0600) } + // the etp rt is written to a temp file to reduce the amount of re-logging in. + // it seems like that crunchyroll has also a little cooldown if a user logs in too often in a short time + os.WriteFile(filepath.Join(os.TempDir(), ".crunchy"), []byte(crunchy.EtpRt), 0600) + out.StopProgress("Logged in") return } From 137f3779ea2e2182ab1c15ed35c89fd931587732 Mon Sep 17 00:00:00 2001 From: bytedream Date: Fri, 10 Jun 2022 16:12:58 +0200 Subject: [PATCH 17/17] 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") } }