diff --git a/Source/AudibleUtilities/AccountsSettings.cs b/Source/AudibleUtilities/AccountsSettings.cs index 8f1bc538..bdef1580 100644 --- a/Source/AudibleUtilities/AccountsSettings.cs +++ b/Source/AudibleUtilities/AccountsSettings.cs @@ -6,6 +6,7 @@ using AudibleApi.Authorization; using Dinah.Core; using Newtonsoft.Json; +#nullable enable namespace AudibleUtilities { // 'AccountsSettings' is intentionally NOT IEnumerable<> so that properties can be added/extended @@ -14,8 +15,8 @@ namespace AudibleUtilities // JSON : Array (properties on the collection will not be serialized) public class AccountsSettings : IUpdatable { - public event EventHandler Updated; - private void update(object sender = null, EventArgs e = null) + public event EventHandler? Updated; + private void update(object? sender = null, EventArgs? e = null) { foreach (var account in Accounts) validate(account); @@ -48,9 +49,9 @@ namespace AudibleUtilities } } - private string _cdm; + private string? _cdm; [JsonProperty] - public string Cdm + public string? Cdm { get => _cdm; set @@ -68,7 +69,7 @@ namespace AudibleUtilities #endregion #region de/serialize - public static AccountsSettings FromJson(string json) + public static AccountsSettings? FromJson(string json) => JsonConvert.DeserializeObject(json, Identity.GetJsonSerializerSettings()); public string ToJson(Formatting formatting = Formatting.Indented) @@ -107,7 +108,7 @@ namespace AudibleUtilities account.Updated += update; } - public Account GetAccount(string accountId, string locale) + public Account? GetAccount(string accountId, string? locale) { if (locale is null) return null; diff --git a/Source/FileLiberator/DownloadOptions.Factory.cs b/Source/FileLiberator/DownloadOptions.Factory.cs index d560886c..2125d2e4 100644 --- a/Source/FileLiberator/DownloadOptions.Factory.cs +++ b/Source/FileLiberator/DownloadOptions.Factory.cs @@ -3,6 +3,7 @@ using AudibleApi; using AudibleApi.Common; using AudibleUtilities.Widevine; using DataLayer; +using Dinah.Core; using LibationFileManager; using NAudio.Lame; using System; @@ -24,6 +25,16 @@ public partial class DownloadOptions public static async Task InitiateDownloadAsync(Api api, Configuration config, LibraryBook libraryBook, CancellationToken token) { var license = await ChooseContent(api, libraryBook, config, token); + Serilog.Log.Logger.Debug("Content License {@License}", new + { + license.DrmType, + license.ContentMetadata.ContentReference.Codec, + license.ContentMetadata.ContentReference.Marketplace, + license.ContentMetadata.ContentReference.ContentSizeInBytes, + license.ContentMetadata.ContentReference.Version, + license.ContentMetadata.ContentReference.FileVersion + }); + token.ThrowIfCancellationRequested(); //Some audiobooks will have incorrect chapters in the metadata returned from the license request, @@ -56,10 +67,28 @@ public partial class DownloadOptions private static async Task ChooseContent(Api api, LibraryBook libraryBook, Configuration config, CancellationToken token) { + Serilog.Log.Logger.Debug("Download Settings {@Settings}", new + { + config.FileDownloadQuality, + config.UseWidevine, + config.Request_xHE_AAC, + config.RequestSpatial, + config.SpatialAudioCodec + }); + var dlQuality = config.FileDownloadQuality == Configuration.DownloadQuality.Normal ? DownloadQuality.Normal : DownloadQuality.High; - if (!config.UseWidevine || await Cdm.GetCdmAsync() is not Cdm cdm) + bool canUseWidevine = api.SupportsWidevine(); + if (!config.UseWidevine || !canUseWidevine || await Cdm.GetCdmAsync() is not Cdm cdm) { + if (config.UseWidevine) + { + if (canUseWidevine) + Serilog.Log.Logger.Information("Unable to get a Widevine CDM. Falling back to ADRM."); + else + Serilog.Log.Logger.Information("Account {@account} is not registered as an android device, so content will not be downloaded with Widevine DRM. Remove and re-add the account in Libation to fix.", libraryBook.Account.ToMask()); + } + token.ThrowIfCancellationRequested(); var license = await api.GetDownloadLicenseAsync(libraryBook.Book.AudibleProductId, dlQuality); return new LicenseInfo(license); diff --git a/Source/FileLiberator/UtilityExtensions.cs b/Source/FileLiberator/UtilityExtensions.cs index 73ac5bbc..d563924c 100644 --- a/Source/FileLiberator/UtilityExtensions.cs +++ b/Source/FileLiberator/UtilityExtensions.cs @@ -7,6 +7,7 @@ using DataLayer; using Dinah.Core; using LibationFileManager; using LibationFileManager.Templates; +using System.Security.Authentication; #nullable enable namespace FileLiberator @@ -25,14 +26,22 @@ namespace FileLiberator public static async Task GetApiAsync(this LibraryBook libraryBook) { - Account account; - using (var accounts = AudibleApiStorage.GetAccountsSettingsPersister()) - account = accounts.AccountsSettings.GetAccount(libraryBook.Account, libraryBook.Book.Locale); + using var accounts = AudibleApiStorage.GetAccountsSettingsPersister(); + var account = accounts.AccountsSettings.GetAccount(libraryBook.Account, libraryBook.Book.Locale) + ?? throw new InvalidCredentialException($"No account found for '{libraryBook.Account}' and locale '{libraryBook.Book.Locale}'"); var apiExtended = await ApiExtended.CreateAsync(account); return apiExtended.Api; } + public static bool SupportsWidevine(this AudibleApi.Api api) + { + //TODO: Expose Api's identity maintainer directly instead of using reflection. + var identityProperty = api.GetType().GetProperty("_identityMaintainer", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + return identityProperty?.GetValue(api) is AudibleApi.Authorization.IIdentityMaintainer identityMaintainer + && identityMaintainer.DeviceType == AudibleApi.Resources.DeviceType; + } + public static LibraryBookDto ToDto(this LibraryBook libraryBook) { using var persister = AudibleApiStorage.GetAccountsSettingsPersister(); diff --git a/Source/LibationAvalonia/Controls/Settings/Audio.axaml.cs b/Source/LibationAvalonia/Controls/Settings/Audio.axaml.cs index 63758b69..a0f08087 100644 --- a/Source/LibationAvalonia/Controls/Settings/Audio.axaml.cs +++ b/Source/LibationAvalonia/Controls/Settings/Audio.axaml.cs @@ -39,7 +39,7 @@ namespace LibationAvalonia.Controls.Settings { using var accounts = AudibleApiStorage.GetAccountsSettingsPersister(); - if (!accounts.AccountsSettings.Accounts.Any(a => a.IdentityTokens.DeviceType == AudibleApi.Resources.DeviceType)) + if (!accounts.AccountsSettings.Accounts.All(a => a.IdentityTokens.DeviceType == AudibleApi.Resources.DeviceType)) { if (VisualRoot is Window parent) { diff --git a/Source/LibationWinForms/Dialogs/SettingsDialog.AudioSettings.cs b/Source/LibationWinForms/Dialogs/SettingsDialog.AudioSettings.cs index da172bf6..c9014ce6 100644 --- a/Source/LibationWinForms/Dialogs/SettingsDialog.AudioSettings.cs +++ b/Source/LibationWinForms/Dialogs/SettingsDialog.AudioSettings.cs @@ -224,7 +224,7 @@ namespace LibationWinForms.Dialogs { using var accounts = AudibleApiStorage.GetAccountsSettingsPersister(); - if (!accounts.AccountsSettings.Accounts.Any(a => a.IdentityTokens.DeviceType == AudibleApi.Resources.DeviceType)) + if (!accounts.AccountsSettings.Accounts.All(a => a.IdentityTokens.DeviceType == AudibleApi.Resources.DeviceType)) { var choice = MessageBox.Show(this, "In order to enable widevine content, Libation will need to log into your accounts again.\r\n\r\n" +