mirror of
https://github.com/Crunchy-DL/Crunchy-Downloader.git
synced 2026-04-23 18:02:10 +00:00
Add - Added background image option to the Appearance settings Add - Background Image Settings - Added new options to control opacity and blur radius Add - Added "Couldn't sync dubs" status if the syncing failed Add - Added functionality to combine multiple episodes from the same season into a single entry in the calendar Add - Added video resolution display next to dubs/subs in the downloads tab Add - Added Cloudflare check to image loading Add - Added hardsub selection if the current is not available Add - Added part size setting to configure the size of parts downloaded at the same time Add - Added quality override to history series Add - Added history marker to search results to indicate if a series is already in the user's history Add - Added seasons tab for seasonal releases (Spring, Summer, Fall, Winter) Add - Added potential releases and release times for the current day and the next week to the custom calendar Chg - Changed Calendar cards background color for improved visibility Chg - Combined Appearance settings into a single section in the settings tab Chg - Consolidated Debug settings into one settings expander for better organization Chg - Changed time sync to now check both the start and end of the video Chg - Changed encoding progress to be displayed by the progress bar Chg - Updated the functionality for hiding dubs in the custom calendar Chg - Adjusted Dub sync to improve accuracy, resolving issues where it failed for more episodes than expected Chg - Subtitles and dubs are now sorted according to the order selected in the MKV file Chg - Changed logout behavior to correctly log out if login fails when starting the downloader Chg - Changed that all downloaded files are removed if an in-progress download is removed from the queue Chg - Changed default profile image Chg - Updated used packages to the newest version Chg - Separated settings to separate tabs Fix - Fixed some series didn't get added to the history Fix - Fixed an issue with file path length that prevented some files from being accessed properly Fix - Fixed an issue where file names exceeded the maximum allowable length, causing errors Fix - Fixed an issue where refreshing a series could get stuck Fix - Fixed a crash that could happen with the syncing Fix - Fixed an issue where the download status showed "Done" while moving files from the temp folder Fix - Fixed an issue where cookies were not being utilized correctly Fix - Resolved issues with displaying dates in UTC format Fix - Fixed an issue with incorrect calendar grouping Fix - Fixed an issue with the previous week navigation in the calendar Fix - Fixed an issue where the calendar would not display correctly when not logged in Fix - Fixed incorrect FFmpeg check for other OS (Linux/macOS) Fix - Fixed an issue where image loading used a different HTTP client
272 lines
No EOL
11 KiB
C#
272 lines
No EOL
11 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Net.Http;
|
|
using System.Threading.Tasks;
|
|
using CRD.Utils;
|
|
using CRD.Utils.Structs;
|
|
using CRD.Utils.Structs.Crunchyroll;
|
|
using CRD.Views;
|
|
using Newtonsoft.Json;
|
|
using ReactiveUI;
|
|
|
|
namespace CRD.Downloader.Crunchyroll;
|
|
|
|
public class CrAuth{
|
|
private readonly CrunchyrollManager crunInstance = CrunchyrollManager.Instance;
|
|
|
|
public async Task AuthAnonymous(){
|
|
string uuid = Guid.NewGuid().ToString();
|
|
|
|
var formData = new Dictionary<string, string>{
|
|
{ "grant_type", "client_id" },
|
|
{ "scope", "offline_access" },
|
|
{ "device_id", uuid },
|
|
{ "device_type", "Chrome on Windows" }
|
|
};
|
|
|
|
var requestContent = new FormUrlEncodedContent(formData);
|
|
|
|
var crunchyAuthHeaders = new Dictionary<string, string>{
|
|
{ "Authorization", ApiUrls.authBasicSwitch },
|
|
{ "User-Agent", ApiUrls.ChromeUserAgent }
|
|
};
|
|
|
|
var request = new HttpRequestMessage(HttpMethod.Post, ApiUrls.BetaAuth){
|
|
Content = requestContent
|
|
};
|
|
|
|
foreach (var header in crunchyAuthHeaders){
|
|
request.Headers.Add(header.Key, header.Value);
|
|
}
|
|
|
|
var response = await HttpClientReq.Instance.SendHttpRequest(request);
|
|
|
|
if (response.IsOk){
|
|
JsonTokenToFileAndVariable(response.ResponseContent, uuid);
|
|
} else{
|
|
Console.Error.WriteLine("Anonymous login failed");
|
|
}
|
|
|
|
crunInstance.Profile = new CrProfile{
|
|
Username = "???",
|
|
Avatar = "crbrand_avatars_logo_marks_mangagirl_taupe.png",
|
|
PreferredContentAudioLanguage = "ja-JP",
|
|
PreferredContentSubtitleLanguage = "de-DE"
|
|
};
|
|
}
|
|
|
|
private void JsonTokenToFileAndVariable(string content, string deviceId){
|
|
crunInstance.Token = Helpers.Deserialize<CrToken>(content, crunInstance.SettingsJsonSerializerSettings);
|
|
|
|
if (crunInstance.Token is{ expires_in: not null }){
|
|
crunInstance.Token.device_id = deviceId;
|
|
crunInstance.Token.expires = DateTime.Now.AddSeconds((double)crunInstance.Token.expires_in);
|
|
|
|
CfgManager.WriteTokenToYamlFile(crunInstance.Token, CfgManager.PathCrToken);
|
|
}
|
|
}
|
|
|
|
public async Task Auth(AuthData data){
|
|
string uuid = Guid.NewGuid().ToString();
|
|
|
|
var formData = new Dictionary<string, string>{
|
|
{ "username", data.Username }, // Replace with actual data
|
|
{ "password", data.Password }, // Replace with actual data
|
|
{ "grant_type", "password" },
|
|
{ "scope", "offline_access" },
|
|
{ "device_id", uuid },
|
|
{ "device_type", "Chrome on Windows" }
|
|
};
|
|
|
|
var requestContent = new FormUrlEncodedContent(formData);
|
|
|
|
var crunchyAuthHeaders = new Dictionary<string, string>{
|
|
{ "Authorization", ApiUrls.authBasicSwitch },
|
|
{ "User-Agent", ApiUrls.ChromeUserAgent }
|
|
};
|
|
|
|
var request = new HttpRequestMessage(HttpMethod.Post, ApiUrls.BetaAuth){
|
|
Content = requestContent
|
|
};
|
|
|
|
foreach (var header in crunchyAuthHeaders){
|
|
request.Headers.Add(header.Key, header.Value);
|
|
}
|
|
|
|
var response = await HttpClientReq.Instance.SendHttpRequest(request);
|
|
|
|
if (response.IsOk){
|
|
JsonTokenToFileAndVariable(response.ResponseContent, uuid);
|
|
} else{
|
|
if (response.ResponseContent.Contains("invalid_credentials")){
|
|
MessageBus.Current.SendMessage(new ToastMessage($"Failed to login - because of invalid login credentials", ToastType.Error, 10));
|
|
} else{
|
|
MessageBus.Current.SendMessage(new ToastMessage($"Failed to login - {response.ResponseContent.Substring(0, response.ResponseContent.Length < 200 ? response.ResponseContent.Length : 200)}",
|
|
ToastType.Error, 10));
|
|
}
|
|
}
|
|
|
|
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(ApiUrls.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(ApiUrls.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 expiration = thirdPartySub.InGrace ? thirdPartySub.InGraceExpirationDate : thirdPartySub.ExpirationDate;
|
|
var remaining = expiration - DateTime.Now;
|
|
crunInstance.Profile.HasPremium = true;
|
|
if (crunInstance.Profile.Subscription != null){
|
|
crunInstance.Profile.Subscription.IsActive = remaining > TimeSpan.Zero;
|
|
crunInstance.Profile.Subscription.NextRenewalDate = expiration;
|
|
}
|
|
} else if (subsc is{ SubscriptionProducts:{ Count: 0 }, NonrecurringSubscriptionProducts.Count: > 0 }){
|
|
var nonRecurringSub = subsc.NonrecurringSubscriptionProducts.First();
|
|
var remaining = nonRecurringSub.EndDate - DateTime.Now;
|
|
crunInstance.Profile.HasPremium = true;
|
|
if (crunInstance.Profile.Subscription != null){
|
|
crunInstance.Profile.Subscription.IsActive = remaining > TimeSpan.Zero;
|
|
crunInstance.Profile.Subscription.NextRenewalDate = nonRecurringSub.EndDate;
|
|
}
|
|
} else if (subsc is{ SubscriptionProducts:{ Count: 0 }, FunimationSubscriptions.Count: > 0 }){
|
|
crunInstance.Profile.HasPremium = true;
|
|
} else if (subsc is{ SubscriptionProducts.Count: > 0 }){
|
|
crunInstance.Profile.HasPremium = true;
|
|
} else{
|
|
crunInstance.Profile.HasPremium = false;
|
|
Console.Error.WriteLine($"No subscription available:\n {JsonConvert.SerializeObject(subsc, Formatting.Indented)} ");
|
|
}
|
|
} else{
|
|
crunInstance.Profile.HasPremium = false;
|
|
Console.Error.WriteLine("Failed to check premium subscription status");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public async Task LoginWithToken(){
|
|
if (crunInstance.Token?.refresh_token == null){
|
|
Console.Error.WriteLine("Missing Refresh Token");
|
|
await AuthAnonymous();
|
|
return;
|
|
}
|
|
|
|
string uuid = Guid.NewGuid().ToString();
|
|
|
|
var formData = new Dictionary<string, string>{
|
|
{ "refresh_token", crunInstance.Token.refresh_token },
|
|
{ "scope", "offline_access" },
|
|
{ "device_id", uuid },
|
|
{ "device_type", "Chrome on Windows" },
|
|
{ "grant_type", "refresh_token" }
|
|
};
|
|
|
|
var requestContent = new FormUrlEncodedContent(formData);
|
|
|
|
var crunchyAuthHeaders = new Dictionary<string, string>{
|
|
{ "Authorization", ApiUrls.authBasicSwitch },
|
|
{ "User-Agent", ApiUrls.ChromeUserAgent }
|
|
};
|
|
|
|
var request = new HttpRequestMessage(HttpMethod.Post, ApiUrls.BetaAuth){
|
|
Content = requestContent
|
|
};
|
|
|
|
foreach (var header in crunchyAuthHeaders){
|
|
request.Headers.Add(header.Key, header.Value);
|
|
}
|
|
|
|
HttpClientReq.Instance.SetETPCookie(crunInstance.Token.refresh_token);
|
|
|
|
var response = await HttpClientReq.Instance.SendHttpRequest(request);
|
|
|
|
if (response.IsOk){
|
|
JsonTokenToFileAndVariable(response.ResponseContent, uuid);
|
|
|
|
if (crunInstance.Token?.refresh_token != null){
|
|
HttpClientReq.Instance.SetETPCookie(crunInstance.Token.refresh_token);
|
|
|
|
await GetProfile();
|
|
}
|
|
} else{
|
|
Console.Error.WriteLine("Token Auth Failed");
|
|
await AuthAnonymous();
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
string uuid = Guid.NewGuid().ToString();
|
|
|
|
var formData = new Dictionary<string, string>{
|
|
{ "refresh_token", crunInstance.Token.refresh_token },
|
|
{ "grant_type", "refresh_token" },
|
|
{ "scope", "offline_access" },
|
|
{ "device_id", uuid },
|
|
{ "device_type", "Chrome on Windows" }
|
|
};
|
|
|
|
var requestContent = new FormUrlEncodedContent(formData);
|
|
|
|
var crunchyAuthHeaders = new Dictionary<string, string>{
|
|
{ "Authorization", ApiUrls.authBasicSwitch },
|
|
{ "User-Agent", ApiUrls.ChromeUserAgent }
|
|
};
|
|
|
|
var request = new HttpRequestMessage(HttpMethod.Post, ApiUrls.BetaAuth){
|
|
Content = requestContent
|
|
};
|
|
|
|
foreach (var header in crunchyAuthHeaders){
|
|
request.Headers.Add(header.Key, header.Value);
|
|
}
|
|
|
|
HttpClientReq.Instance.SetETPCookie(crunInstance.Token.refresh_token);
|
|
|
|
var response = await HttpClientReq.Instance.SendHttpRequest(request);
|
|
|
|
if (response.IsOk){
|
|
JsonTokenToFileAndVariable(response.ResponseContent, uuid);
|
|
} else{
|
|
Console.Error.WriteLine("Refresh Token Auth Failed");
|
|
}
|
|
}
|
|
} |