Fix - Localization bug in widevine licence request

Fix - Subsciption check for third party subscriptions
This commit is contained in:
Elwador 2024-06-19 19:09:18 +02:00
parent 9814fefcf6
commit cd4ceea38a
6 changed files with 94 additions and 49 deletions

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -112,7 +113,16 @@ public class CrAuth{
if (responseSubs.IsOk){ if (responseSubs.IsOk){
var subsc = Helpers.Deserialize<Subscription>(responseSubs.ResponseContent, crunInstance.SettingsJsonSerializerSettings); var subsc = Helpers.Deserialize<Subscription>(responseSubs.ResponseContent, crunInstance.SettingsJsonSerializerSettings);
crunInstance.Profile.Subscription = subsc; crunInstance.Profile.Subscription = subsc;
crunInstance.Profile.HasPremium = subsc.IsActive; if ( subsc.SubscriptionProducts is{ Count: 0 } && subsc.ThirdPartySubscriptionProducts is{ 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{
crunInstance.Profile.HasPremium = subsc.IsActive;
}
} else{ } else{
crunInstance.Profile.HasPremium = false; crunInstance.Profile.HasPremium = false;
Console.Error.WriteLine("Failed to check premium subscription status"); Console.Error.WriteLine("Failed to check premium subscription status");

View File

@ -1029,9 +1029,8 @@ public class Crunchyroll{
var assetIdRegexMatch = Regex.Match(chosenVideoSegments.segments[0].uri, @"/assets/(?:p/)?([^_,]+)"); var assetIdRegexMatch = Regex.Match(chosenVideoSegments.segments[0].uri, @"/assets/(?:p/)?([^_,]+)");
var assetId = assetIdRegexMatch.Success ? assetIdRegexMatch.Groups[1].Value : null; var assetId = assetIdRegexMatch.Success ? assetIdRegexMatch.Groups[1].Value : null;
var sessionId = Helpers.GenerateSessionId(); var sessionId = Helpers.GenerateSessionId();
//TODO change back from error Console.WriteLine("Decryption Needed, attempting to decrypt");
Console.Error.WriteLine("Decryption Needed, attempting to decrypt");
if (!_widevine.canDecrypt){ if (!_widevine.canDecrypt){
dlFailed = true; dlFailed = true;
@ -1070,18 +1069,12 @@ public class Crunchyroll{
ErrorText = "DRM Authentication failed" ErrorText = "DRM Authentication failed"
}; };
} }
//TODO change back from error
Console.Error.WriteLine("Request to DRM Authentication successful");
DrmAuthData authData = Helpers.Deserialize<DrmAuthData>(decRequestResponse.ResponseContent, SettingsJsonSerializerSettings) ?? new DrmAuthData(); DrmAuthData authData = Helpers.Deserialize<DrmAuthData>(decRequestResponse.ResponseContent, SettingsJsonSerializerSettings) ?? new DrmAuthData();
//TODO change back from error
Console.Error.WriteLine("Deserialized Authentication successful");
Dictionary<string, string> authDataDict = new Dictionary<string, string> Dictionary<string, string> authDataDict = new Dictionary<string, string>
{ { "dt-custom-data", authData.CustomData ?? string.Empty },{ "x-dt-auth-token", authData.Token ?? string.Empty } }; { { "dt-custom-data", authData.CustomData ?? string.Empty },{ "x-dt-auth-token", authData.Token ?? string.Empty } };
//TODO change back from error
Console.Error.WriteLine("Requesting encryption keys");
var encryptionKeys = await _widevine.getKeys(chosenVideoSegments.pssh, "https://lic.drmtoday.com/license-proxy-widevine/cenc/", authDataDict); var encryptionKeys = await _widevine.getKeys(chosenVideoSegments.pssh, "https://lic.drmtoday.com/license-proxy-widevine/cenc/", authDataDict);
if (encryptionKeys.Count == 0){ if (encryptionKeys.Count == 0){
@ -1094,23 +1087,24 @@ public class Crunchyroll{
ErrorText = "Couldn't get DRM encryption keys" ErrorText = "Couldn't get DRM encryption keys"
}; };
} }
//TODO change back from error
Console.Error.WriteLine("Requested encryption keys successful");
if (Path.Exists(CfgManager.PathMP4Decrypt)){ if (Path.Exists(CfgManager.PathMP4Decrypt)){
//TODO change back from error
Console.Error.WriteLine("Building command");
var keyId = BitConverter.ToString(encryptionKeys[0].KeyID).Replace("-", "").ToLower(); var keyId = BitConverter.ToString(encryptionKeys[0].KeyID).Replace("-", "").ToLower();
var key = BitConverter.ToString(encryptionKeys[0].Bytes).Replace("-", "").ToLower(); var key = BitConverter.ToString(encryptionKeys[0].Bytes).Replace("-", "").ToLower();
var commandBase = $"--show-progress --key {keyId}:{key}"; var commandBase = $"--show-progress --key {keyId}:{key}";
var commandVideo = commandBase + $" \"{tempTsFile}.video.enc.m4s\" \"{tempTsFile}.video.m4s\""; var commandVideo = commandBase + $" \"{tempTsFile}.video.enc.m4s\" \"{tempTsFile}.video.m4s\"";
var commandAudio = commandBase + $" \"{tempTsFile}.audio.enc.m4s\" \"{tempTsFile}.audio.m4s\""; var commandAudio = commandBase + $" \"{tempTsFile}.audio.enc.m4s\" \"{tempTsFile}.audio.m4s\"";
//TODO change back from error
Console.Error.WriteLine("Built command");
if (videoDownloaded){ if (videoDownloaded){
//TODO change back from error Console.WriteLine("Started decrypting video");
Console.Error.WriteLine("Started decrypting video"); data.DownloadProgress = new DownloadProgress(){
IsDownloading = true,
Percent = 100,
Time = 0,
DownloadSpeed = 0,
Doing = "Decrypting video"
};
Queue.Refresh();
var decryptVideo = await Helpers.ExecuteCommandAsync("mp4decrypt", CfgManager.PathMP4Decrypt, commandVideo); var decryptVideo = await Helpers.ExecuteCommandAsync("mp4decrypt", CfgManager.PathMP4Decrypt, commandVideo);
if (!decryptVideo.IsOk){ if (!decryptVideo.IsOk){
@ -1156,13 +1150,19 @@ public class Crunchyroll{
}); });
} }
} else{ } else{
//TODO change back from error Console.WriteLine("No Video downloaded");
Console.Error.WriteLine("No Video downloaded");
} }
if (audioDownloaded){ if (audioDownloaded){
//TODO change back from error Console.WriteLine("Started decrypting audio");
Console.Error.WriteLine("Started decrypting audio"); data.DownloadProgress = new DownloadProgress(){
IsDownloading = true,
Percent = 100,
Time = 0,
DownloadSpeed = 0,
Doing = "Decrypting audio"
};
Queue.Refresh();
var decryptAudio = await Helpers.ExecuteCommandAsync("mp4decrypt", CfgManager.PathMP4Decrypt, commandAudio); var decryptAudio = await Helpers.ExecuteCommandAsync("mp4decrypt", CfgManager.PathMP4Decrypt, commandAudio);
if (!decryptAudio.IsOk){ if (!decryptAudio.IsOk){
@ -1207,8 +1207,7 @@ public class Crunchyroll{
}); });
} }
} else{ } else{
//TODO change back from error Console.WriteLine("No Audio downloaded");
Console.Error.WriteLine("No Audio downloaded");
} }
} else{ } else{
Console.Error.WriteLine("mp4decrypt not found, files need decryption. Decryption Keys: "); Console.Error.WriteLine("mp4decrypt not found, files need decryption. Decryption Keys: ");

View File

@ -87,7 +87,7 @@ public class Session{
Type = LicenseRequest.RequestType.New, Type = LicenseRequest.RequestType.New,
KeyControlNonce = 1093602366, KeyControlNonce = 1093602366,
ProtocolVersion = ProtocolVersion.Current, ProtocolVersion = ProtocolVersion.Current,
RequestTime = uint.Parse((DateTime.Now - DateTime.UnixEpoch).TotalSeconds.ToString().Split(",")[0]), RequestTime = uint.Parse((DateTime.Now - DateTime.UnixEpoch).TotalSeconds.ToString(System.Globalization.CultureInfo.InvariantCulture).Split('.')[0]),
ContentId = new LicenseRequest.ContentIdentification{ ContentId = new LicenseRequest.ContentIdentification{
CencId = new LicenseRequest.ContentIdentification.Cenc{ CencId = new LicenseRequest.ContentIdentification.Cenc{
LicenseType = LicenseType.Default, LicenseType = LicenseType.Default,
@ -104,7 +104,7 @@ public class Session{
Type = LicenseRequestRaw.RequestType.New, Type = LicenseRequestRaw.RequestType.New,
KeyControlNonce = 1093602366, KeyControlNonce = 1093602366,
ProtocolVersion = ProtocolVersion.Current, ProtocolVersion = ProtocolVersion.Current,
RequestTime = uint.Parse((DateTime.Now - DateTime.UnixEpoch).TotalSeconds.ToString().Split(",")[0]), RequestTime = uint.Parse((DateTime.Now - DateTime.UnixEpoch).TotalSeconds.ToString(System.Globalization.CultureInfo.InvariantCulture).Split('.')[0]),
ContentId = new LicenseRequestRaw.ContentIdentification{ ContentId = new LicenseRequestRaw.ContentIdentification{
CencId = new LicenseRequestRaw.ContentIdentification.Cenc{ CencId = new LicenseRequestRaw.ContentIdentification.Cenc{
LicenseType = LicenseType.Default, LicenseType = LicenseType.Default,

View File

@ -71,34 +71,39 @@ public class Widevine{
canDecrypt = false; canDecrypt = false;
} }
} }
public async Task<List<ContentKey>> getKeys(string? pssh, string licenseServer, Dictionary<string, string> authData){ public async Task<List<ContentKey>> getKeys(string? pssh, string licenseServer, Dictionary<string, string> authData){
if (pssh == null || !canDecrypt) return new List<ContentKey>(); if (pssh == null || !canDecrypt) return new List<ContentKey>();
byte[] psshBuffer = Convert.FromBase64String(pssh); try{
byte[] psshBuffer = Convert.FromBase64String(pssh);
Session ses = new Session(new ContentDecryptionModule{ identifierBlob = identifierBlob, privateKey = privateKey }, psshBuffer); Session ses = new Session(new ContentDecryptionModule{ identifierBlob = identifierBlob, privateKey = privateKey }, psshBuffer);
var playbackRequest2 = new HttpRequestMessage(HttpMethod.Post, licenseServer); var playbackRequest2 = new HttpRequestMessage(HttpMethod.Post, licenseServer);
foreach (var keyValuePair in authData){ foreach (var keyValuePair in authData){
playbackRequest2.Headers.Add(keyValuePair.Key, keyValuePair.Value); playbackRequest2.Headers.Add(keyValuePair.Key, keyValuePair.Value);
} }
var licenceReq = ses.GetLicenseRequest(); var licenceReq = ses.GetLicenseRequest();
playbackRequest2.Content = new ByteArrayContent(licenceReq); playbackRequest2.Content = new ByteArrayContent(licenceReq);
var response = await HttpClientReq.Instance.SendHttpRequest(playbackRequest2); var response = await HttpClientReq.Instance.SendHttpRequest(playbackRequest2);
if (!response.IsOk){ if (!response.IsOk){
Console.Error.WriteLine("Failed to get Keys!"); Console.Error.WriteLine("Failed to get Keys!");
return new List<ContentKey>();
}
LicenceReqResp resp = Helpers.Deserialize<LicenceReqResp>(response.ResponseContent, null) ?? new LicenceReqResp();
ses.ProvideLicense(Convert.FromBase64String(resp.license));
return ses.ContentKeys;
} catch (Exception e){
Console.Error.WriteLine(e);
return new List<ContentKey>(); return new List<ContentKey>();
} }
LicenceReqResp resp = Helpers.Deserialize<LicenceReqResp>(response.ResponseContent,null) ?? new LicenceReqResp();
ses.ProvideLicense(Convert.FromBase64String(resp.license));
return ses.ContentKeys;
} }
} }

View File

@ -40,6 +40,12 @@ public class Subscription{
public bool TaxIncluded{ get; set; } public bool TaxIncluded{ get; set; }
[JsonProperty("subscription_products")] [JsonProperty("subscription_products")]
public List<SubscriptionProduct>? SubscriptionProducts{ get; set; } public List<SubscriptionProduct>? SubscriptionProducts{ get; set; }
[JsonProperty("third_party_subscription_products")]
public List<ThirdPartySubscriptionProduct>? ThirdPartySubscriptionProducts{ get; set; }
[JsonProperty("nonrecurring_subscription_products")]
public List<SubscriptionProduct>? NonrecurringSubscriptionProducts{ get; set; }
} }
public class SubscriptionProduct{ public class SubscriptionProduct{
@ -54,4 +60,25 @@ public class SubscriptionProduct{
public string? Tier{ get; set; } public string? Tier{ get; set; }
[JsonProperty("active_free_trial")] [JsonProperty("active_free_trial")]
public bool ActiveFreeTrial{ get; set; } public bool ActiveFreeTrial{ get; set; }
}
public class ThirdPartySubscriptionProduct{
[JsonProperty("effective_date")]
public DateTime EffectiveDate{ get; set; }
public string? Source{ get; set; }
[JsonProperty("source_reference")]
public string? SourceReference{ get; set; }
public string? Sku{ get; set; }
public string? Tier{ get; set; }
[JsonProperty("active_free_trial")]
public bool ActiveFreeTrial{ get; set; }
[JsonProperty("in_grace")]
public bool InGrace{ get; set; }
[JsonProperty("on_hold")]
public bool OnHold{ get; set; }
[JsonProperty("auto_renew")]
public bool AutoRenew{ get; set; }
[JsonProperty("expiration_date")]
public DateTime ExpirationDate{ get; set; }
} }

View File

@ -53,10 +53,14 @@ public partial class AccountPageViewModel : ViewModelBase{
if (Crunchyroll.Instance.Profile.Subscription != null && Crunchyroll.Instance.Profile.Subscription?.SubscriptionProducts != null){ if (Crunchyroll.Instance.Profile.Subscription != null && Crunchyroll.Instance.Profile.Subscription?.SubscriptionProducts != null){
if (Crunchyroll.Instance.Profile.Subscription?.SubscriptionProducts.Count >= 1){ if (Crunchyroll.Instance.Profile.Subscription?.SubscriptionProducts.Count >= 1){
var sub = Crunchyroll.Instance.Profile.Subscription?.SubscriptionProducts.First(); var sub = Crunchyroll.Instance.Profile.Subscription?.SubscriptionProducts.First();
if (sub != null){ if (sub != null){
IsCancelled = sub.IsCancelled; IsCancelled = sub.IsCancelled;
} }
}else if (Crunchyroll.Instance.Profile.Subscription?.ThirdPartySubscriptionProducts.Count >= 1){
var sub = Crunchyroll.Instance.Profile.Subscription?.ThirdPartySubscriptionProducts.First();
if (sub != null){
IsCancelled = !sub.AutoRenew;
}
} }
if (Crunchyroll.Instance.Profile.Subscription?.NextRenewalDate != null){ if (Crunchyroll.Instance.Profile.Subscription?.NextRenewalDate != null){