diff --git a/Source/AaxDecrypter/AudiobookDownloadBase.cs b/Source/AaxDecrypter/AudiobookDownloadBase.cs index 91883c09..44031193 100644 --- a/Source/AaxDecrypter/AudiobookDownloadBase.cs +++ b/Source/AaxDecrypter/AudiobookDownloadBase.cs @@ -190,7 +190,11 @@ namespace AaxDecrypter //Write aax decryption key string keyPath = Path.ChangeExtension(aaxPath, ".key"); FileUtility.SaferDelete(keyPath); - await File.WriteAllTextAsync(keyPath, $"Key={DownloadOptions.AudibleKey}{Environment.NewLine}IV={DownloadOptions.AudibleIV}"); + + if (string.IsNullOrEmpty(DownloadOptions.AudibleIV)) + await File.WriteAllTextAsync(keyPath, $"ActivationBytes={DownloadOptions.AudibleKey}"); + else + await File.WriteAllTextAsync(keyPath, $"Key={DownloadOptions.AudibleKey}{Environment.NewLine}IV={DownloadOptions.AudibleIV}"); OnFileCreated(aaxPath); OnFileCreated(keyPath); diff --git a/Source/FileLiberator/AudioFileStorageExt.cs b/Source/FileLiberator/AudioFileStorageExt.cs index dc96fa27..e5c2e344 100644 --- a/Source/FileLiberator/AudioFileStorageExt.cs +++ b/Source/FileLiberator/AudioFileStorageExt.cs @@ -25,8 +25,7 @@ namespace FileLiberator if (seriesParent is not null) { - var baseDir = Templates.Folder.GetFilename(seriesParent.ToDto(), AudibleFileStorage.BooksDirectory, ""); - return Templates.Folder.GetFilename(libraryBook.ToDto(), baseDir, ""); + return Templates.Folder.GetFilename(seriesParent.ToDto(), AudibleFileStorage.BooksDirectory, ""); } } } diff --git a/Source/FileLiberator/UtilityExtensions.cs b/Source/FileLiberator/UtilityExtensions.cs index b85eee0c..54f72a44 100644 --- a/Source/FileLiberator/UtilityExtensions.cs +++ b/Source/FileLiberator/UtilityExtensions.cs @@ -41,7 +41,8 @@ namespace FileLiberator SeriesName = libraryBook.Book.SeriesLink.FirstOrDefault()?.Series.Name, SeriesNumber = (int?)libraryBook.Book.SeriesLink.FirstOrDefault()?.Index, - IsPodcast = libraryBook.Book.IsEpisodeChild(), + IsPodcastParent = libraryBook.Book.IsEpisodeParent(), + IsPodcast = libraryBook.Book.IsEpisodeChild() || libraryBook.Book.IsEpisodeParent(), BitRate = libraryBook.Book.AudioFormat.Bitrate, SampleRate = libraryBook.Book.AudioFormat.SampleRate, diff --git a/Source/HangoverAvalonia/HangoverAvalonia.csproj b/Source/HangoverAvalonia/HangoverAvalonia.csproj index 676b4164..fdc5bd73 100644 --- a/Source/HangoverAvalonia/HangoverAvalonia.csproj +++ b/Source/HangoverAvalonia/HangoverAvalonia.csproj @@ -2,6 +2,7 @@ WinExe net7.0 + win-x64 copyused true @@ -66,13 +67,13 @@ - - + + - - - - + + + + diff --git a/Source/LibationAvalonia/App.axaml.cs b/Source/LibationAvalonia/App.axaml.cs index 939e9513..55d3002a 100644 --- a/Source/LibationAvalonia/App.axaml.cs +++ b/Source/LibationAvalonia/App.axaml.cs @@ -28,8 +28,6 @@ namespace LibationAvalonia public static IBrush ProcessQueueBookDefaultBrush { get; private set; } public static IBrush SeriesEntryGridBackgroundBrush { get; private set; } - public static IAssetLoader AssetLoader { get; private set; } - public static readonly Uri AssetUriBase = new("avares://Libation/Assets/"); public static Stream OpenAsset(string assetRelativePath) => AssetLoader.Open(new Uri(AssetUriBase, assetRelativePath)); @@ -37,7 +35,6 @@ namespace LibationAvalonia public override void Initialize() { AvaloniaXamlLoader.Load(this); - AssetLoader = AvaloniaLocator.Current.GetService(); } public static Task> LibraryTask; diff --git a/Source/LibationAvalonia/Controls/LinkLabel.axaml.cs b/Source/LibationAvalonia/Controls/LinkLabel.axaml.cs index 097416ff..b0dff8ea 100644 --- a/Source/LibationAvalonia/Controls/LinkLabel.axaml.cs +++ b/Source/LibationAvalonia/Controls/LinkLabel.axaml.cs @@ -10,9 +10,9 @@ using System.Windows.Input; namespace LibationAvalonia.Controls { - public partial class LinkLabel : TextBlock, IStyleable, ICommandSource + public partial class LinkLabel : TextBlock, ICommandSource { - Type IStyleable.StyleKey => typeof(LinkLabel); + protected override Type StyleKeyOverride => typeof(LinkLabel); public static readonly StyledProperty CommandProperty = AvaloniaProperty.Register(nameof(Command), enableDataValidation: true); diff --git a/Source/LibationAvalonia/Controls/WheelComboBox.axaml.cs b/Source/LibationAvalonia/Controls/WheelComboBox.axaml.cs index cf6a150f..d3938506 100644 --- a/Source/LibationAvalonia/Controls/WheelComboBox.axaml.cs +++ b/Source/LibationAvalonia/Controls/WheelComboBox.axaml.cs @@ -7,7 +7,8 @@ namespace LibationAvalonia.Controls { public partial class WheelComboBox : ComboBox, IStyleable { - Type IStyleable.StyleKey => typeof(ComboBox); + protected override Type StyleKeyOverride => typeof(ComboBox); + public WheelComboBox() { InitializeComponent(); @@ -16,9 +17,15 @@ namespace LibationAvalonia.Controls { var dir = Math.Sign(e.Delta.Y); if (dir == 1 && SelectedIndex > 0) + { SelectedIndex--; + e.Handled = true; + } else if (dir == -1 && SelectedIndex < ItemCount - 1) + { SelectedIndex++; + e.Handled = true; + } base.OnPointerWheelChanged(e); } diff --git a/Source/LibationAvalonia/LibationAvalonia.csproj b/Source/LibationAvalonia/LibationAvalonia.csproj index 39448922..00a7bf61 100644 --- a/Source/LibationAvalonia/LibationAvalonia.csproj +++ b/Source/LibationAvalonia/LibationAvalonia.csproj @@ -70,13 +70,13 @@ - - - - - - - + + + + + + + diff --git a/Source/LibationAvalonia/MacAccessKeyHandler.cs b/Source/LibationAvalonia/MacAccessKeyHandler.cs deleted file mode 100644 index bee7eebf..00000000 --- a/Source/LibationAvalonia/MacAccessKeyHandler.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Avalonia; -using Avalonia.Input; - -namespace LibationAvalonia -{ - internal class MacAccessKeyHandler : AccessKeyHandler - { - protected override void OnPreviewKeyDown(object sender, KeyEventArgs e) - { - if (e.Key is Key.LWin or Key.RWin) - { - var newArgs = new KeyEventArgs { Key = Key.LeftAlt, Handled = e.Handled }; - base.OnPreviewKeyDown(sender, newArgs); - e.Handled = newArgs.Handled; - } - else if (e.Key is not Key.LeftAlt and not Key.RightAlt) - base.OnPreviewKeyDown(sender, e); - } - - protected override void OnPreviewKeyUp(object sender, KeyEventArgs e) - { - if (e.Key is Key.LWin or Key.RWin) - { - var newArgs = new KeyEventArgs { Key = Key.LeftAlt, Handled = e.Handled }; - base.OnPreviewKeyUp(sender, newArgs); - e.Handled = newArgs.Handled; - } - else if (e.Key is not Key.LeftAlt and not Key.RightAlt) - base.OnPreviewKeyDown(sender, e); - } - - protected override void OnKeyDown(object sender, KeyEventArgs e) - { - if (e.KeyModifiers.HasAllFlags(KeyModifiers.Meta)) - { - var newArgs = new KeyEventArgs { Key = e.Key, Handled = e.Handled, KeyModifiers = KeyModifiers.Alt }; - base.OnKeyDown(sender, newArgs); - e.Handled = newArgs.Handled; - } - else if (!e.KeyModifiers.HasFlag(KeyModifiers.Alt)) - base.OnPreviewKeyDown(sender, e); - } - } -} diff --git a/Source/LibationAvalonia/ViewModels/ProcessBookViewModel.cs b/Source/LibationAvalonia/ViewModels/ProcessBookViewModel.cs index 0f8f2ff9..d4607ed0 100644 --- a/Source/LibationAvalonia/ViewModels/ProcessBookViewModel.cs +++ b/Source/LibationAvalonia/ViewModels/ProcessBookViewModel.cs @@ -61,12 +61,12 @@ namespace LibationAvalonia.ViewModels #region Properties exposed to the view public ProcessBookResult Result { get => _result; set { this.RaiseAndSetIfChanged(ref _result, value); this.RaisePropertyChanged(nameof(StatusText)); } } public ProcessBookStatus Status { get => _status; set { this.RaiseAndSetIfChanged(ref _status, value); this.RaisePropertyChanged(nameof(BackgroundColor)); this.RaisePropertyChanged(nameof(IsFinished)); this.RaisePropertyChanged(nameof(IsDownloading)); this.RaisePropertyChanged(nameof(Queued)); } } - public string Narrator { get => _narrator; set => Dispatcher.UIThread.Post(() => this.RaiseAndSetIfChanged(ref _narrator, value)); } - public string Author { get => _author; set => Dispatcher.UIThread.Post(() => this.RaiseAndSetIfChanged(ref _author, value)); } - public string Title { get => _title; set => Dispatcher.UIThread.Post(() => this.RaiseAndSetIfChanged(ref _title, value)); } - public int Progress { get => _progress; private set => Dispatcher.UIThread.Post(() => this.RaiseAndSetIfChanged(ref _progress, value)); } - public string ETA { get => _eta; private set => Dispatcher.UIThread.Post(() => this.RaiseAndSetIfChanged(ref _eta, value)); } - public Bitmap Cover { get => _cover; private set => Dispatcher.UIThread.Post(() => this.RaiseAndSetIfChanged(ref _cover, value)); } + public string Narrator { get => _narrator; set => Dispatcher.UIThread.Invoke(() => this.RaiseAndSetIfChanged(ref _narrator, value)); } + public string Author { get => _author; set => Dispatcher.UIThread.Invoke(() => this.RaiseAndSetIfChanged(ref _author, value)); } + public string Title { get => _title; set => Dispatcher.UIThread.Invoke(() => this.RaiseAndSetIfChanged(ref _title, value)); } + public int Progress { get => _progress; private set => Dispatcher.UIThread.Invoke(() => this.RaiseAndSetIfChanged(ref _progress, value)); } + public string ETA { get => _eta; private set => Dispatcher.UIThread.Invoke(() => this.RaiseAndSetIfChanged(ref _eta, value)); } + public Bitmap Cover { get => _cover; private set => Dispatcher.UIThread.Invoke(() => this.RaiseAndSetIfChanged(ref _cover, value)); } public bool IsFinished => Status is not ProcessBookStatus.Queued and not ProcessBookStatus.Working; public bool IsDownloading => Status is ProcessBookStatus.Working; public bool Queued => Status is ProcessBookStatus.Queued; diff --git a/Source/LibationAvalonia/ViewModels/ProcessQueueViewModel.cs b/Source/LibationAvalonia/ViewModels/ProcessQueueViewModel.cs index c7742e92..50e9cea1 100644 --- a/Source/LibationAvalonia/ViewModels/ProcessQueueViewModel.cs +++ b/Source/LibationAvalonia/ViewModels/ProcessQueueViewModel.cs @@ -45,11 +45,11 @@ namespace LibationAvalonia.ViewModels private bool _progressBarVisible; private decimal _speedLimit; - public int CompletedCount { get => _completedCount; private set => Dispatcher.UIThread.Post(() => { this.RaiseAndSetIfChanged(ref _completedCount, value); this.RaisePropertyChanged(nameof(AnyCompleted)); }); } - public int QueuedCount { get => _queuedCount; private set => Dispatcher.UIThread.Post(() => { this.RaiseAndSetIfChanged(ref _queuedCount, value); this.RaisePropertyChanged(nameof(AnyQueued)); }); } - public int ErrorCount { get => _errorCount; private set => Dispatcher.UIThread.Post(() => { this.RaiseAndSetIfChanged(ref _errorCount, value); this.RaisePropertyChanged(nameof(AnyErrors)); }); } - public string RunningTime { get => _runningTime; set => Dispatcher.UIThread.Post(() => { this.RaiseAndSetIfChanged(ref _runningTime, value); }); } - public bool ProgressBarVisible { get => _progressBarVisible; set => Dispatcher.UIThread.Post(() => { this.RaiseAndSetIfChanged(ref _progressBarVisible, value); }); } + public int CompletedCount { get => _completedCount; private set => Dispatcher.UIThread.Invoke(() => { this.RaiseAndSetIfChanged(ref _completedCount, value); this.RaisePropertyChanged(nameof(AnyCompleted)); }); } + public int QueuedCount { get => _queuedCount; private set => Dispatcher.UIThread.Invoke(() => { this.RaiseAndSetIfChanged(ref _queuedCount, value); this.RaisePropertyChanged(nameof(AnyQueued)); }); } + public int ErrorCount { get => _errorCount; private set => Dispatcher.UIThread.Invoke(() => { this.RaiseAndSetIfChanged(ref _errorCount, value); this.RaisePropertyChanged(nameof(AnyErrors)); }); } + public string RunningTime { get => _runningTime; set => Dispatcher.UIThread.Invoke(() => { this.RaiseAndSetIfChanged(ref _runningTime, value); }); } + public bool ProgressBarVisible { get => _progressBarVisible; set => Dispatcher.UIThread.Invoke(() => { this.RaiseAndSetIfChanged(ref _progressBarVisible, value); }); } public bool AnyCompleted => CompletedCount > 0; public bool AnyQueued => QueuedCount > 0; public bool AnyErrors => ErrorCount > 0; @@ -79,7 +79,7 @@ namespace LibationAvalonia.ViewModels : _speedLimit > 1 ? 0.1m : 0.01m; - Dispatcher.UIThread.Post(() => + Dispatcher.UIThread.Invoke(() => { this.RaisePropertyChanged(nameof(SpeedLimitIncrement)); this.RaisePropertyChanged(); @@ -106,7 +106,7 @@ namespace LibationAvalonia.ViewModels public void WriteLine(string text) { - Dispatcher.UIThread.Post(() => + Dispatcher.UIThread.Invoke(() => LogEntries.Add(new() { LogDate = DateTime.Now, @@ -183,7 +183,7 @@ namespace LibationAvalonia.ViewModels public void AddToQueue(IEnumerable pbook) { - Dispatcher.UIThread.Post(() => + Dispatcher.UIThread.Invoke(() => { Queue.Enqueue(pbook); if (!Running) diff --git a/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs b/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs index 9fe44d1b..bd85f92f 100644 --- a/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs +++ b/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs @@ -252,8 +252,8 @@ namespace LibationAvalonia.Views var displayIndices = config.GridColumnsDisplayIndices; var contextMenu = new ContextMenu(); - contextMenu.MenuClosed += ContextMenu_MenuClosed; - contextMenu.ContextMenuOpening += ContextMenu_ContextMenuOpening; + contextMenu.Closed += ContextMenu_MenuClosed; + contextMenu.Opening += ContextMenu_ContextMenuOpening; List menuItems = new(); contextMenu.ItemsSource = menuItems; diff --git a/Source/LibationAvalonia/Walkthrough.cs b/Source/LibationAvalonia/Walkthrough.cs index 8cee6272..850dd1c3 100644 --- a/Source/LibationAvalonia/Walkthrough.cs +++ b/Source/LibationAvalonia/Walkthrough.cs @@ -248,7 +248,7 @@ namespace LibationAvalonia private async Task displayControlAsync(TemplatedControl control) { await UIThread.InvokeAsync(() => control.IsEnabled = false); - await UIThread.InvokeAsync(MainForm.productsDisplay.Focus); + await UIThread.InvokeAsync(() => MainForm.productsDisplay.Focus()); await UIThread.InvokeAsync(() => flashControlAsync(control)); if (control is MenuItem menuItem) await UIThread.InvokeAsync(menuItem.Open); await Task.Delay(500); diff --git a/Source/LibationFileManager/LibraryBookDto.cs b/Source/LibationFileManager/LibraryBookDto.cs index 859f18c9..861125e9 100644 --- a/Source/LibationFileManager/LibraryBookDto.cs +++ b/Source/LibationFileManager/LibraryBookDto.cs @@ -22,6 +22,7 @@ namespace LibationFileManager public string SeriesName { get; set; } public int? SeriesNumber { get; set; } public bool IsSeries => !string.IsNullOrEmpty(SeriesName); + public bool IsPodcastParent { get; set; } public bool IsPodcast { get; set; } public int BitRate { get; set; } diff --git a/Source/LibationFileManager/TemplateTags.cs b/Source/LibationFileManager/TemplateTags.cs index 8adcd4d6..d0beb15e 100644 --- a/Source/LibationFileManager/TemplateTags.cs +++ b/Source/LibationFileManager/TemplateTags.cs @@ -47,6 +47,7 @@ namespace LibationFileManager public static TemplateTags DateAdded { get; } = new TemplateTags("date added", "Date added to your Audible account. e.g. yyyy-MM-dd", $"", ""); public static TemplateTags IfSeries { get; } = new TemplateTags("if series", "Only include if part of a book series or podcast", "<-if series>", "...<-if series>"); public static TemplateTags IfPodcast { get; } = new TemplateTags("if podcast", "Only include if part of a podcast", "<-if podcast>", "...<-if podcast>"); + public static TemplateTags IfPodcastParent { get; } = new TemplateTags("if podcastparent", "Only include if item is a podcast series parent", "<-if podcastparent>", "...<-if podcastparent>"); public static TemplateTags IfBookseries { get; } = new TemplateTags("if bookseries", "Only include if part of a book series", "<-if bookseries>", "...<-if bookseries>"); } } diff --git a/Source/LibationFileManager/Templates.cs b/Source/LibationFileManager/Templates.cs index f68abeb5..e416d3b1 100644 --- a/Source/LibationFileManager/Templates.cs +++ b/Source/LibationFileManager/Templates.cs @@ -207,13 +207,13 @@ namespace LibationFileManager { TemplateTags.Narrator, lb => lb.Narrators, NameListFormat.Formatter }, { TemplateTags.FirstNarrator, lb => lb.FirstNarrator }, { TemplateTags.Series, lb => lb.SeriesName }, - { TemplateTags.SeriesNumber, lb => lb.SeriesNumber }, + { TemplateTags.SeriesNumber, lb => lb.IsPodcastParent ? null : lb.SeriesNumber }, { TemplateTags.Language, lb => lb.Language }, //Don't allow formatting of LanguageShort { TemplateTags.LanguageShort, lb =>lb.Language, getLanguageShort }, - { TemplateTags.Bitrate, lb => lb.BitRate }, - { TemplateTags.SampleRate, lb => lb.SampleRate }, - { TemplateTags.Channels, lb => lb.Channels }, + { TemplateTags.Bitrate, lb => (int?)(lb.IsPodcastParent ? null : lb.BitRate) }, + { TemplateTags.SampleRate, lb => (int?)(lb.IsPodcastParent ? null : lb.SampleRate) }, + { TemplateTags.Channels, lb => (int?)(lb.IsPodcastParent ? null : lb.Channels) }, { TemplateTags.Account, lb => lb.Account }, { TemplateTags.Locale, lb => lb.Locale }, { TemplateTags.YearPublished, lb => lb.YearPublished }, @@ -242,9 +242,14 @@ namespace LibationFileManager private static readonly ConditionalTagCollection conditionalTags = new() { - { TemplateTags.IfSeries, lb => lb.IsSeries }, - { TemplateTags.IfPodcast, lb => lb.IsPodcast }, - { TemplateTags.IfBookseries, lb => lb.IsSeries && !lb.IsPodcast }, + { TemplateTags.IfSeries, lb => lb.IsSeries || lb.IsPodcastParent }, + { TemplateTags.IfPodcast, lb => lb.IsPodcast || lb.IsPodcastParent }, + { TemplateTags.IfBookseries, lb => lb.IsSeries && !lb.IsPodcast && !lb.IsPodcastParent }, + }; + + private static readonly ConditionalTagCollection folderConditionalTags = new() + { + { TemplateTags.IfPodcastParent, lb => lb.IsPodcastParent } }; #endregion @@ -293,7 +298,8 @@ namespace LibationFileManager public static string Name { get; }= "Folder Template"; public static string Description { get; } = Configuration.GetDescription(nameof(Configuration.FolderTemplate)); public static string DefaultTemplate { get; } = " [<id>]"; - public static IEnumerable<TagCollection> TagCollections => new TagCollection[] { filePropertyTags, conditionalTags }; + public static IEnumerable<TagCollection> TagCollections + => new TagCollection[] { filePropertyTags, conditionalTags, folderConditionalTags }; public override IEnumerable<string> Errors => TemplateText?.Length >= 2 && Path.IsPathFullyQualified(TemplateText) ? base.Errors.Append(ERROR_FULL_PATH_IS_INVALID) : base.Errors;