using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; using System.Threading.Tasks; using System.Web; using CRD.Utils; using CRD.Utils.Structs; using Newtonsoft.Json; using YamlDotNet.Core.Tokens; namespace CRD.Downloader; public class CrAuth{ private readonly Crunchyroll crunInstance = Crunchyroll.Instance; public async Task AuthAnonymous(){ var formData = new Dictionary<string, string>{ { "grant_type", "client_id" }, { "scope", "offline_access" } }; var requestContent = new FormUrlEncodedContent(formData); requestContent.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded"); var request = new HttpRequestMessage(HttpMethod.Post, Api.BetaAuth){ Content = requestContent }; request.Headers.Authorization = new AuthenticationHeaderValue("Basic", Api.authBasicSwitch); var response = await HttpClientReq.Instance.SendHttpRequest(request); if (response.IsOk){ JsonTokenToFileAndVariable(response.ResponseContent); } else{ Console.Error.WriteLine("Anonymous login failed"); } crunInstance.Profile = new CrProfile{ Username = "???", Avatar = "003-cr-hime-excited.png", PreferredContentAudioLanguage = "ja-JP", PreferredContentSubtitleLanguage = "de-DE" }; Crunchyroll.Instance.CmsToken = new CrCmsToken(); } private void JsonTokenToFileAndVariable(string content){ crunInstance.Token = JsonConvert.DeserializeObject<CrToken>(content, crunInstance.SettingsJsonSerializerSettings); if (crunInstance.Token != null && crunInstance.Token.expires_in != null){ crunInstance.Token.expires = DateTime.Now.AddMilliseconds((double)crunInstance.Token.expires_in); CfgManager.WriteTokenToYamlFile(crunInstance.Token, CfgManager.PathCrToken); } } public async Task Auth(AuthData data){ var formData = new Dictionary<string, string>{ { "username", data.Username }, { "password", data.Password }, { "grant_type", "password" }, { "scope", "offline_access" } }; var requestContent = new FormUrlEncodedContent(formData); requestContent.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded"); var request = new HttpRequestMessage(HttpMethod.Post, Api.BetaAuth){ Content = requestContent }; request.Headers.Authorization = new AuthenticationHeaderValue("Basic", Api.authBasicSwitch); var response = await HttpClientReq.Instance.SendHttpRequest(request); if (response.IsOk){ JsonTokenToFileAndVariable(response.ResponseContent); } if (crunInstance.Token?.refresh_token != null){ HttpClientReq.Instance.SetETPCookie(crunInstance.Token.refresh_token); await GetProfile(); } } public async Task GetProfile(){ if (crunInstance.Token?.access_token == null){ Console.Error.WriteLine("Missing Access Token"); return; } var request = HttpClientReq.CreateRequestMessage(Api.BetaProfile, HttpMethod.Get, true, true, null); var response = await HttpClientReq.Instance.SendHttpRequest(request); if (response.IsOk){ var profileTemp = Helpers.Deserialize<CrProfile>(response.ResponseContent, crunInstance.SettingsJsonSerializerSettings); if (profileTemp != null){ crunInstance.Profile = profileTemp; var requestSubs = HttpClientReq.CreateRequestMessage(Api.Subscription + crunInstance.Token.account_id, HttpMethod.Get, true, false, null); var responseSubs = await HttpClientReq.Instance.SendHttpRequest(requestSubs); if (responseSubs.IsOk){ var subsc = Helpers.Deserialize<Subscription>(responseSubs.ResponseContent, crunInstance.SettingsJsonSerializerSettings); crunInstance.Profile.Subscription = subsc; if ( subsc is{ SubscriptionProducts:{ Count: 0 }, ThirdPartySubscriptionProducts.Count: > 0 }){ var thirdPartySub = subsc.ThirdPartySubscriptionProducts.First(); var remaining = thirdPartySub.ExpirationDate - DateTime.UtcNow; crunInstance.Profile.HasPremium = remaining > TimeSpan.Zero; crunInstance.Profile.Subscription.IsActive = remaining > TimeSpan.Zero; crunInstance.Profile.Subscription.NextRenewalDate = thirdPartySub.ExpirationDate; } else if(subsc is{ SubscriptionProducts:{ Count: 0 }, NonrecurringSubscriptionProducts.Count: > 0 }){ var nonRecurringSub = subsc.NonrecurringSubscriptionProducts.First(); var remaining = nonRecurringSub.EndDate - DateTime.UtcNow; crunInstance.Profile.HasPremium = remaining > TimeSpan.Zero; crunInstance.Profile.Subscription.IsActive = remaining > TimeSpan.Zero; crunInstance.Profile.Subscription.NextRenewalDate = nonRecurringSub.EndDate; } else{ crunInstance.Profile.HasPremium = subsc.IsActive; } } else{ crunInstance.Profile.HasPremium = false; Console.Error.WriteLine("Failed to check premium subscription status"); } } } } public async void LoginWithToken(){ if (crunInstance.Token?.refresh_token == null){ Console.Error.WriteLine("Missing Refresh Token"); return; } var formData = new Dictionary<string, string>{ { "refresh_token", crunInstance.Token.refresh_token }, { "grant_type", "refresh_token" }, { "scope", "offline_access" } }; var requestContent = new FormUrlEncodedContent(formData); requestContent.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded"); var request = new HttpRequestMessage(HttpMethod.Post, Api.BetaAuth){ Content = requestContent }; request.Headers.Authorization = new AuthenticationHeaderValue("Basic", Api.authBasicSwitch); var response = await HttpClientReq.Instance.SendHttpRequest(request); if (response.IsOk){ JsonTokenToFileAndVariable(response.ResponseContent); } else{ Console.Error.WriteLine("Token Auth Failed"); } if (crunInstance.Token?.refresh_token != null){ HttpClientReq.Instance.SetETPCookie(crunInstance.Token.refresh_token); await GetProfile(); } await GetCmsToken(); } public async Task RefreshToken(bool needsToken){ if (crunInstance.Token?.access_token == null && crunInstance.Token?.refresh_token == null || crunInstance.Token.access_token != null && crunInstance.Token.refresh_token == null){ await AuthAnonymous(); } else{ if (!(DateTime.Now > crunInstance.Token.expires) && needsToken){ return; } } if (crunInstance.Profile.Username == "???"){ return; } var formData = new Dictionary<string, string>{ { "refresh_token", crunInstance.Token?.refresh_token ?? string.Empty }, { "grant_type", "refresh_token" }, { "scope", "offline_access" } }; var requestContent = new FormUrlEncodedContent(formData); requestContent.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded"); var request = new HttpRequestMessage(HttpMethod.Post, Api.BetaAuth){ Content = requestContent }; request.Headers.Authorization = new AuthenticationHeaderValue("Basic", Api.authBasicSwitch); var response = await HttpClientReq.Instance.SendHttpRequest(request); if (response.IsOk){ JsonTokenToFileAndVariable(response.ResponseContent); } else{ Console.Error.WriteLine("Refresh Token Auth Failed"); } await GetCmsToken(); } public async Task GetCmsToken(){ if (crunInstance.Token?.access_token == null){ Console.Error.WriteLine($"Missing Access Token: {crunInstance.Token?.access_token}"); return; } var request = HttpClientReq.CreateRequestMessage(Api.BetaCmsToken, HttpMethod.Get, true, true, null); var response = await HttpClientReq.Instance.SendHttpRequest(request); if (response.IsOk){ crunInstance.CmsToken = JsonConvert.DeserializeObject<CrCmsToken>(response.ResponseContent, crunInstance.SettingsJsonSerializerSettings); } else{ Console.Error.WriteLine("CMS Token Auth Failed"); } } public async Task GetCmsData(){ if (crunInstance.CmsToken?.Cms == null){ Console.Error.WriteLine("Missing CMS Token"); return; } UriBuilder uriBuilder = new UriBuilder(Api.BetaCms + crunInstance.CmsToken.Cms.Bucket + "/index?"); NameValueCollection query = HttpUtility.ParseQueryString(uriBuilder.Query); query["preferred_audio_language"] = "ja-JP"; query["Policy"] = crunInstance.CmsToken.Cms.Policy; query["Signature"] = crunInstance.CmsToken.Cms.Signature; query["Key-Pair-Id"] = crunInstance.CmsToken.Cms.KeyPairId; uriBuilder.Query = query.ToString(); var request = new HttpRequestMessage(HttpMethod.Get, uriBuilder.ToString()); var response = await HttpClientReq.Instance.SendHttpRequest(request); if (response.IsOk){ Console.WriteLine(response.ResponseContent); } else{ Console.Error.WriteLine("Failed to Get CMS Index"); } } }