diff --git a/Source/AaxDecrypter/AaxDecrypter.csproj b/Source/AaxDecrypter/AaxDecrypter.csproj
index ea3d58f3..91ce5ac5 100644
--- a/Source/AaxDecrypter/AaxDecrypter.csproj
+++ b/Source/AaxDecrypter/AaxDecrypter.csproj
@@ -13,7 +13,7 @@
-
+
diff --git a/Source/AaxDecrypter/AaxcDownloadConvertBase.cs b/Source/AaxDecrypter/AaxcDownloadConvertBase.cs
index 9476e4b7..1b309e59 100644
--- a/Source/AaxDecrypter/AaxcDownloadConvertBase.cs
+++ b/Source/AaxDecrypter/AaxcDownloadConvertBase.cs
@@ -40,8 +40,14 @@ namespace AaxDecrypter
AaxFile.AppleTags.Album = AaxFile.AppleTags.Album?.Replace(" (Unabridged)", "");
}
- if (DownloadOptions.FixupFile && !string.IsNullOrWhiteSpace(AaxFile.AppleTags.Narrator))
- AaxFile.AppleTags.AppleListBox.EditOrAddTag("TCOM", AaxFile.AppleTags.Narrator);
+ if (DownloadOptions.FixupFile)
+ {
+ if (!string.IsNullOrWhiteSpace(AaxFile.AppleTags.Narrator))
+ AaxFile.AppleTags.AppleListBox.EditOrAddTag("TCOM", AaxFile.AppleTags.Narrator);
+
+ if (!string.IsNullOrWhiteSpace(AaxFile.AppleTags.Copyright))
+ AaxFile.AppleTags.Copyright = AaxFile.AppleTags.Copyright.Replace("(P)", "℗").Replace("©", "©");
+ }
//Finishing configuring lame encoder.
if (DownloadOptions.OutputFormat == OutputFormat.Mp3)
diff --git a/Source/AaxDecrypter/AaxcDownloadMultiConverter.cs b/Source/AaxDecrypter/AaxcDownloadMultiConverter.cs
index 0a8fb4e9..917e42c8 100644
--- a/Source/AaxDecrypter/AaxcDownloadMultiConverter.cs
+++ b/Source/AaxDecrypter/AaxcDownloadMultiConverter.cs
@@ -93,15 +93,13 @@ That naming may not be desirable for everyone, but it's an easy change to instea
? AaxFile.ConvertToMultiMp4aAsync
(
splitChapters,
- newSplitCallback => newSplit(++chapterCount, splitChapters, newSplitCallback),
- DownloadOptions.TrimOutputToChapterLength
+ newSplitCallback => newSplit(++chapterCount, splitChapters, newSplitCallback)
)
: AaxFile.ConvertToMultiMp3Async
(
splitChapters,
newSplitCallback => newSplit(++chapterCount, splitChapters, newSplitCallback),
- DownloadOptions.LameConfig,
- DownloadOptions.TrimOutputToChapterLength
+ DownloadOptions.LameConfig
);
void newSplit(int currentChapter, ChapterInfo splitChapters, NewSplitCallback newSplitCallback)
diff --git a/Source/AaxDecrypter/AaxcDownloadSingleConverter.cs b/Source/AaxDecrypter/AaxcDownloadSingleConverter.cs
index 77674e74..4a2b60f3 100644
--- a/Source/AaxDecrypter/AaxcDownloadSingleConverter.cs
+++ b/Source/AaxDecrypter/AaxcDownloadSingleConverter.cs
@@ -3,6 +3,7 @@ using AAXClean.Codecs;
using Dinah.Core.Net.Http;
using FileManager;
using System;
+using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
@@ -14,12 +15,15 @@ namespace AaxDecrypter
public AaxcDownloadSingleConverter(string outFileName, string cacheDirectory, IDownloadOptions dlOptions)
: base(outFileName, cacheDirectory, dlOptions)
{
+ var step = 1;
AsyncSteps.Name = $"Download and Convert Aaxc To {DownloadOptions.OutputFormat}";
- AsyncSteps["Step 1: Get Aaxc Metadata"] = () => Task.Run(Step_GetMetadata);
- AsyncSteps["Step 2: Download Decrypted Audiobook"] = Step_DownloadAndDecryptAudiobookAsync;
- AsyncSteps["Step 3: Download Clips and Bookmarks"] = Step_DownloadClipsBookmarksAsync;
- AsyncSteps["Step 4: Create Cue"] = Step_CreateCueAsync;
+ AsyncSteps[$"Step {step++}: Get Aaxc Metadata"] = () => Task.Run(Step_GetMetadata);
+ AsyncSteps[$"Step {step++}: Download Decrypted Audiobook"] = Step_DownloadAndDecryptAudiobookAsync;
+ if (DownloadOptions.MoveMoovToBeginning && DownloadOptions.OutputFormat is OutputFormat.M4b)
+ AsyncSteps[$"Step {step++}: Move moov atom to beginning"] = Step_MoveMoov;
+ AsyncSteps[$"Step {step++}: Download Clips and Bookmarks"] = Step_DownloadClipsBookmarksAsync;
+ AsyncSteps[$"Step {step++}: Create Cue"] = Step_CreateCueAsync;
}
protected async override Task Step_DownloadAndDecryptAudiobookAsync()
@@ -31,18 +35,7 @@ namespace AaxDecrypter
try
{
- await (AaxConversion = decryptAsync(outputFile));
-
- if (AaxConversion.IsCompletedSuccessfully
- && DownloadOptions.MoveMoovToBeginning
- && DownloadOptions.OutputFormat is OutputFormat.M4b)
- {
- outputFile.Close();
- AaxConversion = Mp4File.RelocateMoovAsync(OutputFileName);
- AaxConversion.ConversionProgressUpdate += AaxConversion_MoovProgressUpdate;
- await AaxConversion;
- AaxConversion.ConversionProgressUpdate -= AaxConversion_MoovProgressUpdate;
- }
+ await (AaxConversion = decryptAsync(outputFile));
return AaxConversion.IsCompletedSuccessfully;
}
@@ -52,23 +45,30 @@ namespace AaxDecrypter
}
}
+ private async Task Step_MoveMoov()
+ {
+ AaxConversion = Mp4File.RelocateMoovAsync(OutputFileName);
+ AaxConversion.ConversionProgressUpdate += AaxConversion_MoovProgressUpdate;
+ await AaxConversion;
+ AaxConversion.ConversionProgressUpdate -= AaxConversion_MoovProgressUpdate;
+ return AaxConversion.IsCompletedSuccessfully;
+ }
+
private void AaxConversion_MoovProgressUpdate(object sender, ConversionProgressEventArgs e)
{
averageSpeed.AddPosition(e.ProcessPosition.TotalSeconds);
- var remainingTimeToProcess = (e.TotalDuration - e.ProcessPosition).TotalSeconds;
+ var remainingTimeToProcess = (e.EndTime - e.ProcessPosition).TotalSeconds;
var estTimeRemaining = remainingTimeToProcess / averageSpeed.Average;
if (double.IsNormal(estTimeRemaining))
OnDecryptTimeRemaining(TimeSpan.FromSeconds(estTimeRemaining));
- var progressPercent = 100d * (1 - remainingTimeToProcess / e.TotalDuration.TotalSeconds);
-
OnDecryptProgressUpdate(
new DownloadProgress
{
- ProgressPercentage = progressPercent,
- BytesReceived = (long)(InputFileStream.Length * progressPercent),
+ ProgressPercentage = 100 * e.FractionCompleted,
+ BytesReceived = (long)(InputFileStream.Length * e.FractionCompleted),
TotalBytesToReceive = InputFileStream.Length
});
}
@@ -79,15 +79,13 @@ namespace AaxDecrypter
(
outputFile,
DownloadOptions.LameConfig,
- DownloadOptions.ChapterInfo,
- DownloadOptions.TrimOutputToChapterLength
+ DownloadOptions.ChapterInfo
)
: DownloadOptions.FixupFile
? AaxFile.ConvertToMp4aAsync
(
outputFile,
- DownloadOptions.ChapterInfo,
- DownloadOptions.TrimOutputToChapterLength
+ DownloadOptions.ChapterInfo
)
: AaxFile.ConvertToMp4aAsync(outputFile);
}
diff --git a/Source/AaxDecrypter/AudiobookDownloadBase.cs b/Source/AaxDecrypter/AudiobookDownloadBase.cs
index fbbb3716..fff17d58 100644
--- a/Source/AaxDecrypter/AudiobookDownloadBase.cs
+++ b/Source/AaxDecrypter/AudiobookDownloadBase.cs
@@ -27,6 +27,7 @@ namespace AaxDecrypter
protected IDownloadOptions DownloadOptions { get; }
protected NetworkFileStream InputFileStream => nfsPersister.NetworkFileStream;
protected virtual long InputFilePosition => InputFileStream.Position;
+ private bool downloadFinished;
private readonly NetworkFileStreamPersister nfsPersister;
private readonly DownloadProgress zeroProgress;
@@ -84,7 +85,11 @@ namespace AaxDecrypter
{
AverageSpeed averageSpeed = new();
- while (InputFileStream.CanRead && InputFileStream.Length > InputFilePosition && !InputFileStream.IsCancelled)
+ while (
+ InputFileStream.CanRead
+ && InputFileStream.Length > InputFilePosition
+ && !InputFileStream.IsCancelled
+ && !downloadFinished)
{
averageSpeed.AddPosition(InputFilePosition);
@@ -138,8 +143,7 @@ namespace AaxDecrypter
protected virtual void FinalizeDownload()
{
nfsPersister?.Dispose();
- OnDecryptTimeRemaining(TimeSpan.Zero);
- OnDecryptProgressUpdate(zeroProgress);
+ downloadFinished = true;
}
protected async Task Step_DownloadClipsBookmarksAsync()
diff --git a/Source/AaxDecrypter/MpegUtil.cs b/Source/AaxDecrypter/MpegUtil.cs
index 4d35fd2d..d7ab9c37 100644
--- a/Source/AaxDecrypter/MpegUtil.cs
+++ b/Source/AaxDecrypter/MpegUtil.cs
@@ -1,5 +1,6 @@
using AAXClean;
using NAudio.Lame;
+using System;
namespace AaxDecrypter
{
@@ -9,17 +10,26 @@ namespace AaxDecrypter
{
double bitrateMultiple = 1;
+ if (mp4File.TimeScale < lameConfig.OutputSampleRate)
+ {
+ lameConfig.OutputSampleRate = mp4File.TimeScale;
+ }
+ else if (mp4File.TimeScale > lameConfig.OutputSampleRate)
+ {
+ bitrateMultiple *= (double)lameConfig.OutputSampleRate / mp4File.TimeScale;
+ }
+
if (mp4File.AudioChannels == 2)
{
if (downsample)
- bitrateMultiple = 0.5;
+ bitrateMultiple /= 2;
else
lameConfig.Mode = MPEGMode.Stereo;
}
if (matchSourceBitrate)
{
- int kbps = (int)(mp4File.AverageBitrate * bitrateMultiple / 1024);
+ int kbps = (int)Math.Round(mp4File.AverageBitrate * bitrateMultiple / 1024);
if (lameConfig.VBR is null)
lameConfig.BitRate = kbps;
diff --git a/Source/AaxDecrypter/NetworkFileStream.cs b/Source/AaxDecrypter/NetworkFileStream.cs
index 53f1753e..bd8ce19c 100644
--- a/Source/AaxDecrypter/NetworkFileStream.cs
+++ b/Source/AaxDecrypter/NetworkFileStream.cs
@@ -14,6 +14,7 @@ namespace AaxDecrypter
public class NetworkFileStream : Stream, IUpdatable
{
public event EventHandler Updated;
+ public event EventHandler DownloadCompleted;
#region Public Properties
@@ -139,7 +140,10 @@ namespace AaxDecrypter
public async Task BeginDownloadingAsync()
{
if (ContentLength != 0 && WritePosition == ContentLength)
+ {
+ _backgroundDownloadTask = Task.CompletedTask;
return;
+ }
if (ContentLength != 0 && WritePosition > ContentLength)
throw new WebException($"Specified write position (0x{WritePosition:X10}) is larger than {nameof(ContentLength)} (0x{ContentLength:X10}).");
@@ -230,6 +234,7 @@ namespace AaxDecrypter
_writeFile.Close();
_downloadedPiece.Set();
OnUpdate();
+ DownloadCompleted?.Invoke(this, null);
}
}
diff --git a/Source/AaxDecrypter/UnencryptedAudiobookDownloader.cs b/Source/AaxDecrypter/UnencryptedAudiobookDownloader.cs
index 138c8ea0..193cd9b6 100644
--- a/Source/AaxDecrypter/UnencryptedAudiobookDownloader.cs
+++ b/Source/AaxDecrypter/UnencryptedAudiobookDownloader.cs
@@ -26,8 +26,11 @@ namespace AaxDecrypter
protected override async Task Step_DownloadAndDecryptAudiobookAsync()
{
- while (InputFilePosition < InputFileStream.Length && !InputFileStream.IsCancelled)
- await Task.Delay(200);
+ TaskCompletionSource completionSource = new();
+
+ InputFileStream.DownloadCompleted += (_, _) => completionSource.SetResult();
+
+ await completionSource.Task;
if (IsCanceled)
return false;
diff --git a/Source/FileLiberator/AudioDecodable.cs b/Source/FileLiberator/AudioDecodable.cs
index 800b711b..9f6a803d 100644
--- a/Source/FileLiberator/AudioDecodable.cs
+++ b/Source/FileLiberator/AudioDecodable.cs
@@ -17,10 +17,14 @@ namespace FileLiberator
protected LameConfig GetLameOptions(Configuration config)
{
- LameConfig lameConfig = new();
- lameConfig.Mode = MPEGMode.Mono;
+ LameConfig lameConfig = new()
+ {
+ Mode = MPEGMode.Mono,
+ Quality = config.LameEncoderQuality,
+ OutputSampleRate = (int)config.MaxSampleRate
+ };
- if (config.LameTargetBitrate)
+ if (config.LameTargetBitrate)
{
if (config.LameConstantBitrate)
lameConfig.BitRate = config.LameBitrate;
diff --git a/Source/FileLiberator/ConvertToMp3.cs b/Source/FileLiberator/ConvertToMp3.cs
index 5b487d4f..4f70b1ef 100644
--- a/Source/FileLiberator/ConvertToMp3.cs
+++ b/Source/FileLiberator/ConvertToMp3.cs
@@ -103,20 +103,20 @@ namespace FileLiberator
{
averageSpeed.AddPosition(e.ProcessPosition.TotalSeconds);
- var remainingTimeToProcess = (e.TotalDuration - e.ProcessPosition).TotalSeconds;
+ var remainingTimeToProcess = (e.EndTime - e.ProcessPosition).TotalSeconds;
var estTimeRemaining = remainingTimeToProcess / averageSpeed.Average;
if (double.IsNormal(estTimeRemaining))
OnStreamingTimeRemaining(TimeSpan.FromSeconds(estTimeRemaining));
- double progressPercent = 100 * e.ProcessPosition.TotalSeconds / e.TotalDuration.TotalSeconds;
+ double progressPercent = 100 * e.FractionCompleted;
OnStreamingProgressChanged(
new DownloadProgress
{
ProgressPercentage = progressPercent,
- BytesReceived = (long)e.ProcessPosition.TotalSeconds,
- TotalBytesToReceive = (long)e.TotalDuration.TotalSeconds
+ BytesReceived = (long)(e.ProcessPosition - e.StartTime).TotalSeconds,
+ TotalBytesToReceive = (long)(e.EndTime - e.StartTime).TotalSeconds
});
}
}
diff --git a/Source/FileLiberator/DownloadOptions.cs b/Source/FileLiberator/DownloadOptions.cs
index 94315542..7b46170d 100644
--- a/Source/FileLiberator/DownloadOptions.cs
+++ b/Source/FileLiberator/DownloadOptions.cs
@@ -22,7 +22,7 @@ namespace FileLiberator
public OutputFormat OutputFormat { get; init; }
public ChapterInfo ChapterInfo { get; init; }
public NAudio.Lame.LameConfig LameConfig { get; init; }
- public string UserAgent => AudibleApi.Resources.User_Agent;
+ public string UserAgent => AudibleApi.Resources.Download_User_Agent;
public bool TrimOutputToChapterLength => config.AllowLibationFixup && config.StripAudibleBrandAudio;
public bool StripUnabridged => config.AllowLibationFixup && config.StripUnabridged;
public bool CreateCueSheet => config.CreateCueSheet;
diff --git a/Source/LibationAvalonia/Dialogs/SettingsDialog.axaml b/Source/LibationAvalonia/Dialogs/SettingsDialog.axaml
index c2b17889..ffaa7fc9 100644
--- a/Source/LibationAvalonia/Dialogs/SettingsDialog.axaml
+++ b/Source/LibationAvalonia/Dialogs/SettingsDialog.axaml
@@ -2,8 +2,8 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- mc:Ignorable="d" d:DesignWidth="850" d:DesignHeight="620"
- MinWidth="800" MinHeight="620"
+ mc:Ignorable="d" d:DesignWidth="900" d:DesignHeight="700"
+ MinWidth="900" MinHeight="700"
x:Class="LibationAvalonia.Dialogs.SettingsDialog"
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
Title="Edit Settings"
@@ -603,6 +603,25 @@
+
+
+
+
+
+
+
+
+
+
SampleRates { get; }
+ = new(
+ new []
+ {
+ AAXClean.SampleRate.Hz_44100,
+ AAXClean.SampleRate.Hz_32000,
+ AAXClean.SampleRate.Hz_24000,
+ AAXClean.SampleRate.Hz_22050,
+ AAXClean.SampleRate.Hz_16000,
+ AAXClean.SampleRate.Hz_12000,
+ }
+ .Select(s => new SampleRateSelection(s)));
+
+ public AvaloniaList EncoderQualities { get; }
+ = new(
+ new[]
+ {
+ NAudio.Lame.EncoderQuality.High,
+ NAudio.Lame.EncoderQuality.Standard,
+ NAudio.Lame.EncoderQuality.Fast,
+ });
+
public AudioSettings(Configuration config)
{
@@ -398,6 +424,9 @@ namespace LibationAvalonia.Dialogs
LameMatchSource = config.LameMatchSourceBR;
LameBitrate = config.LameBitrate;
LameVBRQuality = config.LameVBRQuality;
+
+ SelectedSampleRate = SampleRates.FirstOrDefault(s => s.SampleRate == config.MaxSampleRate);
+ SelectedEncoderQuality = config.LameEncoderQuality;
}
public Task SaveSettingsAsync(Configuration config)
@@ -422,6 +451,9 @@ namespace LibationAvalonia.Dialogs
config.LameBitrate = LameBitrate;
config.LameVBRQuality = LameVBRQuality;
+ config.LameEncoderQuality = SelectedEncoderQuality;
+ config.MaxSampleRate = SelectedSampleRate?.SampleRate ?? config.MaxSampleRate;
+
return Task.FromResult(true);
}
diff --git a/Source/LibationAvalonia/Views/MainWindow.ProcessQueue.cs b/Source/LibationAvalonia/Views/MainWindow.ProcessQueue.cs
index d6bdad54..77e8b626 100644
--- a/Source/LibationAvalonia/Views/MainWindow.ProcessQueue.cs
+++ b/Source/LibationAvalonia/Views/MainWindow.ProcessQueue.cs
@@ -47,6 +47,23 @@ namespace LibationAvalonia.Views
Serilog.Log.Logger.Error(ex, "An error occurred while handling the stop light button click for {libraryBook}", libraryBook);
}
}
+
+ public void ProductsDisplay_ConvertToMp3Clicked(object sender, LibraryBook libraryBook)
+ {
+ try
+ {
+ if (libraryBook.Book.UserDefinedItem.BookStatus is LiberatedStatus.Liberated)
+ {
+ Serilog.Log.Logger.Information("Begin single pdf backup of {libraryBook}", libraryBook);
+ SetQueueCollapseState(false);
+ _viewModel.ProcessQueue.AddConvertMp3(libraryBook);
+ }
+ }
+ catch (Exception ex)
+ {
+ Serilog.Log.Logger.Error(ex, "An error occurred while handling the stop light button click for {libraryBook}", libraryBook);
+ }
+ }
private void SetQueueCollapseState(bool collapsed)
{
_viewModel.QueueOpen = !collapsed;
diff --git a/Source/LibationAvalonia/Views/MainWindow.axaml b/Source/LibationAvalonia/Views/MainWindow.axaml
index 6241135c..c1a44e47 100644
--- a/Source/LibationAvalonia/Views/MainWindow.axaml
+++ b/Source/LibationAvalonia/Views/MainWindow.axaml
@@ -188,7 +188,8 @@
+ LiberateClicked="ProductsDisplay_LiberateClicked"
+ ConvertToMp3Clicked="ProductsDisplay_ConvertToMp3Clicked" />
diff --git a/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs b/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs
index 3bdd2d6d..c7f2a29a 100644
--- a/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs
+++ b/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs
@@ -19,6 +19,7 @@ namespace LibationAvalonia.Views
public partial class ProductsDisplay : UserControl
{
public event EventHandler LiberateClicked;
+ public event EventHandler ConvertToMp3Clicked;
private ProductsDisplayViewModel _viewModel => DataContext as ProductsDisplayViewModel;
ImageDisplayDialog imageDisplayDialog;
@@ -131,6 +132,12 @@ namespace LibationAvalonia.Views
await MessageBox.ShowAdminAlert(null, msg, msg, ex);
}
};
+ var convertToMp3MenuItem = new MenuItem
+ {
+ Header = "_Convert to Mp3",
+ IsEnabled = entry.Book.UserDefinedItem.BookStatus != LiberatedStatus.NotLiberated
+ };
+ convertToMp3MenuItem.Click += (_, _) => ConvertToMp3Clicked?.Invoke(this, entry.LibraryBook);
var bookRecordMenuItem = new MenuItem { Header = "View _Bookmarks/Clips" };
bookRecordMenuItem.Click += async (_, _) => await new BookRecordsDialog(entry.LibraryBook).ShowDialog(VisualRoot as Window);
@@ -141,6 +148,7 @@ namespace LibationAvalonia.Views
setNotDownloadMenuItem,
removeMenuItem,
locateFileMenuItem,
+ convertToMp3MenuItem,
new Separator(),
bookRecordMenuItem
});
diff --git a/Source/LibationFileManager/Configuration.PersistentSettings.cs b/Source/LibationFileManager/Configuration.PersistentSettings.cs
index 152ebe5f..4902d289 100644
--- a/Source/LibationFileManager/Configuration.PersistentSettings.cs
+++ b/Source/LibationFileManager/Configuration.PersistentSettings.cs
@@ -121,6 +121,12 @@ namespace LibationFileManager
[Description("Lame encoder target. true = Bitrate, false = Quality")]
public bool LameTargetBitrate { get => GetNonString(defaultValue: false); set => SetNonString(value); }
+ [Description("Maximum audio sample rate")]
+ public AAXClean.SampleRate MaxSampleRate { get => GetNonString(defaultValue: AAXClean.SampleRate.Hz_44100); set => SetNonString(value); }
+
+ [Description("Lame encoder quality")]
+ public NAudio.Lame.EncoderQuality LameEncoderQuality { get => GetNonString(defaultValue: NAudio.Lame.EncoderQuality.High); set => SetNonString(value); }
+
[Description("Lame encoder downsamples to mono")]
public bool LameDownsampleMono { get => GetNonString(defaultValue: true); set => SetNonString(value); }
diff --git a/Source/LibationUiBase/SampleRateSelection.cs b/Source/LibationUiBase/SampleRateSelection.cs
new file mode 100644
index 00000000..bc7c8ee0
--- /dev/null
+++ b/Source/LibationUiBase/SampleRateSelection.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace LibationUiBase
+{
+ public class SampleRateSelection
+ {
+ public AAXClean.SampleRate SampleRate { get; }
+ public SampleRateSelection(AAXClean.SampleRate sampleRate)
+ {
+ SampleRate = sampleRate;
+ }
+ public override string ToString() => $"{(int)SampleRate} Hz";
+ }
+}
diff --git a/Source/LibationWinForms/Dialogs/SettingsDialog.AudioSettings.cs b/Source/LibationWinForms/Dialogs/SettingsDialog.AudioSettings.cs
index 0647cc2b..c36e9652 100644
--- a/Source/LibationWinForms/Dialogs/SettingsDialog.AudioSettings.cs
+++ b/Source/LibationWinForms/Dialogs/SettingsDialog.AudioSettings.cs
@@ -1,6 +1,7 @@
using System;
using LibationFileManager;
using System.Linq;
+using LibationUiBase;
namespace LibationWinForms.Dialogs
{
@@ -26,6 +27,25 @@ namespace LibationWinForms.Dialogs
Configuration.ClipBookmarkFormat.Json
});
+ maxSampleRateCb.Items.AddRange(
+ new object[]
+ {
+ new SampleRateSelection(AAXClean.SampleRate.Hz_44100),
+ new SampleRateSelection(AAXClean.SampleRate.Hz_32000),
+ new SampleRateSelection(AAXClean.SampleRate.Hz_24000),
+ new SampleRateSelection(AAXClean.SampleRate.Hz_22050),
+ new SampleRateSelection(AAXClean.SampleRate.Hz_16000),
+ new SampleRateSelection(AAXClean.SampleRate.Hz_12000)
+ });
+
+ encoderQualityCb.Items.AddRange(
+ new object[]
+ {
+ NAudio.Lame.EncoderQuality.High,
+ NAudio.Lame.EncoderQuality.Standard,
+ NAudio.Lame.EncoderQuality.Fast,
+ });
+
allowLibationFixupCbox.Checked = config.AllowLibationFixup;
createCueSheetCbox.Checked = config.CreateCueSheet;
downloadCoverArtCbox.Checked = config.DownloadCoverArt;
@@ -42,6 +62,8 @@ namespace LibationWinForms.Dialogs
lameTargetBitrateRb.Checked = config.LameTargetBitrate;
lameTargetQualityRb.Checked = !config.LameTargetBitrate;
+ maxSampleRateCb.SelectedItem = maxSampleRateCb.Items.Cast().Single(s => s.SampleRate == config.MaxSampleRate);
+ encoderQualityCb.SelectedItem = config.LameEncoderQuality;
lameDownsampleMonoCbox.Checked = config.LameDownsampleMono;
lameBitrateTb.Value = config.LameBitrate;
lameConstantBitrateCbox.Checked = config.LameConstantBitrate;
@@ -75,6 +97,9 @@ namespace LibationWinForms.Dialogs
config.MoveMoovToBeginning = moveMoovAtomCbox.Checked;
config.LameTargetBitrate = lameTargetBitrateRb.Checked;
+ config.MaxSampleRate = ((SampleRateSelection)maxSampleRateCb.SelectedItem).SampleRate;
+ config.LameEncoderQuality = (NAudio.Lame.EncoderQuality)encoderQualityCb.SelectedItem;
+ encoderQualityCb.SelectedItem = config.LameEncoderQuality;
config.LameDownsampleMono = lameDownsampleMonoCbox.Checked;
config.LameBitrate = lameBitrateTb.Value;
config.LameConstantBitrate = lameConstantBitrateCbox.Checked;
diff --git a/Source/LibationWinForms/Dialogs/SettingsDialog.Designer.cs b/Source/LibationWinForms/Dialogs/SettingsDialog.Designer.cs
index 719a6e2c..dfea7df1 100644
--- a/Source/LibationWinForms/Dialogs/SettingsDialog.Designer.cs
+++ b/Source/LibationWinForms/Dialogs/SettingsDialog.Designer.cs
@@ -115,6 +115,10 @@
this.retainAaxFileCbox = new System.Windows.Forms.CheckBox();
this.downloadCoverArtCbox = new System.Windows.Forms.CheckBox();
this.createCueSheetCbox = new System.Windows.Forms.CheckBox();
+ this.maxSampleRateCb = new System.Windows.Forms.ComboBox();
+ this.encoderQualityCb = new System.Windows.Forms.ComboBox();
+ this.label20 = new System.Windows.Forms.Label();
+ this.label21 = new System.Windows.Forms.Label();
this.badBookGb.SuspendLayout();
this.tabControl.SuspendLayout();
this.tab1ImportantSettings.SuspendLayout();
@@ -743,6 +747,10 @@
//
// lameOptionsGb
//
+ this.lameOptionsGb.Controls.Add(this.label20);
+ this.lameOptionsGb.Controls.Add(this.label21);
+ this.lameOptionsGb.Controls.Add(this.encoderQualityCb);
+ this.lameOptionsGb.Controls.Add(this.maxSampleRateCb);
this.lameOptionsGb.Controls.Add(this.lameDownsampleMonoCbox);
this.lameOptionsGb.Controls.Add(this.lameBitrateGb);
this.lameOptionsGb.Controls.Add(this.label1);
@@ -757,8 +765,7 @@
//
// lameDownsampleMonoCbox
//
- this.lameDownsampleMonoCbox.AutoSize = true;
- this.lameDownsampleMonoCbox.Location = new System.Drawing.Point(234, 35);
+ this.lameDownsampleMonoCbox.Location = new System.Drawing.Point(237, 30);
this.lameDownsampleMonoCbox.Name = "lameDownsampleMonoCbox";
this.lameDownsampleMonoCbox.Size = new System.Drawing.Size(184, 34);
this.lameDownsampleMonoCbox.TabIndex = 1;
@@ -776,9 +783,9 @@
this.lameBitrateGb.Controls.Add(this.label11);
this.lameBitrateGb.Controls.Add(this.label3);
this.lameBitrateGb.Controls.Add(this.lameBitrateTb);
- this.lameBitrateGb.Location = new System.Drawing.Point(6, 84);
+ this.lameBitrateGb.Location = new System.Drawing.Point(6, 104);
this.lameBitrateGb.Name = "lameBitrateGb";
- this.lameBitrateGb.Size = new System.Drawing.Size(421, 101);
+ this.lameBitrateGb.Size = new System.Drawing.Size(421, 102);
this.lameBitrateGb.TabIndex = 0;
this.lameBitrateGb.TabStop = false;
this.lameBitrateGb.Text = "Bitrate";
@@ -786,7 +793,7 @@
// LameMatchSourceBRCbox
//
this.LameMatchSourceBRCbox.AutoSize = true;
- this.LameMatchSourceBRCbox.Location = new System.Drawing.Point(260, 77);
+ this.LameMatchSourceBRCbox.Location = new System.Drawing.Point(275, 76);
this.LameMatchSourceBRCbox.Name = "LameMatchSourceBRCbox";
this.LameMatchSourceBRCbox.Size = new System.Drawing.Size(140, 19);
this.LameMatchSourceBRCbox.TabIndex = 3;
@@ -883,7 +890,7 @@
this.label1.AutoSize = true;
this.label1.Enabled = false;
this.label1.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point);
- this.label1.Location = new System.Drawing.Point(6, 298);
+ this.label1.Location = new System.Drawing.Point(6, 325);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(172, 15);
this.label1.TabIndex = 1;
@@ -904,9 +911,9 @@
this.lameQualityGb.Controls.Add(this.label14);
this.lameQualityGb.Controls.Add(this.label2);
this.lameQualityGb.Controls.Add(this.lameVBRQualityTb);
- this.lameQualityGb.Location = new System.Drawing.Point(6, 186);
+ this.lameQualityGb.Location = new System.Drawing.Point(6, 212);
this.lameQualityGb.Name = "lameQualityGb";
- this.lameQualityGb.Size = new System.Drawing.Size(421, 109);
+ this.lameQualityGb.Size = new System.Drawing.Size(421, 103);
this.lameQualityGb.TabIndex = 0;
this.lameQualityGb.TabStop = false;
this.lameQualityGb.Text = "Quality";
@@ -986,7 +993,7 @@
// label13
//
this.label13.AutoSize = true;
- this.label13.Location = new System.Drawing.Point(376, 81);
+ this.label13.Location = new System.Drawing.Point(376, 80);
this.label13.Name = "label13";
this.label13.Size = new System.Drawing.Size(39, 15);
this.label13.TabIndex = 1;
@@ -995,7 +1002,7 @@
// label10
//
this.label10.AutoSize = true;
- this.label10.Location = new System.Drawing.Point(6, 81);
+ this.label10.Location = new System.Drawing.Point(6, 80);
this.label10.Name = "label10";
this.label10.Size = new System.Drawing.Size(43, 15);
this.label10.TabIndex = 1;
@@ -1036,7 +1043,7 @@
this.groupBox2.Controls.Add(this.lameTargetBitrateRb);
this.groupBox2.Location = new System.Drawing.Point(6, 22);
this.groupBox2.Name = "groupBox2";
- this.groupBox2.Size = new System.Drawing.Size(222, 56);
+ this.groupBox2.Size = new System.Drawing.Size(214, 47);
this.groupBox2.TabIndex = 0;
this.groupBox2.TabStop = false;
this.groupBox2.Text = "Target";
@@ -1044,7 +1051,7 @@
// lameTargetQualityRb
//
this.lameTargetQualityRb.AutoSize = true;
- this.lameTargetQualityRb.Location = new System.Drawing.Point(138, 23);
+ this.lameTargetQualityRb.Location = new System.Drawing.Point(139, 22);
this.lameTargetQualityRb.Name = "lameTargetQualityRb";
this.lameTargetQualityRb.Size = new System.Drawing.Size(63, 19);
this.lameTargetQualityRb.TabIndex = 0;
@@ -1056,7 +1063,7 @@
// lameTargetBitrateRb
//
this.lameTargetBitrateRb.AutoSize = true;
- this.lameTargetBitrateRb.Location = new System.Drawing.Point(6, 23);
+ this.lameTargetBitrateRb.Location = new System.Drawing.Point(6, 22);
this.lameTargetBitrateRb.Name = "lameTargetBitrateRb";
this.lameTargetBitrateRb.Size = new System.Drawing.Size(59, 19);
this.lameTargetBitrateRb.TabIndex = 0;
@@ -1112,6 +1119,42 @@
this.createCueSheetCbox.UseVisualStyleBackColor = true;
this.createCueSheetCbox.CheckedChanged += new System.EventHandler(this.allowLibationFixupCbox_CheckedChanged);
//
+ // maxSampleRateCb
+ //
+ this.maxSampleRateCb.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.maxSampleRateCb.FormattingEnabled = true;
+ this.maxSampleRateCb.Location = new System.Drawing.Point(119, 75);
+ this.maxSampleRateCb.Name = "maxSampleRateCb";
+ this.maxSampleRateCb.Size = new System.Drawing.Size(101, 23);
+ this.maxSampleRateCb.TabIndex = 2;
+ //
+ // encoderQualityCb
+ //
+ this.encoderQualityCb.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.encoderQualityCb.FormattingEnabled = true;
+ this.encoderQualityCb.Location = new System.Drawing.Point(337, 75);
+ this.encoderQualityCb.Name = "encoderQualityCb";
+ this.encoderQualityCb.Size = new System.Drawing.Size(90, 23);
+ this.encoderQualityCb.TabIndex = 2;
+ //
+ // label20
+ //
+ this.label20.AutoSize = true;
+ this.label20.Location = new System.Drawing.Point(12, 78);
+ this.label20.Name = "label20";
+ this.label20.Size = new System.Drawing.Size(101, 15);
+ this.label20.TabIndex = 3;
+ this.label20.Text = "Max Sample Rate:";
+ //
+ // label21
+ //
+ this.label21.AutoSize = true;
+ this.label21.Location = new System.Drawing.Point(239, 78);
+ this.label21.Name = "label21";
+ this.label21.Size = new System.Drawing.Size(94, 15);
+ this.label21.TabIndex = 3;
+ this.label21.Text = "Encoder Quality:";
+ //
// SettingsDialog
//
this.AcceptButton = this.saveBtn;
@@ -1253,5 +1296,9 @@
private System.Windows.Forms.ComboBox clipsBookmarksFormatCb;
private System.Windows.Forms.CheckBox downloadClipsBookmarksCbox;
private System.Windows.Forms.CheckBox moveMoovAtomCbox;
+ private System.Windows.Forms.ComboBox encoderQualityCb;
+ private System.Windows.Forms.ComboBox maxSampleRateCb;
+ private System.Windows.Forms.Label label21;
+ private System.Windows.Forms.Label label20;
}
}
\ No newline at end of file
diff --git a/Source/LibationWinForms/Form1.Designer.cs b/Source/LibationWinForms/Form1.Designer.cs
index fc51a930..4a525c52 100644
--- a/Source/LibationWinForms/Form1.Designer.cs
+++ b/Source/LibationWinForms/Form1.Designer.cs
@@ -520,6 +520,7 @@
this.productsDisplay.VisibleCountChanged += new System.EventHandler(this.productsDisplay_VisibleCountChanged);
this.productsDisplay.RemovableCountChanged += new System.EventHandler(this.productsDisplay_RemovableCountChanged);
this.productsDisplay.LiberateClicked += new System.EventHandler(this.ProductsDisplay_LiberateClicked);
+ this.productsDisplay.ConvertToMp3Clicked += new System.EventHandler(this.ProductsDisplay_ConvertToMp3Clicked);
this.productsDisplay.InitialLoaded += new System.EventHandler(this.productsDisplay_InitialLoaded);
//
// toggleQueueHideBtn
diff --git a/Source/LibationWinForms/Form1.ProcessQueue.cs b/Source/LibationWinForms/Form1.ProcessQueue.cs
index 398e0802..dfd7ac90 100644
--- a/Source/LibationWinForms/Form1.ProcessQueue.cs
+++ b/Source/LibationWinForms/Form1.ProcessQueue.cs
@@ -56,6 +56,23 @@ namespace LibationWinForms
}
}
+ private void ProductsDisplay_ConvertToMp3Clicked(object sender, LibraryBook libraryBook)
+ {
+ try
+ {
+ if (libraryBook.Book.UserDefinedItem.BookStatus is LiberatedStatus.Liberated)
+ {
+ Serilog.Log.Logger.Information("Begin single pdf backup of {libraryBook}", libraryBook);
+ SetQueueCollapseState(false);
+ processBookQueue1.AddConvertMp3(libraryBook);
+ }
+ }
+ catch (Exception ex)
+ {
+ Serilog.Log.Logger.Error(ex, "An error occurred while handling the stop light button click for {libraryBook}", libraryBook);
+ }
+ }
+
private void SetQueueCollapseState(bool collapsed)
{
if (collapsed && !splitContainer1.Panel2Collapsed)
diff --git a/Source/LibationWinForms/GridView/ProductsDisplay.Designer.cs b/Source/LibationWinForms/GridView/ProductsDisplay.Designer.cs
index b84d670c..1e32af52 100644
--- a/Source/LibationWinForms/GridView/ProductsDisplay.Designer.cs
+++ b/Source/LibationWinForms/GridView/ProductsDisplay.Designer.cs
@@ -41,6 +41,7 @@
this.productsGrid.TabIndex = 0;
this.productsGrid.VisibleCountChanged += new System.EventHandler(this.productsGrid_VisibleCountChanged);
this.productsGrid.LiberateClicked += new LibationWinForms.GridView.LibraryBookEntryClickedEventHandler(this.productsGrid_LiberateClicked);
+ this.productsGrid.ConvertToMp3Clicked += new LibationWinForms.GridView.LibraryBookEntryClickedEventHandler(this.productsGrid_ConvertToMp3Clicked);
this.productsGrid.CoverClicked += new LibationWinForms.GridView.GridEntryClickedEventHandler(this.productsGrid_CoverClicked);
this.productsGrid.DetailsClicked += new LibationWinForms.GridView.LibraryBookEntryClickedEventHandler(this.productsGrid_DetailsClicked);
this.productsGrid.DescriptionClicked += new LibationWinForms.GridView.GridEntryRectangleClickedEventHandler(this.productsGrid_DescriptionClicked);
diff --git a/Source/LibationWinForms/GridView/ProductsDisplay.cs b/Source/LibationWinForms/GridView/ProductsDisplay.cs
index 56a710af..75027385 100644
--- a/Source/LibationWinForms/GridView/ProductsDisplay.cs
+++ b/Source/LibationWinForms/GridView/ProductsDisplay.cs
@@ -19,6 +19,7 @@ namespace LibationWinForms.GridView
public event EventHandler VisibleCountChanged;
public event EventHandler RemovableCountChanged;
public event EventHandler LiberateClicked;
+ public event EventHandler ConvertToMp3Clicked;
public event EventHandler InitialLoaded;
private bool hasBeenDisplayed;
@@ -204,6 +205,12 @@ namespace LibationWinForms.GridView
LiberateClicked?.Invoke(this, liveGridEntry.LibraryBook);
}
+ private void productsGrid_ConvertToMp3Clicked(LibraryBookEntry liveGridEntry)
+ {
+ if (liveGridEntry.LibraryBook.Book.UserDefinedItem.BookStatus is not LiberatedStatus.Error)
+ ConvertToMp3Clicked?.Invoke(this, liveGridEntry.LibraryBook);
+ }
+
private void productsGrid_RemovableCountChanged(object sender, EventArgs e)
{
RemovableCountChanged?.Invoke(sender, productsGrid.GetAllBookEntries().Count(lbe => lbe.Remove is RemoveStatus.Removed));
diff --git a/Source/LibationWinForms/GridView/ProductsGrid.cs b/Source/LibationWinForms/GridView/ProductsGrid.cs
index 7766a73c..75355bf9 100644
--- a/Source/LibationWinForms/GridView/ProductsGrid.cs
+++ b/Source/LibationWinForms/GridView/ProductsGrid.cs
@@ -22,6 +22,7 @@ namespace LibationWinForms.GridView
/// Number of visible rows has changed
public event EventHandler VisibleCountChanged;
public event LibraryBookEntryClickedEventHandler LiberateClicked;
+ public event LibraryBookEntryClickedEventHandler ConvertToMp3Clicked;
public event GridEntryClickedEventHandler CoverClicked;
public event LibraryBookEntryClickedEventHandler DetailsClicked;
public event GridEntryRectangleClickedEventHandler DescriptionClicked;
@@ -176,6 +177,13 @@ namespace LibationWinForms.GridView
}
};
+ var convertToMp3MenuItem = new ToolStripMenuItem
+ {
+ Text = "&Convert to Mp3",
+ Enabled = entry.Book.UserDefinedItem.BookStatus != LiberatedStatus.NotLiberated
+ };
+ convertToMp3MenuItem.Click += (_, e) => ConvertToMp3Clicked?.Invoke(entry as LibraryBookEntry);
+
var bookRecordMenuItem = new ToolStripMenuItem { Text = "View &Bookmarks/Clips" };
bookRecordMenuItem.Click += (_, _) => new BookRecordsDialog(entry.LibraryBook).ShowDialog(this);
@@ -184,6 +192,7 @@ namespace LibationWinForms.GridView
stopLightContextMenu.Items.Add(setNotDownloadMenuItem);
stopLightContextMenu.Items.Add(removeMenuItem);
stopLightContextMenu.Items.Add(locateFileMenuItem);
+ stopLightContextMenu.Items.Add(convertToMp3MenuItem);
stopLightContextMenu.Items.Add(new ToolStripSeparator());
stopLightContextMenu.Items.Add(bookRecordMenuItem);