From 901bbf0706e22ee37ee4f940b274f40b412261b7 Mon Sep 17 00:00:00 2001 From: bytedream Date: Fri, 13 May 2022 19:22:17 +0200 Subject: [PATCH 01/12] Add update command --- cmd/crunchyroll-go/cmd/update.go | 134 +++++++++++++++++++++++++++++++ crunchyroll-go.1 | 9 +++ 2 files changed, 143 insertions(+) create mode 100644 cmd/crunchyroll-go/cmd/update.go diff --git a/cmd/crunchyroll-go/cmd/update.go b/cmd/crunchyroll-go/cmd/update.go new file mode 100644 index 0000000..4c7aaad --- /dev/null +++ b/cmd/crunchyroll-go/cmd/update.go @@ -0,0 +1,134 @@ +package cmd + +import ( + "encoding/json" + "fmt" + "github.com/spf13/cobra" + "io" + "os" + "os/exec" + "runtime" + "strings" +) + +var ( + updateInstallFlag bool +) + +var updateCmd = &cobra.Command{ + Use: "update", + Short: "Check if updates are available", + Args: cobra.MaximumNArgs(0), + + RunE: func(cmd *cobra.Command, args []string) error { + return update() + }, +} + +func init() { + updateCmd.Flags().BoolVarP(&updateInstallFlag, + "install", + "i", + false, + "If set and a new version is available, the new version gets installed") + + rootCmd.AddCommand(updateCmd) +} + +func update() error { + var release map[string]interface{} + + resp, err := client.Get("https://api.github.com/repos/ByteDream/crunchyroll-go/releases/latest") + if err != nil { + return err + } + defer resp.Body.Close() + if err = json.NewDecoder(resp.Body).Decode(&release); err != nil { + return err + } + releaseVersion := strings.TrimPrefix(release["tag_name"].(string), "v") + + if Version == "development" { + out.Info("Development version, update service not available") + return nil + } + + latestRelease := strings.SplitN(releaseVersion, ".", 4) + if len(latestRelease) != 3 { + return fmt.Errorf("latest tag name (%s) is not parsable", releaseVersion) + } + + internalVersion := strings.SplitN(Version, ".", 4) + if len(internalVersion) != 3 { + return fmt.Errorf("internal version (%s) is not parsable", Version) + } + + var hasUpdate bool + for i := 0; i < 3; i++ { + if latestRelease[i] < internalVersion[i] { + out.Info("Local version (%s) is newer than version in latest release (%s)", Version, releaseVersion) + return nil + } else if latestRelease[i] > internalVersion[i] { + hasUpdate = true + } + } + + if !hasUpdate { + out.Info("Version is up-to-date") + return nil + } + + out.Info("A new version is available (%s). Installed version is %s: https://github.com/ByteDream/crunchyroll-go/releases/tag/v%s", releaseVersion, Version, releaseVersion) + + if updateInstallFlag { + if runtime.GOARCH != "amd64" { + return fmt.Errorf("invalid architecture found (%s), only amd64 is currently supported for automatic updating. "+ + "You have to update manually (https://github.com/ByteDream/crunchyroll-go)", runtime.GOARCH) + } + + var downloadFile string + switch runtime.GOOS { + case "linux": + yayCommand := exec.Command("pacman -Q crunchyroll-go") + if yayCommand.Run() == nil && yayCommand.ProcessState.Success() { + out.Info("crunchyroll-go was probably installed via an Arch Linux AUR helper (like yay). Updating via this AUR helper is recommended") + return nil + } + downloadFile = fmt.Sprintf("crunchy-v%s_linux", releaseVersion) + case "darwin": + downloadFile = fmt.Sprintf("crunchy-v%s_darwin", releaseVersion) + case "windows": + downloadFile = fmt.Sprintf("crunchy-v%s_windows.exe", releaseVersion) + default: + return fmt.Errorf("invalid operation system found (%s), only linux, windows and darwin / macos are currently supported. "+ + "You have to update manually (https://github.com/ByteDream/crunchyroll-go)", runtime.GOOS) + } + + out.SetProgress("Updating executable %s", os.Args[0]) + + perms, err := os.Stat(os.Args[0]) + if err != nil { + return err + } + os.Remove(os.Args[0]) + executeFile, err := os.OpenFile(os.Args[0], os.O_CREATE|os.O_WRONLY, perms.Mode()) + if err != nil { + return err + } + defer executeFile.Close() + + resp, err := client.Get(fmt.Sprintf("https://github.com/ByteDream/crunchyroll-go/releases/download/v%s/%s", releaseVersion, downloadFile)) + if err != nil { + return err + } + defer resp.Body.Close() + + if _, err = io.Copy(executeFile, resp.Body); err != nil { + return err + } + + out.StopProgress("Updated executable %s", os.Args[0]) + } + + return nil +} diff --git a/crunchyroll-go.1 b/crunchyroll-go.1 index 558aa23..576c291 100644 --- a/crunchyroll-go.1 +++ b/crunchyroll-go.1 @@ -13,6 +13,8 @@ crunchyroll-go login [\fB--persistent\fR] [\fB--session-id\fR \fISESSION_ID\fR] crunchyroll-go download [\fB-a\fR \fIAUDIO\fR] [\fB-s\fR \fISUBTITLE\fR] [\fB-d\fR \fIDIRECTORY\fR] [\fB-o\fR \fIOUTPUT\fR] [\fB-r\fR \fIRESOLUTION\fR] [\fB-g\fR \fIGOROUTINES\fR] \fIURLs...\fR .br crunchyroll-go archive [\fB-l\fR \fILANGUAGE\fR] [\fB-d\fR \fIDIRECTORY\fR] [\fB-o\fR \fIOUTPUT\fR] [\fB-m\fR \fIMERGE BEHAVIOR\fR] [\fB-c\fR \fICOMPRESS\fR] [\fB-r\fR \fIRESOLUTION\fR] [\fB-g\fR \fIGOROUTINES\fR] \fIURLs...\fR +.br +crunchyroll-go update [\fB-i\fR \fIINSTALL\fR] .SH DESCRIPTION .TP @@ -141,6 +143,13 @@ The video resolution. Can either be specified via the pixels (e.g. 1920x1080), t \fB-g, --goroutines GOROUTINES\fR Sets the number of parallel downloads for the segments the final video is made of. Default is the number of cores the computer has. +.SH UPDATE COMMAND +Checks if a newer version is available. +.TP + +\fB-i, --install INSTALL\fR +If given, the command tries to update the executable with the newer version (if a newer is available). + .SH URL OPTIONS If you want to download only specific episode of a series, you could either pass every single episode url to the downloader (which is fine for 1 - 3 episodes) or use filtering. It works pretty simple, just put a specific pattern surrounded by square brackets at the end of the url from the anime you want to download. A season and / or episode as well as a range from where to where episodes should be downloaded can be specified. From 608e03bc11925c9bb78ad6a0b4efbf4137a34c6e Mon Sep 17 00:00:00 2001 From: bytedream Date: Wed, 18 May 2022 22:21:49 +0200 Subject: [PATCH 02/12] Add better update output --- cmd/crunchyroll-go/cmd/update.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmd/crunchyroll-go/cmd/update.go b/cmd/crunchyroll-go/cmd/update.go index 4c7aaad..c8512e6 100644 --- a/cmd/crunchyroll-go/cmd/update.go +++ b/cmd/crunchyroll-go/cmd/update.go @@ -63,10 +63,12 @@ func update() error { return fmt.Errorf("internal version (%s) is not parsable", Version) } + out.Info("Installed version is %s", Version) + var hasUpdate bool for i := 0; i < 3; i++ { if latestRelease[i] < internalVersion[i] { - out.Info("Local version (%s) is newer than version in latest release (%s)", Version, releaseVersion) + out.Info("Local version is newer than version in latest release (%s)", releaseVersion) return nil } else if latestRelease[i] > internalVersion[i] { hasUpdate = true @@ -78,7 +80,7 @@ func update() error { return nil } - out.Info("A new version is available (%s). Installed version is %s: https://github.com/ByteDream/crunchyroll-go/releases/tag/v%s", releaseVersion, Version, releaseVersion) + out.Info("A new version is available (%s): https://github.com/ByteDream/crunchyroll-go/releases/tag/v%s", releaseVersion, releaseVersion) if updateInstallFlag { if runtime.GOARCH != "amd64" { From b78d6a7871f4f177fdeb6729a2a011a3f48b7747 Mon Sep 17 00:00:00 2001 From: bytedream Date: Sat, 21 May 2022 00:29:03 +0200 Subject: [PATCH 03/12] Change default Makefile version variable to development --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3dd5c3e..9b96538 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -VERSION=2.2.2 +VERSION=development BINARY_NAME=crunchy VERSION_BINARY_NAME=$(BINARY_NAME)-v$(VERSION) From eb2414d0120bd1067b9324d4b4c31d4dcea9a568 Mon Sep 17 00:00:00 2001 From: bytedream Date: Sat, 21 May 2022 21:38:45 +0200 Subject: [PATCH 04/12] Update module version to v3 --- Makefile | 8 ++++---- README.md | 4 ++-- cmd/crunchyroll-go/cmd/archive.go | 4 ++-- cmd/crunchyroll-go/cmd/download.go | 4 ++-- cmd/crunchyroll-go/cmd/login.go | 2 +- cmd/crunchyroll-go/cmd/root.go | 2 +- cmd/crunchyroll-go/cmd/utils.go | 4 ++-- cmd/crunchyroll-go/main.go | 2 +- go.mod | 2 +- utils/locale.go | 2 +- utils/sort.go | 2 +- 11 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Makefile b/Makefile index 9b96538..a747c1d 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ DESTDIR= PREFIX=/usr build: - go build -ldflags "-X 'github.com/ByteDream/crunchyroll-go/v2/cmd/crunchyroll-go/cmd.Version=$(VERSION)'" -o $(BINARY_NAME) cmd/crunchyroll-go/main.go + go build -ldflags "-X 'github.com/ByteDream/crunchyroll-go/v3/cmd/crunchyroll-go/cmd.Version=$(VERSION)'" -o $(BINARY_NAME) cmd/crunchyroll-go/main.go clean: rm -f $(BINARY_NAME) $(VERSION_BINARY_NAME)_* @@ -24,8 +24,8 @@ uninstall: rm -f $(DESTDIR)$(PREFIX)/share/licenses/crunchyroll-go/LICENSE release: - CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-X 'github.com/ByteDream/crunchyroll-go/v2/cmd/crunchyroll-go/cmd.Version=$(VERSION)'" -o $(VERSION_BINARY_NAME)_linux cmd/crunchyroll-go/main.go - CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "-X 'github.com/ByteDream/crunchyroll-go/v2/cmd/crunchyroll-go/cmd.Version=$(VERSION)'" -o $(VERSION_BINARY_NAME)_windows.exe cmd/crunchyroll-go/main.go - CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "-X 'github.com/ByteDream/crunchyroll-go/v2/cmd/crunchyroll-go/cmd.Version=$(VERSION)'" -o $(VERSION_BINARY_NAME)_darwin cmd/crunchyroll-go/main.go + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-X 'github.com/ByteDream/crunchyroll-go/v3/cmd/crunchyroll-go/cmd.Version=$(VERSION)'" -o $(VERSION_BINARY_NAME)_linux cmd/crunchyroll-go/main.go + CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "-X 'github.com/ByteDream/crunchyroll-go/v3/cmd/crunchyroll-go/cmd.Version=$(VERSION)'" -o $(VERSION_BINARY_NAME)_windows.exe cmd/crunchyroll-go/main.go + CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "-X 'github.com/ByteDream/crunchyroll-go/v3/cmd/crunchyroll-go/cmd.Version=$(VERSION)'" -o $(VERSION_BINARY_NAME)_darwin cmd/crunchyroll-go/main.go strip $(VERSION_BINARY_NAME)_linux diff --git a/README.md b/README.md index c83493c..f875018 100644 --- a/README.md +++ b/README.md @@ -205,10 +205,10 @@ These flags you can use across every sub-command: Download the library via `go get` ```shell -$ go get github.com/ByteDream/crunchyroll-go/v2 +$ go get github.com/ByteDream/crunchyroll-go/v3 ``` -The documentation is available on [pkg.go.dev](https://pkg.go.dev/github.com/ByteDream/crunchyroll-go/v2). +The documentation is available on [pkg.go.dev](https://pkg.go.dev/github.com/ByteDream/crunchyroll-go/v3). Examples how to use the library and some features of it are described in the [wiki](https://github.com/ByteDream/crunchyroll-go/wiki/Library). diff --git a/cmd/crunchyroll-go/cmd/archive.go b/cmd/crunchyroll-go/cmd/archive.go index 6dcf61b..d4a7e48 100644 --- a/cmd/crunchyroll-go/cmd/archive.go +++ b/cmd/crunchyroll-go/cmd/archive.go @@ -8,8 +8,8 @@ import ( "compress/gzip" "context" "fmt" - "github.com/ByteDream/crunchyroll-go/v2" - "github.com/ByteDream/crunchyroll-go/v2/utils" + "github.com/ByteDream/crunchyroll-go/v3" + "github.com/ByteDream/crunchyroll-go/v3/utils" "github.com/grafov/m3u8" "github.com/spf13/cobra" "io" diff --git a/cmd/crunchyroll-go/cmd/download.go b/cmd/crunchyroll-go/cmd/download.go index 92b6027..3dddf48 100644 --- a/cmd/crunchyroll-go/cmd/download.go +++ b/cmd/crunchyroll-go/cmd/download.go @@ -3,8 +3,8 @@ package cmd import ( "context" "fmt" - "github.com/ByteDream/crunchyroll-go/v2" - "github.com/ByteDream/crunchyroll-go/v2/utils" + "github.com/ByteDream/crunchyroll-go/v3" + "github.com/ByteDream/crunchyroll-go/v3/utils" "github.com/grafov/m3u8" "github.com/spf13/cobra" "math" diff --git a/cmd/crunchyroll-go/cmd/login.go b/cmd/crunchyroll-go/cmd/login.go index c9fc923..4bee3b0 100644 --- a/cmd/crunchyroll-go/cmd/login.go +++ b/cmd/crunchyroll-go/cmd/login.go @@ -2,7 +2,7 @@ package cmd import ( "fmt" - "github.com/ByteDream/crunchyroll-go/v2" + "github.com/ByteDream/crunchyroll-go/v3" "github.com/spf13/cobra" "os" "path/filepath" diff --git a/cmd/crunchyroll-go/cmd/root.go b/cmd/crunchyroll-go/cmd/root.go index 82ee133..02873f1 100644 --- a/cmd/crunchyroll-go/cmd/root.go +++ b/cmd/crunchyroll-go/cmd/root.go @@ -3,7 +3,7 @@ package cmd import ( "context" "fmt" - "github.com/ByteDream/crunchyroll-go/v2" + "github.com/ByteDream/crunchyroll-go/v3" "github.com/spf13/cobra" "net/http" "os" diff --git a/cmd/crunchyroll-go/cmd/utils.go b/cmd/crunchyroll-go/cmd/utils.go index 2a632ff..2fddd60 100644 --- a/cmd/crunchyroll-go/cmd/utils.go +++ b/cmd/crunchyroll-go/cmd/utils.go @@ -2,8 +2,8 @@ package cmd import ( "fmt" - "github.com/ByteDream/crunchyroll-go/v2" - "github.com/ByteDream/crunchyroll-go/v2/utils" + "github.com/ByteDream/crunchyroll-go/v3" + "github.com/ByteDream/crunchyroll-go/v3/utils" "net/http" "net/url" "os" diff --git a/cmd/crunchyroll-go/main.go b/cmd/crunchyroll-go/main.go index a502afb..0ced8aa 100644 --- a/cmd/crunchyroll-go/main.go +++ b/cmd/crunchyroll-go/main.go @@ -1,7 +1,7 @@ package main import ( - "github.com/ByteDream/crunchyroll-go/v2/cmd/crunchyroll-go/cmd" + "github.com/ByteDream/crunchyroll-go/v3/cmd/crunchyroll-go/cmd" ) func main() { diff --git a/go.mod b/go.mod index 9a38468..5c64ddf 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/ByteDream/crunchyroll-go/v2 +module github.com/ByteDream/crunchyroll-go/v3 go 1.18 diff --git a/utils/locale.go b/utils/locale.go index 537b165..85a8650 100644 --- a/utils/locale.go +++ b/utils/locale.go @@ -1,7 +1,7 @@ package utils import ( - "github.com/ByteDream/crunchyroll-go/v2" + "github.com/ByteDream/crunchyroll-go/v3" ) // AllLocales is an array of all available locales. diff --git a/utils/sort.go b/utils/sort.go index a44717d..eacb96b 100644 --- a/utils/sort.go +++ b/utils/sort.go @@ -1,7 +1,7 @@ package utils import ( - "github.com/ByteDream/crunchyroll-go/v2" + "github.com/ByteDream/crunchyroll-go/v3" "sort" "strconv" "strings" From 638689ee327f4b5df4571baa0afd9763b222a9ee Mon Sep 17 00:00:00 2001 From: bytedream Date: Fri, 27 May 2022 15:13:15 +0200 Subject: [PATCH 05/12] Add new login method & deprecated login with session id --- crunchyroll.go | 298 +++++++++++++++++++++++++------------------------ 1 file changed, 151 insertions(+), 147 deletions(-) diff --git a/crunchyroll.go b/crunchyroll.go index 1dafee7..0aa4a2e 100644 --- a/crunchyroll.go +++ b/crunchyroll.go @@ -10,6 +10,7 @@ import ( "net/url" "regexp" "strconv" + "strings" ) // LOCALE represents a locale / language. @@ -36,14 +37,17 @@ type Crunchyroll struct { Context context.Context // Locale specifies in which language all results should be returned / requested. Locale LOCALE - // SessionID is the crunchyroll session id which was used for authentication. - SessionID string + // EtpRt is the crunchyroll beta equivalent to a session id (prior SessionID field in + // this struct in v2 and below). + EtpRt string // Config stores parameters which are needed by some api calls. Config struct { TokenType string AccessToken string + Bucket string + CountryCode string Premium bool Channel string @@ -59,101 +63,38 @@ type Crunchyroll struct { cache bool } -// LoginWithCredentials logs in via crunchyroll username or email and password. -func LoginWithCredentials(user string, password string, locale LOCALE, client *http.Client) (*Crunchyroll, error) { - sessionIDEndpoint := fmt.Sprintf("https://api.crunchyroll.com/start_session.0.json?version=1.0&access_token=%s&device_type=%s&device_id=%s", - "LNDJgOit5yaRIWN", "com.crunchyroll.windows.desktop", "Az2srGnChW65fuxYz2Xxl1GcZQgtGgI") - sessResp, err := client.Get(sessionIDEndpoint) - if err != nil { - return nil, err - } - defer sessResp.Body.Close() - - if sessResp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("failed to start session for credentials login: %s", sessResp.Status) - } - - var data map[string]interface{} - body, _ := io.ReadAll(sessResp.Body) - if err = json.Unmarshal(body, &data); err != nil { - return nil, fmt.Errorf("failed to parse start session with credentials response: %w", err) - } - - sessionID := data["data"].(map[string]interface{})["session_id"].(string) - - loginEndpoint := "https://api.crunchyroll.com/login.0.json" - authValues := url.Values{} - authValues.Set("session_id", sessionID) - authValues.Set("account", user) - authValues.Set("password", password) - loginResp, err := client.Post(loginEndpoint, "application/x-www-form-urlencoded", bytes.NewBufferString(authValues.Encode())) - if err != nil { - return nil, err - } - defer loginResp.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"]) - } - } - - return LoginWithSessionID(sessionID, locale, client) +type loginResponse struct { + AccessToken string `json:"access_token"` + ExpiresIn int `json:"expires_in"` + TokenType string `json:"token_type"` + Scope string `json:"scope"` + Country string `json:"country"` + AccountID string `json:"account_id"` } -// LoginWithSessionID logs in via a crunchyroll session id. -// Session ids are automatically generated as a cookie when visiting https://www.crunchyroll.com. -func LoginWithSessionID(sessionID string, locale LOCALE, client *http.Client) (*Crunchyroll, error) { - crunchy := &Crunchyroll{ - Client: client, - Context: context.Background(), - Locale: locale, - SessionID: sessionID, - cache: true, - } - var endpoint string - var err error - var resp *http.Response - var jsonBody map[string]interface{} +// LoginWithCredentials logs in via crunchyroll username or email and password. +func LoginWithCredentials(user string, password string, locale LOCALE, client *http.Client) (*Crunchyroll, error) { + endpoint := "https://beta-api.crunchyroll.com/auth/v1/token" + values := url.Values{} + values.Set("username", user) + values.Set("password", password) + values.Set("grant_type", "password") - // start session - endpoint = fmt.Sprintf("https://api.crunchyroll.com/start_session.0.json?session_id=%s", - sessionID) - resp, err = client.Get(endpoint) + req, err := http.NewRequest(http.MethodPost, endpoint, bytes.NewBufferString(values.Encode())) + if err != nil { + return nil, err + } + req.Header.Set("Authorization", "Basic aHJobzlxM2F3dnNrMjJ1LXRzNWE6cHROOURteXRBU2Z6QjZvbXVsSzh6cUxzYTczVE1TY1k=") + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + resp, err := request(req, client) if err != nil { return nil, err } defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("failed to start session: %s", resp.Status) - } - - 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, fmt.Errorf("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" - } + var loginResp loginResponse + json.NewDecoder(resp.Body).Decode(&loginResp) var etpRt string for _, cookie := range resp.Cookies() { @@ -163,83 +104,164 @@ func LoginWithSessionID(sessionID string, locale LOCALE, client *http.Client) (* } } - // token - endpoint = "https://beta-api.crunchyroll.com/auth/v1/token" + return postLogin(loginResp, etpRt, locale, client) +} + +// LoginWithSessionID logs in via a crunchyroll session id. +// Session ids are automatically generated as a cookie when visiting https://www.crunchyroll.com. +// +// Deprecated: Login via session id caused some trouble in the past (e.g. #15 or #30) which resulted in +// login not working. Use LoginWithEtpRt instead. EtpRt practically the crunchyroll beta equivalent to +// a session id. +// The method will stay in the library until session id login is removed completely or login with it +// does not work for a longer period of time. +func LoginWithSessionID(sessionID string, locale LOCALE, client *http.Client) (*Crunchyroll, error) { + endpoint := fmt.Sprintf("https://api.crunchyroll.com/start_session.0.json?session_id=%s", + sessionID) + resp, err := client.Get(endpoint) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + var jsonBody map[string]any + json.NewDecoder(resp.Body).Decode(&jsonBody) + + var etpRt string + for _, cookie := range resp.Cookies() { + if cookie.Name == "etp_rt" { + etpRt = cookie.Value + break + } + } + + return LoginWithEtpRt(etpRt, locale, client) +} + +// LoginWithEtpRt logs in via the crunchyroll etp rt cookie. This cookie is the crunchyroll beta +// equivalent to the classic session id. +// The etp_rt cookie is automatically set when visiting https://beta.crunchyroll.com. Note that you +// need a crunchyroll account to access it. +func LoginWithEtpRt(etpRt string, locale LOCALE, client *http.Client) (*Crunchyroll, error) { + endpoint := "https://beta-api.crunchyroll.com/auth/v1/token" grantType := url.Values{} grantType.Set("grant_type", "etp_rt_cookie") - authRequest, err := http.NewRequest(http.MethodPost, endpoint, bytes.NewBufferString(grantType.Encode())) + req, err := http.NewRequest(http.MethodPost, endpoint, bytes.NewBufferString(grantType.Encode())) if err != nil { return nil, err } - authRequest.Header.Add("Authorization", "Basic bm9haWhkZXZtXzZpeWcwYThsMHE6") - authRequest.Header.Add("Content-Type", "application/x-www-form-urlencoded") - authRequest.AddCookie(&http.Cookie{ - Name: "session_id", - Value: sessionID, - }) - authRequest.AddCookie(&http.Cookie{ + req.Header.Add("Authorization", "Basic bm9haWhkZXZtXzZpeWcwYThsMHE6") + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + req.AddCookie(&http.Cookie{ Name: "etp_rt", Value: etpRt, }) + resp, err := request(req, client) + if err != nil { + return nil, err + } - resp, err = client.Do(authRequest) + var loginResp loginResponse + json.NewDecoder(resp.Body).Decode(&loginResp) + + return postLogin(loginResp, etpRt, locale, client) +} + +func postLogin(loginResp loginResponse, etpRt string, locale LOCALE, client *http.Client) (*Crunchyroll, error) { + crunchy := &Crunchyroll{ + Client: client, + Context: context.Background(), + Locale: locale, + EtpRt: etpRt, + cache: true, + } + + crunchy.Config.TokenType = loginResp.TokenType + crunchy.Config.AccessToken = loginResp.AccessToken + crunchy.Config.AccountID = loginResp.AccountID + + var jsonBody map[string]any + + endpoint := "https://beta-api.crunchyroll.com/index/v2" + resp, err := crunchy.request(endpoint) if err != nil { return nil, err } defer resp.Body.Close() - if err = json.NewDecoder(resp.Body).Decode(&jsonBody); err != nil { - return nil, fmt.Errorf("failed to parse 'token' response: %w", err) + json.NewDecoder(resp.Body).Decode(&jsonBody) + 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 = "-" } - crunchy.Config.TokenType = jsonBody["token_type"].(string) - crunchy.Config.AccessToken = jsonBody["access_token"].(string) - - // index - endpoint = "https://beta-api.crunchyroll.com/index/v2" - resp, err = crunchy.request(endpoint) - if err != nil { - return nil, err - } - defer resp.Body.Close() - if err = json.NewDecoder(resp.Body).Decode(&jsonBody); err != nil { - return nil, fmt.Errorf("failed to parse 'index' response: %w", err) - } - cms := jsonBody["cms"].(map[string]interface{}) - crunchy.Config.Policy = cms["policy"].(string) crunchy.Config.Signature = cms["signature"].(string) crunchy.Config.KeyPairID = cms["key_pair_id"].(string) - // me endpoint = "https://beta-api.crunchyroll.com/accounts/v1/me" resp, err = crunchy.request(endpoint) if err != nil { return nil, err } defer resp.Body.Close() - if err = json.NewDecoder(resp.Body).Decode(&jsonBody); err != nil { - return nil, fmt.Errorf("failed to parse 'me' response: %w", err) - } - - crunchy.Config.AccountID = jsonBody["account_id"].(string) + json.NewDecoder(resp.Body).Decode(&jsonBody) crunchy.Config.ExternalID = jsonBody["external_id"].(string) - //profile endpoint = "https://beta-api.crunchyroll.com/accounts/v1/me/profile" resp, err = crunchy.request(endpoint) if err != nil { return nil, err } defer resp.Body.Close() - if err = json.NewDecoder(resp.Body).Decode(&jsonBody); err != nil { - return nil, fmt.Errorf("failed to parse 'profile' response: %w", err) - } - + json.NewDecoder(resp.Body).Decode(&jsonBody) crunchy.Config.MaturityRating = jsonBody["maturity_rating"].(string) return crunchy, nil } +func request(req *http.Request, client *http.Client) (*http.Response, error) { + resp, err := client.Do(req) + if err == nil { + var buf bytes.Buffer + io.Copy(&buf, resp.Body) + defer resp.Body.Close() + defer func() { + resp.Body = io.NopCloser(&buf) + }() + + if buf.Len() != 0 { + var errMap map[string]any + + if err = json.Unmarshal(buf.Bytes(), &errMap); err != nil { + return nil, fmt.Errorf("invalid json response: %w", err) + } + + if val, ok := errMap["error"]; ok { + if errorAsString, ok := val.(string); ok { + if code, ok := errMap["code"].(string); ok { + return nil, fmt.Errorf("error for endpoint %s (%d): %s - %s", req.URL.String(), resp.StatusCode, errorAsString, code) + } + return nil, fmt.Errorf("error for endpoint %s (%d): %s", req.URL.String(), resp.StatusCode, errorAsString) + } else if errorAsBool, ok := val.(bool); ok && errorAsBool { + if msg, ok := errMap["message"].(string); ok { + return nil, fmt.Errorf("error for endpoint %s (%d): %s", req.URL.String(), resp.StatusCode, msg) + } + } + } + } + + if resp.StatusCode >= 400 { + return nil, fmt.Errorf("error for endpoint %s: %s", req.URL.String(), resp.Status) + } + } + return resp, err +} + // request is a base function which handles api requests. func (c *Crunchyroll) request(endpoint string) (*http.Response, error) { req, err := http.NewRequest(http.MethodGet, endpoint, nil) @@ -248,25 +270,7 @@ func (c *Crunchyroll) request(endpoint string) (*http.Response, error) { } req.Header.Add("Authorization", fmt.Sprintf("%s %s", c.Config.TokenType, c.Config.AccessToken)) - resp, err := c.Client.Do(req) - if err == nil { - defer resp.Body.Close() - bodyAsBytes, _ := io.ReadAll(resp.Body) - defer resp.Body.Close() - if resp.StatusCode == http.StatusUnauthorized { - return nil, fmt.Errorf("invalid access token") - } else { - var errStruct struct { - Message string `json:"message"` - } - json.NewDecoder(bytes.NewBuffer(bodyAsBytes)).Decode(&errStruct) - if errStruct.Message != "" { - return nil, fmt.Errorf(errStruct.Message) - } - } - resp.Body = io.NopCloser(bytes.NewBuffer(bodyAsBytes)) - } - return resp, err + return request(req, c.Client) } // IsCaching returns if data gets cached or not. From c94ce0fb59dddb29bcbce4ad7e1e5b2dc1814b84 Mon Sep 17 00:00:00 2001 From: bytedream Date: Fri, 27 May 2022 15:58:28 +0200 Subject: [PATCH 06/12] Fix country code not set --- crunchyroll.go | 1 + 1 file changed, 1 insertion(+) diff --git a/crunchyroll.go b/crunchyroll.go index 0aa4a2e..06a28ec 100644 --- a/crunchyroll.go +++ b/crunchyroll.go @@ -180,6 +180,7 @@ func postLogin(loginResp loginResponse, etpRt string, locale LOCALE, client *htt crunchy.Config.TokenType = loginResp.TokenType crunchy.Config.AccessToken = loginResp.AccessToken crunchy.Config.AccountID = loginResp.AccountID + crunchy.Config.CountryCode = loginResp.Country var jsonBody map[string]any From 0780a2a2bceb42d83fb533540de247718585e6a9 Mon Sep 17 00:00:00 2001 From: bytedream Date: Fri, 27 May 2022 15:59:00 +0200 Subject: [PATCH 07/12] Change login type from session id to etp rt --- cmd/crunchyroll-go/cmd/login.go | 46 ++++++++++++++++++++++++++++++--- cmd/crunchyroll-go/cmd/utils.go | 14 +++++----- 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/cmd/crunchyroll-go/cmd/login.go b/cmd/crunchyroll-go/cmd/login.go index 4bee3b0..28a784e 100644 --- a/cmd/crunchyroll-go/cmd/login.go +++ b/cmd/crunchyroll-go/cmd/login.go @@ -12,6 +12,7 @@ var ( loginPersistentFlag bool loginSessionIDFlag bool + loginEtpRtFlag bool ) var loginCmd = &cobra.Command{ @@ -22,6 +23,8 @@ var loginCmd = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { if loginSessionIDFlag { return loginSessionID(args[0]) + } else if loginEtpRtFlag { + return loginEtpRt(args[0]) } else { return loginCredentials(args[0], args[1]) } @@ -38,6 +41,10 @@ func init() { "session-id", false, "Use a session id to login instead of username and password") + loginCmd.Flags().BoolVar(&loginEtpRtFlag, + "etp-rt", + false, + "Use a etp rt cookie to login instead of username and password") rootCmd.AddCommand(loginCmd) } @@ -60,7 +67,7 @@ func loginCredentials(user, password string) error { 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 { + if err = os.WriteFile(filepath.Join(os.TempDir(), ".crunchy"), []byte(c.EtpRt), 0600); err != nil { return err } @@ -73,7 +80,38 @@ func loginCredentials(user, password string) error { func loginSessionID(sessionID string) error { out.Debug("Logging in via session id") - if _, err := crunchyroll.LoginWithSessionID(sessionID, systemLocale(false), client); err != nil { + var c *crunchyroll.Crunchyroll + var err error + if c, err = crunchyroll.LoginWithSessionID(sessionID, systemLocale(false), client); err != nil { + out.Err(err.Error()) + os.Exit(1) + } + + if loginPersistentFlag { + if configDir, err := os.UserConfigDir(); err != nil { + return fmt.Errorf("could not save credentials persistent: %w", err) + } else { + os.MkdirAll(filepath.Join(configDir, "crunchyroll-go"), 0755) + if err = os.WriteFile(filepath.Join(configDir, "crunchyroll-go", "crunchy"), []byte(c.EtpRt), 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 err = os.WriteFile(filepath.Join(os.TempDir(), ".crunchy"), []byte(c.EtpRt), 0600); err != nil { + return err + } + + if !loginPersistentFlag { + out.Info("Due to security reasons, you have to login again on the next reboot") + } + + return nil +} + +func loginEtpRt(etpRt string) error { + out.Debug("Logging in via etp rt") + if _, err := crunchyroll.LoginWithEtpRt(etpRt, systemLocale(false), client); err != nil { out.Err(err.Error()) os.Exit(1) } @@ -84,13 +122,13 @@ func loginSessionID(sessionID string) error { return fmt.Errorf("could not save credentials persistent: %w", err) } else { os.MkdirAll(filepath.Join(configDir, "crunchyroll-go"), 0755) - if err = os.WriteFile(filepath.Join(configDir, "crunchyroll-go", "crunchy"), []byte(sessionID), 0600); err != nil { + if err = os.WriteFile(filepath.Join(configDir, "crunchyroll-go", "crunchy"), []byte(etpRt), 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 err = os.WriteFile(filepath.Join(os.TempDir(), ".crunchy"), []byte(sessionID), 0600); err != nil { + if err = os.WriteFile(filepath.Join(os.TempDir(), ".crunchy"), []byte(etpRt), 0600); err != nil { return err } diff --git a/cmd/crunchyroll-go/cmd/utils.go b/cmd/crunchyroll-go/cmd/utils.go index 2fddd60..86691ab 100644 --- a/cmd/crunchyroll-go/cmd/utils.go +++ b/cmd/crunchyroll-go/cmd/utils.go @@ -147,10 +147,10 @@ func loadCrunchy() { out.StopProgress("Failed to read login information: %v", err) os.Exit(1) } - if crunchy, err = crunchyroll.LoginWithSessionID(url.QueryEscape(string(body)), systemLocale(true), client); err != nil { - out.Debug("Failed to login with temp session id: %w", err) + if crunchy, err = crunchyroll.LoginWithEtpRt(url.QueryEscape(string(body)), systemLocale(true), client); err != nil { + out.Debug("Failed to login with temp etp rt: %w", err) } else { - out.Debug("Logged in with session id %s. BLANK THIS LINE OUT IF YOU'RE ASKED TO POST THE DEBUG OUTPUT SOMEWHERE", body) + out.Debug("Logged in with etp rt %s. BLANK THIS LINE OUT IF YOU'RE ASKED TO POST THE DEBUG OUTPUT SOMEWHERE", body) out.StopProgress("Logged in") return @@ -168,20 +168,20 @@ 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 { + if crunchy, err = crunchyroll.LoginWithEtpRt(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 etp rt %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 session id %s. BLANK THIS LINE OUT IF YOU'RE ASKED TO POST THE DEBUG OUTPUT SOMEWHERE", crunchy.SessionID) + 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 session id 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.SessionID), 0600) + os.WriteFile(filepath.Join(os.TempDir(), ".crunchy"), []byte(crunchy.EtpRt), 0600) } out.StopProgress("Logged in") return From b4ba8c45996eeb3fce5484cd7af23199b868bff6 Mon Sep 17 00:00:00 2001 From: bytedream Date: Fri, 27 May 2022 16:00:08 +0200 Subject: [PATCH 08/12] Bump go CI version to 1.18 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 44c7ac6..7c54dd3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v3 with: - go-version: 1.16 + go-version: 1.18 - name: Build run: go build -v cmd/crunchyroll-go/main.go From 15373ed7d6b689e900ba3c44cf35376048bed679 Mon Sep 17 00:00:00 2001 From: bytedream Date: Fri, 27 May 2022 16:03:37 +0200 Subject: [PATCH 09/12] Fix typo --- cmd/crunchyroll-go/cmd/utils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/crunchyroll-go/cmd/utils.go b/cmd/crunchyroll-go/cmd/utils.go index 86691ab..aa98399 100644 --- a/cmd/crunchyroll-go/cmd/utils.go +++ b/cmd/crunchyroll-go/cmd/utils.go @@ -179,7 +179,7 @@ func loadCrunchy() { 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 session id is written to a temp file to reduce the amount of re-logging in. + // 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) } From 6581a5bd0f14f0601c2e4b84478bad84df74998d Mon Sep 17 00:00:00 2001 From: bytedream Date: Sun, 29 May 2022 16:10:37 +0200 Subject: [PATCH 10/12] Fix typo --- cmd/crunchyroll-go/cmd/utils.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/crunchyroll-go/cmd/utils.go b/cmd/crunchyroll-go/cmd/utils.go index aa98399..0a89921 100644 --- a/cmd/crunchyroll-go/cmd/utils.go +++ b/cmd/crunchyroll-go/cmd/utils.go @@ -148,9 +148,9 @@ func loadCrunchy() { os.Exit(1) } if crunchy, err = crunchyroll.LoginWithEtpRt(url.QueryEscape(string(body)), systemLocale(true), client); err != nil { - out.Debug("Failed to login with temp etp rt: %w", err) + out.Debug("Failed to login with temp etp rt cookie: %w", err) } else { - out.Debug("Logged in with etp rt %s. BLANK THIS LINE OUT IF YOU'RE ASKED TO POST THE DEBUG OUTPUT SOMEWHERE", body) + out.Debug("Logged in with etp rt cookie %s. BLANK THIS LINE OUT IF YOU'RE ASKED TO POST THE DEBUG OUTPUT SOMEWHERE", body) out.StopProgress("Logged in") return @@ -172,7 +172,7 @@ func loadCrunchy() { out.StopProgress(err.Error()) os.Exit(1) } - out.Debug("Logged in with etp rt %s. BLANK THIS LINE OUT IF YOU'RE ASKED TO POST THE DEBUG OUTPUT SOMEWHERE", split[0]) + 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()) From 2471042d02b743e9925493e2b69e78c237a70ecb Mon Sep 17 00:00:00 2001 From: bytedream Date: Mon, 30 May 2022 08:56:30 +0200 Subject: [PATCH 11/12] Change error formatter for non Errorf --- cmd/crunchyroll-go/cmd/utils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/crunchyroll-go/cmd/utils.go b/cmd/crunchyroll-go/cmd/utils.go index 0a89921..5c4a36a 100644 --- a/cmd/crunchyroll-go/cmd/utils.go +++ b/cmd/crunchyroll-go/cmd/utils.go @@ -148,7 +148,7 @@ func loadCrunchy() { os.Exit(1) } if crunchy, err = crunchyroll.LoginWithEtpRt(url.QueryEscape(string(body)), systemLocale(true), client); err != nil { - out.Debug("Failed to login with temp etp rt cookie: %w", err) + 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) From 1c37c3e699e033f2eedf41ca8326c125ef826319 Mon Sep 17 00:00:00 2001 From: bytedream Date: Mon, 30 May 2022 12:08:52 +0200 Subject: [PATCH 12/12] Add method parameter to internal request function --- crunchyroll.go | 12 ++++++------ episode.go | 3 ++- movie_listing.go | 5 +++-- season.go | 3 ++- stream.go | 3 ++- video.go | 9 +++++---- 6 files changed, 20 insertions(+), 15 deletions(-) diff --git a/crunchyroll.go b/crunchyroll.go index 06a28ec..bdffa27 100644 --- a/crunchyroll.go +++ b/crunchyroll.go @@ -185,7 +185,7 @@ func postLogin(loginResp loginResponse, etpRt string, locale LOCALE, client *htt var jsonBody map[string]any endpoint := "https://beta-api.crunchyroll.com/index/v2" - resp, err := crunchy.request(endpoint) + resp, err := crunchy.request(endpoint, http.MethodGet) if err != nil { return nil, err } @@ -205,7 +205,7 @@ func postLogin(loginResp loginResponse, etpRt string, locale LOCALE, client *htt crunchy.Config.KeyPairID = cms["key_pair_id"].(string) endpoint = "https://beta-api.crunchyroll.com/accounts/v1/me" - resp, err = crunchy.request(endpoint) + resp, err = crunchy.request(endpoint, http.MethodGet) if err != nil { return nil, err } @@ -214,7 +214,7 @@ func postLogin(loginResp loginResponse, etpRt string, locale LOCALE, client *htt crunchy.Config.ExternalID = jsonBody["external_id"].(string) endpoint = "https://beta-api.crunchyroll.com/accounts/v1/me/profile" - resp, err = crunchy.request(endpoint) + resp, err = crunchy.request(endpoint, http.MethodGet) if err != nil { return nil, err } @@ -264,8 +264,8 @@ func request(req *http.Request, client *http.Client) (*http.Response, error) { } // request is a base function which handles api requests. -func (c *Crunchyroll) request(endpoint string) (*http.Response, error) { - req, err := http.NewRequest(http.MethodGet, endpoint, nil) +func (c *Crunchyroll) request(endpoint string, method string) (*http.Response, error) { + req, err := http.NewRequest(method, endpoint, nil) if err != nil { return nil, err } @@ -292,7 +292,7 @@ func (c *Crunchyroll) SetCaching(caching bool) { func (c *Crunchyroll) Search(query string, limit uint) (s []*Series, m []*Movie, err error) { searchEndpoint := fmt.Sprintf("https://beta-api.crunchyroll.com/content/v1/search?q=%s&n=%d&type=&locale=%s", query, limit, c.Locale) - resp, err := c.request(searchEndpoint) + resp, err := c.request(searchEndpoint, http.MethodGet) if err != nil { return nil, nil, err } diff --git a/episode.go b/episode.go index a25844c..e9dd16a 100644 --- a/episode.go +++ b/episode.go @@ -3,6 +3,7 @@ package crunchyroll import ( "encoding/json" "fmt" + "net/http" "regexp" "strconv" "strings" @@ -85,7 +86,7 @@ func EpisodeFromID(crunchy *Crunchyroll, id string) (*Episode, error) { crunchy.Locale, crunchy.Config.Signature, crunchy.Config.Policy, - crunchy.Config.KeyPairID)) + crunchy.Config.KeyPairID), http.MethodGet) if err != nil { return nil, err } diff --git a/movie_listing.go b/movie_listing.go index 63d7fab..110f67a 100644 --- a/movie_listing.go +++ b/movie_listing.go @@ -3,6 +3,7 @@ package crunchyroll import ( "encoding/json" "fmt" + "net/http" ) // MovieListing contains information about something which is called @@ -48,7 +49,7 @@ func MovieListingFromID(crunchy *Crunchyroll, id string) (*MovieListing, error) crunchy.Locale, crunchy.Config.Signature, crunchy.Config.Policy, - crunchy.Config.KeyPairID)) + crunchy.Config.KeyPairID), http.MethodGet) if err != nil { return nil, err } @@ -77,7 +78,7 @@ func (ml *MovieListing) AudioLocale() (LOCALE, error) { ml.crunchy.Locale, ml.crunchy.Config.Signature, ml.crunchy.Config.Policy, - ml.crunchy.Config.KeyPairID)) + ml.crunchy.Config.KeyPairID), http.MethodGet) if err != nil { return "", err } diff --git a/season.go b/season.go index 825a816..f5513f5 100644 --- a/season.go +++ b/season.go @@ -3,6 +3,7 @@ package crunchyroll import ( "encoding/json" "fmt" + "net/http" "regexp" ) @@ -94,7 +95,7 @@ func (s *Season) Episodes() (episodes []*Episode, err error) { s.crunchy.Locale, s.crunchy.Config.Signature, s.crunchy.Config.Policy, - s.crunchy.Config.KeyPairID)) + s.crunchy.Config.KeyPairID), http.MethodGet) if err != nil { return nil, err } diff --git a/stream.go b/stream.go index 7505a24..00060ef 100644 --- a/stream.go +++ b/stream.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "github.com/grafov/m3u8" + "net/http" "regexp" ) @@ -72,7 +73,7 @@ func (s *Stream) Formats() ([]*Format, error) { // fromVideoStreams returns all streams which are accessible via the endpoint. func fromVideoStreams(crunchy *Crunchyroll, endpoint string) (streams []*Stream, err error) { - resp, err := crunchy.request(endpoint) + resp, err := crunchy.request(endpoint, http.MethodGet) if err != nil { return nil, err } diff --git a/video.go b/video.go index 00b7734..365f954 100644 --- a/video.go +++ b/video.go @@ -3,6 +3,7 @@ package crunchyroll import ( "encoding/json" "fmt" + "net/http" ) type video struct { @@ -77,7 +78,7 @@ func MovieFromID(crunchy *Crunchyroll, id string) (*Movie, error) { crunchy.Locale, crunchy.Config.Signature, crunchy.Config.Policy, - crunchy.Config.KeyPairID)) + crunchy.Config.KeyPairID), http.MethodGet) if err != nil { return nil, err } @@ -110,7 +111,7 @@ func (m *Movie) MovieListing() (movieListings []*MovieListing, err error) { m.crunchy.Locale, m.crunchy.Config.Signature, m.crunchy.Config.Policy, - m.crunchy.Config.KeyPairID)) + m.crunchy.Config.KeyPairID), http.MethodGet) if err != nil { return nil, err } @@ -173,7 +174,7 @@ func SeriesFromID(crunchy *Crunchyroll, id string) (*Series, error) { crunchy.Locale, crunchy.Config.Signature, crunchy.Config.Policy, - crunchy.Config.KeyPairID)) + crunchy.Config.KeyPairID), http.MethodGet) if err != nil { return nil, err } @@ -206,7 +207,7 @@ func (s *Series) Seasons() (seasons []*Season, err error) { s.crunchy.Locale, s.crunchy.Config.Signature, s.crunchy.Config.Policy, - s.crunchy.Config.KeyPairID)) + s.crunchy.Config.KeyPairID), http.MethodGet) if err != nil { return nil, err }