Merge branch 'master' of https://github.com/rmcrackan/Libation
This commit is contained in:
commit
64a85b6aab
@ -103,8 +103,8 @@ namespace AaxDecrypter
|
|||||||
|
|
||||||
OnInitialized();
|
OnInitialized();
|
||||||
OnRetrievedTitle(AaxFile.AppleTags.TitleSansUnabridged);
|
OnRetrievedTitle(AaxFile.AppleTags.TitleSansUnabridged);
|
||||||
OnRetrievedAuthors(AaxFile.AppleTags.FirstAuthor ?? "[unknown]");
|
OnRetrievedAuthors(AaxFile.AppleTags.FirstAuthor);
|
||||||
OnRetrievedNarrators(AaxFile.AppleTags.Narrator ?? "[unknown]");
|
OnRetrievedNarrators(AaxFile.AppleTags.Narrator);
|
||||||
OnRetrievedCoverArt(AaxFile.AppleTags.Cover);
|
OnRetrievedCoverArt(AaxFile.AppleTags.Cover);
|
||||||
|
|
||||||
return !IsCanceled;
|
return !IsCanceled;
|
||||||
|
|||||||
@ -61,9 +61,6 @@ namespace AaxDecrypter
|
|||||||
|
|
||||||
#region Constants
|
#region Constants
|
||||||
|
|
||||||
//Size of each range request. Android app uses 64MB chunks.
|
|
||||||
private const int RANGE_REQUEST_SZ = 64 * 1024 * 1024;
|
|
||||||
|
|
||||||
//Download memory buffer size
|
//Download memory buffer size
|
||||||
private const int DOWNLOAD_BUFF_SZ = 8 * 1024;
|
private const int DOWNLOAD_BUFF_SZ = 8 * 1024;
|
||||||
|
|
||||||
@ -161,7 +158,7 @@ namespace AaxDecrypter
|
|||||||
|
|
||||||
//Initiate connection with the first request block and
|
//Initiate connection with the first request block and
|
||||||
//get the total content length before returning.
|
//get the total content length before returning.
|
||||||
using var client = new HttpClient();
|
var client = new HttpClient();
|
||||||
var response = await RequestNextByteRangeAsync(client);
|
var response = await RequestNextByteRangeAsync(client);
|
||||||
|
|
||||||
if (ContentLength != 0 && ContentLength != response.FileSize)
|
if (ContentLength != 0 && ContentLength != response.FileSize)
|
||||||
@ -170,38 +167,59 @@ namespace AaxDecrypter
|
|||||||
ContentLength = response.FileSize;
|
ContentLength = response.FileSize;
|
||||||
|
|
||||||
_downloadedPiece = new EventWaitHandle(false, EventResetMode.AutoReset);
|
_downloadedPiece = new EventWaitHandle(false, EventResetMode.AutoReset);
|
||||||
//Hand off the open request to the downloader to download and write data to file.
|
//Hand off the client and the open request to the downloader to download and write data to file.
|
||||||
DownloadTask = Task.Run(() => DownloadLoopInternal(response), _cancellationSource.Token);
|
DownloadTask = Task.Run(() => DownloadLoopInternal(client , response), _cancellationSource.Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DownloadLoopInternal(BlockResponse initialResponse)
|
private async Task DownloadLoopInternal(HttpClient client, BlockResponse blockResponse)
|
||||||
{
|
{
|
||||||
await DownloadToFile(initialResponse);
|
|
||||||
initialResponse.Dispose();
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var client = new HttpClient();
|
long startPosition = WritePosition;
|
||||||
|
|
||||||
while (WritePosition < ContentLength && !IsCancelled)
|
while (WritePosition < ContentLength && !IsCancelled)
|
||||||
{
|
{
|
||||||
using var response = await RequestNextByteRangeAsync(client);
|
try
|
||||||
await DownloadToFile(response);
|
{
|
||||||
|
await DownloadToFile(blockResponse);
|
||||||
|
}
|
||||||
|
catch (HttpIOException e)
|
||||||
|
when (e.HttpRequestError is HttpRequestError.ResponseEnded
|
||||||
|
&& WritePosition != startPosition
|
||||||
|
&& WritePosition < ContentLength && !IsCancelled)
|
||||||
|
{
|
||||||
|
Serilog.Log.Logger.Debug($"The download connection ended before the file completed downloading all 0x{ContentLength:X10} bytes");
|
||||||
|
|
||||||
|
//the download made *some* progress since the last attempt.
|
||||||
|
//Try again to complete the download from where it left off.
|
||||||
|
//Make sure to rewind file to last flush position.
|
||||||
|
_writeFile.Position = startPosition = WritePosition;
|
||||||
|
blockResponse.Dispose();
|
||||||
|
blockResponse = await RequestNextByteRangeAsync(client);
|
||||||
|
|
||||||
|
Serilog.Log.Logger.Debug($"Resuming the file download starting at position 0x{WritePosition:X10}.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_writeFile.Close();
|
_writeFile.Dispose();
|
||||||
|
blockResponse.Dispose();
|
||||||
|
client.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<BlockResponse> RequestNextByteRangeAsync(HttpClient client)
|
private async Task<BlockResponse> RequestNextByteRangeAsync(HttpClient client)
|
||||||
{
|
{
|
||||||
var request = new HttpRequestMessage(HttpMethod.Get, Uri);
|
using var request = new HttpRequestMessage(HttpMethod.Get, Uri);
|
||||||
|
|
||||||
|
//Just in case it snuck in the saved json (Issue #1232)
|
||||||
|
RequestHeaders.Remove("Range");
|
||||||
|
|
||||||
foreach (var header in RequestHeaders)
|
foreach (var header in RequestHeaders)
|
||||||
request.Headers.Add(header.Key, header.Value);
|
request.Headers.Add(header.Key, header.Value);
|
||||||
|
|
||||||
request.Headers.Add("Range", $"bytes={WritePosition}-{WritePosition + RANGE_REQUEST_SZ - 1}");
|
request.Headers.Add("Range", $"bytes={WritePosition}-");
|
||||||
|
|
||||||
var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, _cancellationSource.Token);
|
var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, _cancellationSource.Token);
|
||||||
|
|
||||||
@ -226,7 +244,7 @@ namespace AaxDecrypter
|
|||||||
private async Task DownloadToFile(BlockResponse block)
|
private async Task DownloadToFile(BlockResponse block)
|
||||||
{
|
{
|
||||||
var endPosition = WritePosition + block.BlockSize;
|
var endPosition = WritePosition + block.BlockSize;
|
||||||
var networkStream = await block.Response.Content.ReadAsStreamAsync(_cancellationSource.Token);
|
using var networkStream = await block.Response.Content.ReadAsStreamAsync(_cancellationSource.Token);
|
||||||
|
|
||||||
var downloadPosition = WritePosition;
|
var downloadPosition = WritePosition;
|
||||||
var nextFlush = downloadPosition + DATA_FLUSH_SZ;
|
var nextFlush = downloadPosition + DATA_FLUSH_SZ;
|
||||||
@ -286,7 +304,6 @@ namespace AaxDecrypter
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
networkStream.Close();
|
|
||||||
_downloadedPiece.Set();
|
_downloadedPiece.Set();
|
||||||
OnUpdate();
|
OnUpdate();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -104,9 +104,6 @@ namespace ApplicationServices
|
|||||||
[Name("Content Type")]
|
[Name("Content Type")]
|
||||||
public string ContentType { get; set; }
|
public string ContentType { get; set; }
|
||||||
|
|
||||||
[Name("Audio Format")]
|
|
||||||
public string AudioFormat { get; set; }
|
|
||||||
|
|
||||||
[Name("Language")]
|
[Name("Language")]
|
||||||
public string Language { get; set; }
|
public string Language { get; set; }
|
||||||
|
|
||||||
@ -152,7 +149,6 @@ namespace ApplicationServices
|
|||||||
BookStatus = a.Book.UserDefinedItem.BookStatus.ToString(),
|
BookStatus = a.Book.UserDefinedItem.BookStatus.ToString(),
|
||||||
PdfStatus = a.Book.UserDefinedItem.PdfStatus.ToString(),
|
PdfStatus = a.Book.UserDefinedItem.PdfStatus.ToString(),
|
||||||
ContentType = a.Book.ContentType.ToString(),
|
ContentType = a.Book.ContentType.ToString(),
|
||||||
AudioFormat = a.Book.AudioFormat.ToString(),
|
|
||||||
Language = a.Book.Language,
|
Language = a.Book.Language,
|
||||||
LastDownloaded = a.Book.UserDefinedItem.LastDownloaded,
|
LastDownloaded = a.Book.UserDefinedItem.LastDownloaded,
|
||||||
LastDownloadedVersion = a.Book.UserDefinedItem.LastDownloadedVersion?.ToString() ?? "",
|
LastDownloadedVersion = a.Book.UserDefinedItem.LastDownloadedVersion?.ToString() ?? "",
|
||||||
@ -228,7 +224,6 @@ namespace ApplicationServices
|
|||||||
nameof(ExportDto.BookStatus),
|
nameof(ExportDto.BookStatus),
|
||||||
nameof(ExportDto.PdfStatus),
|
nameof(ExportDto.PdfStatus),
|
||||||
nameof(ExportDto.ContentType),
|
nameof(ExportDto.ContentType),
|
||||||
nameof(ExportDto.AudioFormat),
|
|
||||||
nameof(ExportDto.Language),
|
nameof(ExportDto.Language),
|
||||||
nameof(ExportDto.LastDownloaded),
|
nameof(ExportDto.LastDownloaded),
|
||||||
nameof(ExportDto.LastDownloadedVersion),
|
nameof(ExportDto.LastDownloadedVersion),
|
||||||
@ -299,7 +294,6 @@ namespace ApplicationServices
|
|||||||
row.CreateCell(col++).SetCellValue(dto.BookStatus);
|
row.CreateCell(col++).SetCellValue(dto.BookStatus);
|
||||||
row.CreateCell(col++).SetCellValue(dto.PdfStatus);
|
row.CreateCell(col++).SetCellValue(dto.PdfStatus);
|
||||||
row.CreateCell(col++).SetCellValue(dto.ContentType);
|
row.CreateCell(col++).SetCellValue(dto.ContentType);
|
||||||
row.CreateCell(col++).SetCellValue(dto.AudioFormat);
|
|
||||||
row.CreateCell(col++).SetCellValue(dto.Language);
|
row.CreateCell(col++).SetCellValue(dto.Language);
|
||||||
|
|
||||||
if (dto.LastDownloaded.HasValue)
|
if (dto.LastDownloaded.HasValue)
|
||||||
|
|||||||
@ -19,7 +19,6 @@ namespace DataLayer.Configurations
|
|||||||
//
|
//
|
||||||
entity.Ignore(nameof(Book.Authors));
|
entity.Ignore(nameof(Book.Authors));
|
||||||
entity.Ignore(nameof(Book.Narrators));
|
entity.Ignore(nameof(Book.Narrators));
|
||||||
entity.Ignore(nameof(Book.AudioFormat));
|
|
||||||
entity.Ignore(nameof(Book.TitleWithSubtitle));
|
entity.Ignore(nameof(Book.TitleWithSubtitle));
|
||||||
entity.Ignore(b => b.Categories);
|
entity.Ignore(b => b.Categories);
|
||||||
|
|
||||||
|
|||||||
@ -1,65 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace DataLayer
|
|
||||||
{
|
|
||||||
internal enum AudioFormatEnum : long
|
|
||||||
{
|
|
||||||
//Defining the enum this way ensures that when comparing:
|
|
||||||
//LC_128_44100_stereo > LC_64_44100_stereo > LC_64_22050_stereo > LC_64_22050_stereo
|
|
||||||
//This matches how audible interprets these codecs when specifying quality using AudibleApi.DownloadQuality
|
|
||||||
//I've never seen mono formats.
|
|
||||||
Unknown = 0,
|
|
||||||
LC_32_22050_stereo = (32L << 18) | (22050 << 2) | 2,
|
|
||||||
LC_64_22050_stereo = (64L << 18) | (22050 << 2) | 2,
|
|
||||||
LC_64_44100_stereo = (64L << 18) | (44100 << 2) | 2,
|
|
||||||
LC_128_44100_stereo = (128L << 18) | (44100 << 2) | 2,
|
|
||||||
AAX_22_32 = LC_32_22050_stereo,
|
|
||||||
AAX_22_64 = LC_64_22050_stereo,
|
|
||||||
AAX_44_64 = LC_64_44100_stereo,
|
|
||||||
AAX_44_128 = LC_128_44100_stereo
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AudioFormat : IComparable<AudioFormat>, IComparable
|
|
||||||
{
|
|
||||||
internal int AudioFormatID { get; private set; }
|
|
||||||
public int Bitrate { get; private init; }
|
|
||||||
public int SampleRate { get; private init; }
|
|
||||||
public int Channels { get; private init; }
|
|
||||||
public bool IsValid => Bitrate != 0 && SampleRate != 0 && Channels != 0;
|
|
||||||
|
|
||||||
public static AudioFormat FromString(string formatStr)
|
|
||||||
{
|
|
||||||
if (Enum.TryParse(formatStr, ignoreCase: true, out AudioFormatEnum enumVal))
|
|
||||||
return FromEnum(enumVal);
|
|
||||||
return FromEnum(AudioFormatEnum.Unknown);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static AudioFormat FromEnum(AudioFormatEnum enumVal)
|
|
||||||
{
|
|
||||||
var val = (long)enumVal;
|
|
||||||
|
|
||||||
return new()
|
|
||||||
{
|
|
||||||
Bitrate = (int)(val >> 18),
|
|
||||||
SampleRate = (int)(val >> 2) & ushort.MaxValue,
|
|
||||||
Channels = (int)(val & 3)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
internal AudioFormatEnum ToEnum()
|
|
||||||
{
|
|
||||||
var val = (AudioFormatEnum)(((long)Bitrate << 18) | ((long)SampleRate << 2) | (long)Channels);
|
|
||||||
|
|
||||||
return Enum.IsDefined(val) ?
|
|
||||||
val : AudioFormatEnum.Unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
=> IsValid ?
|
|
||||||
$"{Bitrate} Kbps, {SampleRate / 1000d:F1} kHz, {(Channels == 2 ? "Stereo" : Channels)}" :
|
|
||||||
"Unknown";
|
|
||||||
|
|
||||||
public int CompareTo(AudioFormat other) => ToEnum().CompareTo(other.ToEnum());
|
|
||||||
|
|
||||||
public int CompareTo(object obj) => CompareTo(obj as AudioFormat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -43,9 +43,9 @@ namespace DataLayer
|
|||||||
public ContentType ContentType { get; private set; }
|
public ContentType ContentType { get; private set; }
|
||||||
public string Locale { get; private set; }
|
public string Locale { get; private set; }
|
||||||
|
|
||||||
internal AudioFormatEnum _audioFormat;
|
//This field is now unused, however, there is little sense in adding a
|
||||||
|
//database migration to remove an unused field. Leave it for compatibility.
|
||||||
public AudioFormat AudioFormat { get => AudioFormat.FromEnum(_audioFormat); set => _audioFormat = value.ToEnum(); }
|
internal long _audioFormat;
|
||||||
|
|
||||||
// mutable
|
// mutable
|
||||||
public string PictureId { get; set; }
|
public string PictureId { get; set; }
|
||||||
|
|||||||
@ -154,9 +154,6 @@ namespace DtoImporterService
|
|||||||
// Update the book titles, since formatting can change
|
// Update the book titles, since formatting can change
|
||||||
book.UpdateTitle(item.Title, item.Subtitle);
|
book.UpdateTitle(item.Title, item.Subtitle);
|
||||||
|
|
||||||
var codec = item.AvailableCodecs?.Max(f => AudioFormat.FromString(f.EnhancedCodec)) ?? new AudioFormat();
|
|
||||||
book.AudioFormat = codec;
|
|
||||||
|
|
||||||
// set/update book-specific info which may have changed
|
// set/update book-specific info which may have changed
|
||||||
if (item.PictureId is not null)
|
if (item.PictureId is not null)
|
||||||
book.PictureId = item.PictureId;
|
book.PictureId = item.PictureId;
|
||||||
|
|||||||
@ -19,6 +19,7 @@ namespace FileLiberator
|
|||||||
protected void OnTitleDiscovered(object _, string title)
|
protected void OnTitleDiscovered(object _, string title)
|
||||||
{
|
{
|
||||||
Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(TitleDiscovered), Title = title });
|
Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(TitleDiscovered), Title = title });
|
||||||
|
if (title != null)
|
||||||
TitleDiscovered?.Invoke(this, title);
|
TitleDiscovered?.Invoke(this, title);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,6 +27,7 @@ namespace FileLiberator
|
|||||||
protected void OnAuthorsDiscovered(object _, string authors)
|
protected void OnAuthorsDiscovered(object _, string authors)
|
||||||
{
|
{
|
||||||
Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(AuthorsDiscovered), Authors = authors });
|
Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(AuthorsDiscovered), Authors = authors });
|
||||||
|
if (authors != null)
|
||||||
AuthorsDiscovered?.Invoke(this, authors);
|
AuthorsDiscovered?.Invoke(this, authors);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,6 +35,7 @@ namespace FileLiberator
|
|||||||
protected void OnNarratorsDiscovered(object _, string narrators)
|
protected void OnNarratorsDiscovered(object _, string narrators)
|
||||||
{
|
{
|
||||||
Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(NarratorsDiscovered), Narrators = narrators });
|
Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(NarratorsDiscovered), Narrators = narrators });
|
||||||
|
if (narrators != null)
|
||||||
NarratorsDiscovered?.Invoke(this, narrators);
|
NarratorsDiscovered?.Invoke(this, narrators);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -39,8 +39,8 @@ namespace FileLiberator
|
|||||||
/// Path: in progress directory.
|
/// Path: in progress directory.
|
||||||
/// File name: final file name.
|
/// File name: final file name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string GetInProgressFilename(this AudioFileStorage _, LibraryBook libraryBook, string extension)
|
public static string GetInProgressFilename(this AudioFileStorage _, LibraryBookDto libraryBook, string extension)
|
||||||
=> Templates.File.GetFilename(libraryBook.ToDto(), AudibleFileStorage.DecryptInProgressDirectory, extension, returnFirstExisting: true);
|
=> Templates.File.GetFilename(libraryBook, AudibleFileStorage.DecryptInProgressDirectory, extension, returnFirstExisting: true);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// PDF: audio file does not exist
|
/// PDF: audio file does not exist
|
||||||
@ -48,6 +48,12 @@ namespace FileLiberator
|
|||||||
public static string GetBooksDirectoryFilename(this AudioFileStorage _, LibraryBook libraryBook, string extension)
|
public static string GetBooksDirectoryFilename(this AudioFileStorage _, LibraryBook libraryBook, string extension)
|
||||||
=> Templates.File.GetFilename(libraryBook.ToDto(), AudibleFileStorage.BooksDirectory, extension);
|
=> Templates.File.GetFilename(libraryBook.ToDto(), AudibleFileStorage.BooksDirectory, extension);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// PDF: audio file does not exist
|
||||||
|
/// </summary>
|
||||||
|
public static string GetBooksDirectoryFilename(this AudioFileStorage _, LibraryBookDto dto, string extension)
|
||||||
|
=> Templates.File.GetFilename(dto, AudibleFileStorage.BooksDirectory, extension);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// PDF: audio file already exists
|
/// PDF: audio file already exists
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -47,13 +47,18 @@ namespace FileLiberator
|
|||||||
if (libraryBook.Book.Audio_Exists())
|
if (libraryBook.Book.Audio_Exists())
|
||||||
return new StatusHandler { "Cannot find decrypt. Final audio file already exists" };
|
return new StatusHandler { "Cannot find decrypt. Final audio file already exists" };
|
||||||
|
|
||||||
|
downloadValidation(libraryBook);
|
||||||
|
var api = await libraryBook.GetApiAsync();
|
||||||
|
var config = Configuration.Instance;
|
||||||
|
using var downloadOptions = await DownloadOptions.InitiateDownloadAsync(api, config, libraryBook);
|
||||||
|
|
||||||
bool success = false;
|
bool success = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
FilePathCache.Inserted += FilePathCache_Inserted;
|
FilePathCache.Inserted += FilePathCache_Inserted;
|
||||||
FilePathCache.Removed += FilePathCache_Removed;
|
FilePathCache.Removed += FilePathCache_Removed;
|
||||||
|
|
||||||
success = await downloadAudiobookAsync(libraryBook);
|
success = await downloadAudiobookAsync(api, config, downloadOptions);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@ -78,12 +83,12 @@ namespace FileLiberator
|
|||||||
var finalStorageDir = getDestinationDirectory(libraryBook);
|
var finalStorageDir = getDestinationDirectory(libraryBook);
|
||||||
|
|
||||||
var moveFilesTask = Task.Run(() => moveFilesToBooksDir(libraryBook, entries));
|
var moveFilesTask = Task.Run(() => moveFilesToBooksDir(libraryBook, entries));
|
||||||
Task[] finalTasks = new[]
|
Task[] finalTasks =
|
||||||
{
|
[
|
||||||
Task.Run(() => downloadCoverArt(libraryBook)),
|
Task.Run(() => downloadCoverArt(downloadOptions)),
|
||||||
moveFilesTask,
|
moveFilesTask,
|
||||||
Task.Run(() => WindowsDirectory.SetCoverAsFolderIcon(libraryBook.Book.PictureId, finalStorageDir))
|
Task.Run(() => WindowsDirectory.SetCoverAsFolderIcon(libraryBook.Book.PictureId, finalStorageDir))
|
||||||
};
|
];
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -116,16 +121,9 @@ namespace FileLiberator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> downloadAudiobookAsync(LibraryBook libraryBook)
|
private async Task<bool> downloadAudiobookAsync(AudibleApi.Api api, Configuration config, DownloadOptions dlOptions)
|
||||||
{
|
{
|
||||||
var config = Configuration.Instance;
|
var outFileName = AudibleFileStorage.Audio.GetInProgressFilename(dlOptions.LibraryBookDto, dlOptions.OutputFormat.ToString().ToLower());
|
||||||
|
|
||||||
downloadValidation(libraryBook);
|
|
||||||
|
|
||||||
var api = await libraryBook.GetApiAsync();
|
|
||||||
|
|
||||||
using var dlOptions = await DownloadOptions.InitiateDownloadAsync(api, libraryBook, config);
|
|
||||||
var outFileName = AudibleFileStorage.Audio.GetInProgressFilename(libraryBook, dlOptions.OutputFormat.ToString().ToLower());
|
|
||||||
var cacheDir = AudibleFileStorage.DownloadsInProgressDirectory;
|
var cacheDir = AudibleFileStorage.DownloadsInProgressDirectory;
|
||||||
|
|
||||||
if (dlOptions.DrmType is not DrmType.Adrm and not DrmType.Widevine)
|
if (dlOptions.DrmType is not DrmType.Adrm and not DrmType.Widevine)
|
||||||
@ -149,7 +147,7 @@ namespace FileLiberator
|
|||||||
abDownloader.RetrievedAuthors += OnAuthorsDiscovered;
|
abDownloader.RetrievedAuthors += OnAuthorsDiscovered;
|
||||||
abDownloader.RetrievedNarrators += OnNarratorsDiscovered;
|
abDownloader.RetrievedNarrators += OnNarratorsDiscovered;
|
||||||
abDownloader.RetrievedCoverArt += AaxcDownloader_RetrievedCoverArt;
|
abDownloader.RetrievedCoverArt += AaxcDownloader_RetrievedCoverArt;
|
||||||
abDownloader.FileCreated += (_, path) => OnFileCreated(libraryBook, path);
|
abDownloader.FileCreated += (_, path) => OnFileCreated(dlOptions.LibraryBook, path);
|
||||||
|
|
||||||
// REAL WORK DONE HERE
|
// REAL WORK DONE HERE
|
||||||
var success = await abDownloader.RunAsync();
|
var success = await abDownloader.RunAsync();
|
||||||
@ -158,12 +156,12 @@ namespace FileLiberator
|
|||||||
{
|
{
|
||||||
var metadataFile = LibationFileManager.Templates.Templates.File.GetFilename(dlOptions.LibraryBookDto, Path.GetDirectoryName(outFileName), ".metadata.json");
|
var metadataFile = LibationFileManager.Templates.Templates.File.GetFilename(dlOptions.LibraryBookDto, Path.GetDirectoryName(outFileName), ".metadata.json");
|
||||||
|
|
||||||
var item = await api.GetCatalogProductAsync(libraryBook.Book.AudibleProductId, AudibleApi.CatalogOptions.ResponseGroupOptions.ALL_OPTIONS);
|
var item = await api.GetCatalogProductAsync(dlOptions.LibraryBook.Book.AudibleProductId, AudibleApi.CatalogOptions.ResponseGroupOptions.ALL_OPTIONS);
|
||||||
item.SourceJson.Add(nameof(ContentMetadata.ChapterInfo), Newtonsoft.Json.Linq.JObject.FromObject(dlOptions.ContentMetadata.ChapterInfo));
|
item.SourceJson.Add(nameof(ContentMetadata.ChapterInfo), Newtonsoft.Json.Linq.JObject.FromObject(dlOptions.ContentMetadata.ChapterInfo));
|
||||||
item.SourceJson.Add(nameof(ContentMetadata.ContentReference), Newtonsoft.Json.Linq.JObject.FromObject(dlOptions.ContentMetadata.ContentReference));
|
item.SourceJson.Add(nameof(ContentMetadata.ContentReference), Newtonsoft.Json.Linq.JObject.FromObject(dlOptions.ContentMetadata.ContentReference));
|
||||||
|
|
||||||
File.WriteAllText(metadataFile, item.SourceJson.ToString());
|
File.WriteAllText(metadataFile, item.SourceJson.ToString());
|
||||||
OnFileCreated(libraryBook, metadataFile);
|
OnFileCreated(dlOptions.LibraryBook, metadataFile);
|
||||||
}
|
}
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
@ -280,7 +278,7 @@ namespace FileLiberator
|
|||||||
private static FilePathCache.CacheEntry getFirstAudioFile(IEnumerable<FilePathCache.CacheEntry> entries)
|
private static FilePathCache.CacheEntry getFirstAudioFile(IEnumerable<FilePathCache.CacheEntry> entries)
|
||||||
=> entries.FirstOrDefault(f => f.FileType == FileType.Audio);
|
=> entries.FirstOrDefault(f => f.FileType == FileType.Audio);
|
||||||
|
|
||||||
private static void downloadCoverArt(LibraryBook libraryBook)
|
private static void downloadCoverArt(DownloadOptions options)
|
||||||
{
|
{
|
||||||
if (!Configuration.Instance.DownloadCoverArt) return;
|
if (!Configuration.Instance.DownloadCoverArt) return;
|
||||||
|
|
||||||
@ -288,24 +286,24 @@ namespace FileLiberator
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var destinationDir = getDestinationDirectory(libraryBook);
|
var destinationDir = getDestinationDirectory(options.LibraryBook);
|
||||||
coverPath = AudibleFileStorage.Audio.GetBooksDirectoryFilename(libraryBook, ".jpg");
|
coverPath = AudibleFileStorage.Audio.GetBooksDirectoryFilename(options.LibraryBookDto, ".jpg");
|
||||||
coverPath = Path.Combine(destinationDir, Path.GetFileName(coverPath));
|
coverPath = Path.Combine(destinationDir, Path.GetFileName(coverPath));
|
||||||
|
|
||||||
if (File.Exists(coverPath))
|
if (File.Exists(coverPath))
|
||||||
FileUtility.SaferDelete(coverPath);
|
FileUtility.SaferDelete(coverPath);
|
||||||
|
|
||||||
var picBytes = PictureStorage.GetPictureSynchronously(new(libraryBook.Book.PictureLarge ?? libraryBook.Book.PictureId, PictureSize.Native));
|
var picBytes = PictureStorage.GetPictureSynchronously(new(options.LibraryBook.Book.PictureLarge ?? options.LibraryBook.Book.PictureId, PictureSize.Native));
|
||||||
if (picBytes.Length > 0)
|
if (picBytes.Length > 0)
|
||||||
{
|
{
|
||||||
File.WriteAllBytes(coverPath, picBytes);
|
File.WriteAllBytes(coverPath, picBytes);
|
||||||
SetFileTime(libraryBook, coverPath);
|
SetFileTime(options.LibraryBook, coverPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
//Failure to download cover art should not be considered a failure to download the book
|
//Failure to download cover art should not be considered a failure to download the book
|
||||||
Serilog.Log.Logger.Error(ex, $"Error downloading cover art of {libraryBook.Book.AudibleProductId} to {coverPath} catalog product.");
|
Serilog.Log.Logger.Error(ex, $"Error downloading cover art of {options.LibraryBook.Book.AudibleProductId} to {coverPath} catalog product.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,7 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
#nullable enable
|
#nullable enable
|
||||||
@ -23,7 +24,7 @@ public partial class DownloadOptions
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initiate an audiobook download from the audible api.
|
/// Initiate an audiobook download from the audible api.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static async Task<DownloadOptions> InitiateDownloadAsync(Api api, LibraryBook libraryBook, Configuration config)
|
public static async Task<DownloadOptions> InitiateDownloadAsync(Api api, Configuration config, LibraryBook libraryBook)
|
||||||
{
|
{
|
||||||
var license = await ChooseContent(api, libraryBook, config);
|
var license = await ChooseContent(api, libraryBook, config);
|
||||||
var options = BuildDownloadOptions(libraryBook, config, license);
|
var options = BuildDownloadOptions(libraryBook, config, license);
|
||||||
@ -33,42 +34,40 @@ public partial class DownloadOptions
|
|||||||
|
|
||||||
private static async Task<ContentLicense> ChooseContent(Api api, LibraryBook libraryBook, Configuration config)
|
private static async Task<ContentLicense> ChooseContent(Api api, LibraryBook libraryBook, Configuration config)
|
||||||
{
|
{
|
||||||
var cdm = await Cdm.GetCdmAsync();
|
|
||||||
|
|
||||||
var dlQuality = config.FileDownloadQuality == Configuration.DownloadQuality.Normal ? DownloadQuality.Normal : DownloadQuality.High;
|
var dlQuality = config.FileDownloadQuality == Configuration.DownloadQuality.Normal ? DownloadQuality.Normal : DownloadQuality.High;
|
||||||
|
|
||||||
ContentLicense? contentLic = null;
|
if (!config.UseWidevine || await Cdm.GetCdmAsync() is not Cdm cdm)
|
||||||
ContentLicense? fallback = null;
|
return await api.GetDownloadLicenseAsync(libraryBook.Book.AudibleProductId, dlQuality);
|
||||||
|
|
||||||
|
ContentLicense? contentLic = null, fallback = null;
|
||||||
|
|
||||||
if (cdm is null)
|
|
||||||
{
|
|
||||||
//Doesn't matter what the user chose. We can't get a CDM so we must fall back to AAX(C)
|
|
||||||
contentLic = await api.GetDownloadLicenseAsync(libraryBook.Book.AudibleProductId, dlQuality);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var spatial = config.FileDownloadQuality is Configuration.DownloadQuality.Spatial;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
//try to request a widevine content license using the user's spatial audio settings
|
||||||
var codecChoice = config.SpatialAudioCodec switch
|
var codecChoice = config.SpatialAudioCodec switch
|
||||||
{
|
{
|
||||||
Configuration.SpatialCodec.EC_3 => Ec3Codec,
|
Configuration.SpatialCodec.EC_3 => Ec3Codec,
|
||||||
Configuration.SpatialCodec.AC_4 => Ac4Codec,
|
Configuration.SpatialCodec.AC_4 => Ac4Codec,
|
||||||
_ => throw new NotSupportedException($"Unknown value for {nameof(config.SpatialAudioCodec)}")
|
_ => throw new NotSupportedException($"Unknown value for {nameof(config.SpatialAudioCodec)}")
|
||||||
};
|
};
|
||||||
contentLic = await api.GetDownloadLicenseAsync(libraryBook.Book.AudibleProductId, dlQuality, ChapterTitlesType.Tree, DrmType.Widevine, spatial, codecChoice);
|
|
||||||
|
contentLic
|
||||||
|
= await api.GetDownloadLicenseAsync(
|
||||||
|
libraryBook.Book.AudibleProductId,
|
||||||
|
dlQuality,
|
||||||
|
ChapterTitlesType.Tree,
|
||||||
|
DrmType.Widevine,
|
||||||
|
config.RequestSpatial,
|
||||||
|
codecChoice);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Serilog.Log.Logger.Error(ex, "Failed to request a Widevine license.");
|
Serilog.Log.Logger.Error(ex, "Failed to request a Widevine license.");
|
||||||
|
//We failed to get a widevine license, so fall back to AAX(C)
|
||||||
|
return await api.GetDownloadLicenseAsync(libraryBook.Book.AudibleProductId, dlQuality);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contentLic is null)
|
if (!contentLic.ContentMetadata.ContentReference.IsSpatial && contentLic.DrmType != DrmType.Adrm)
|
||||||
{
|
|
||||||
//We failed to get a widevine license, so fall back to AAX(C)
|
|
||||||
contentLic = await api.GetDownloadLicenseAsync(libraryBook.Book.AudibleProductId, dlQuality);
|
|
||||||
}
|
|
||||||
else if (!contentLic.ContentMetadata.ContentReference.IsSpatial && contentLic.DrmType != DrmType.Adrm)
|
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
We got a widevine license and we have a Cdm, but we still need to decide if we WANT the file
|
We got a widevine license and we have a Cdm, but we still need to decide if we WANT the file
|
||||||
@ -86,8 +85,8 @@ public partial class DownloadOptions
|
|||||||
|
|
||||||
To decide which file we want, use this simple rule: if files are different codecs and
|
To decide which file we want, use this simple rule: if files are different codecs and
|
||||||
Widevine is significantly larger, use Widevine. Otherwise use ADRM.
|
Widevine is significantly larger, use Widevine. Otherwise use ADRM.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
fallback = await api.GetDownloadLicenseAsync(libraryBook.Book.AudibleProductId, dlQuality);
|
fallback = await api.GetDownloadLicenseAsync(libraryBook.Book.AudibleProductId, dlQuality);
|
||||||
|
|
||||||
var wvCr = contentLic.ContentMetadata.ContentReference;
|
var wvCr = contentLic.ContentMetadata.ContentReference;
|
||||||
@ -100,14 +99,13 @@ public partial class DownloadOptions
|
|||||||
contentLic = fallback;
|
contentLic = fallback;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (contentLic.DrmType == DrmType.Widevine && cdm is not null)
|
if (contentLic.DrmType == DrmType.Widevine)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var client = new HttpClient();
|
using var client = new HttpClient();
|
||||||
var mpdResponse = await client.GetAsync(contentLic.LicenseResponse);
|
using var mpdResponse = await client.GetAsync(contentLic.LicenseResponse);
|
||||||
var dash = new MpegDash(mpdResponse.Content.ReadAsStream());
|
var dash = new MpegDash(mpdResponse.Content.ReadAsStream());
|
||||||
|
|
||||||
if (!dash.TryGetUri(new Uri(contentLic.LicenseResponse), out var contentUri))
|
if (!dash.TryGetUri(new Uri(contentLic.LicenseResponse), out var contentUri))
|
||||||
@ -124,7 +122,6 @@ public partial class DownloadOptions
|
|||||||
Key = Convert.ToHexStringLower(keys[0].Kid.ToByteArray()),
|
Key = Convert.ToHexStringLower(keys[0].Kid.ToByteArray()),
|
||||||
Iv = Convert.ToHexStringLower(keys[0].Key)
|
Iv = Convert.ToHexStringLower(keys[0].Key)
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@ -160,9 +157,6 @@ public partial class DownloadOptions
|
|||||||
: contentLic.DrmType is DrmType.Adrm && contentLic.Voucher?.Key.Length == 32 && contentLic.Voucher?.Iv.Length == 32 ? AAXClean.FileType.Aaxc
|
: contentLic.DrmType is DrmType.Adrm && contentLic.Voucher?.Key.Length == 32 && contentLic.Voucher?.Iv.Length == 32 ? AAXClean.FileType.Aaxc
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
//Set the requested AudioFormat for use in file naming templates
|
|
||||||
libraryBook.Book.AudioFormat = AudioFormat.FromString(contentLic.ContentMetadata.ContentReference.ContentFormat);
|
|
||||||
|
|
||||||
var dlOptions = new DownloadOptions(config, libraryBook, contentLic.ContentMetadata.ContentUrl?.OfflineUrl)
|
var dlOptions = new DownloadOptions(config, libraryBook, contentLic.ContentMetadata.ContentUrl?.OfflineUrl)
|
||||||
{
|
{
|
||||||
AudibleKey = contentLic.Voucher?.Key,
|
AudibleKey = contentLic.Voucher?.Key,
|
||||||
@ -176,6 +170,14 @@ public partial class DownloadOptions
|
|||||||
RuntimeLength = TimeSpan.FromMilliseconds(contentLic.ContentMetadata.ChapterInfo.RuntimeLengthMs),
|
RuntimeLength = TimeSpan.FromMilliseconds(contentLic.ContentMetadata.ChapterInfo.RuntimeLengthMs),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
dlOptions.LibraryBookDto.Codec = contentLic.ContentMetadata.ContentReference.Codec;
|
||||||
|
if (TryGetAudioInfo(contentLic.ContentMetadata.ContentUrl, out int? bitrate, out int? sampleRate, out int? channels))
|
||||||
|
{
|
||||||
|
dlOptions.LibraryBookDto.BitRate = bitrate;
|
||||||
|
dlOptions.LibraryBookDto.SampleRate = sampleRate;
|
||||||
|
dlOptions.LibraryBookDto.Channels = channels;
|
||||||
|
}
|
||||||
|
|
||||||
var titleConcat = config.CombineNestedChapterTitles ? ": " : null;
|
var titleConcat = config.CombineNestedChapterTitles ? ": " : null;
|
||||||
var chapters
|
var chapters
|
||||||
= flattenChapters(contentLic.ContentMetadata.ChapterInfo.Chapters, titleConcat)
|
= flattenChapters(contentLic.ContentMetadata.ChapterInfo.Chapters, titleConcat)
|
||||||
@ -202,6 +204,43 @@ public partial class DownloadOptions
|
|||||||
return dlOptions;
|
return dlOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The most reliable way to get these audio file properties is from the filename itself.
|
||||||
|
/// Using AAXClean to read the metadata works well for everything except AC-4 bitrate.
|
||||||
|
/// </summary>
|
||||||
|
private static bool TryGetAudioInfo(ContentUrl? contentUrl, out int? bitrate, out int? sampleRate, out int? channels)
|
||||||
|
{
|
||||||
|
bitrate = sampleRate = channels = null;
|
||||||
|
|
||||||
|
if (contentUrl?.OfflineUrl is not string url || !Uri.TryCreate(url, default, out var uri))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var file = Path.GetFileName(uri.LocalPath);
|
||||||
|
|
||||||
|
var match = AdrmAudioProperties().Match(file);
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
bitrate = int.Parse(match.Groups[1].Value);
|
||||||
|
sampleRate = int.Parse(match.Groups[2].Value);
|
||||||
|
channels = int.Parse(match.Groups[3].Value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if ((match = WidevineAudioProperties().Match(file)).Success)
|
||||||
|
{
|
||||||
|
bitrate = int.Parse(match.Groups[2].Value);
|
||||||
|
sampleRate = int.Parse(match.Groups[1].Value) * 1000;
|
||||||
|
channels = match.Groups[3].Value switch
|
||||||
|
{
|
||||||
|
"ec3" => 6,
|
||||||
|
"ac4" => 3,
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static LameConfig GetLameOptions(Configuration config)
|
public static LameConfig GetLameOptions(Configuration config)
|
||||||
{
|
{
|
||||||
LameConfig lameConfig = new()
|
LameConfig lameConfig = new()
|
||||||
@ -359,4 +398,9 @@ public partial class DownloadOptions
|
|||||||
|
|
||||||
static double RelativePercentDifference(long num1, long num2)
|
static double RelativePercentDifference(long num1, long num2)
|
||||||
=> Math.Abs(num1 - num2) / (double)(num1 + num2);
|
=> Math.Abs(num1 - num2) / (double)(num1 + num2);
|
||||||
|
|
||||||
|
[GeneratedRegex(@".+_(\d+)_(\d+)-(\w+).mp4", RegexOptions.Singleline | RegexOptions.IgnoreCase)]
|
||||||
|
private static partial Regex WidevineAudioProperties();
|
||||||
|
[GeneratedRegex(@".+_lc_(\d+)_(\d+)_(\d+).aax", RegexOptions.Singleline | RegexOptions.IgnoreCase)]
|
||||||
|
private static partial Regex AdrmAudioProperties();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,9 +55,6 @@ namespace FileLiberator
|
|||||||
IsPodcastParent = libraryBook.Book.IsEpisodeParent(),
|
IsPodcastParent = libraryBook.Book.IsEpisodeParent(),
|
||||||
IsPodcast = libraryBook.Book.IsEpisodeChild() || libraryBook.Book.IsEpisodeParent(),
|
IsPodcast = libraryBook.Book.IsEpisodeChild() || libraryBook.Book.IsEpisodeParent(),
|
||||||
|
|
||||||
BitRate = libraryBook.Book.AudioFormat.Bitrate,
|
|
||||||
SampleRate = libraryBook.Book.AudioFormat.SampleRate,
|
|
||||||
Channels = libraryBook.Book.AudioFormat.Channels,
|
|
||||||
Language = libraryBook.Book.Language
|
Language = libraryBook.Book.Language
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,14 +43,40 @@
|
|||||||
<controls:WheelComboBox
|
<controls:WheelComboBox
|
||||||
Margin="5,0,0,0"
|
Margin="5,0,0,0"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
SelectionChanged="Quality_SelectionChanged"
|
|
||||||
ItemsSource="{CompiledBinding DownloadQualities}"
|
ItemsSource="{CompiledBinding DownloadQualities}"
|
||||||
SelectedItem="{CompiledBinding FileDownloadQuality}"/>
|
SelectedItem="{CompiledBinding FileDownloadQuality}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid ColumnDefinitions="*,Auto" Margin="0,5,0,0"
|
|
||||||
IsEnabled="{CompiledBinding SpatialSelected}"
|
<Grid ColumnDefinitions="*,*">
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
IsChecked="{CompiledBinding UseWidevine, Mode=TwoWay}"
|
||||||
|
IsCheckedChanged="UseWidevine_IsCheckedChanged"
|
||||||
|
ToolTip.Tip="{CompiledBinding UseWidevineTip}">
|
||||||
|
<TextBlock Text="{CompiledBinding UseWidevineText}" />
|
||||||
|
</CheckBox>
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
Grid.Column="1"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
ToolTip.Tip="{CompiledBinding RequestSpatialTip}"
|
||||||
|
IsEnabled="{CompiledBinding UseWidevine}"
|
||||||
|
IsChecked="{CompiledBinding RequestSpatial, Mode=TwoWay}">
|
||||||
|
<TextBlock Text="{CompiledBinding RequestSpatialText}" />
|
||||||
|
</CheckBox>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid ColumnDefinitions="*,Auto"
|
||||||
ToolTip.Tip="{CompiledBinding SpatialAudioCodecTip}">
|
ToolTip.Tip="{CompiledBinding SpatialAudioCodecTip}">
|
||||||
|
<Grid.IsEnabled>
|
||||||
|
<MultiBinding Converter="{x:Static BoolConverters.And}">
|
||||||
|
<MultiBinding.Bindings>
|
||||||
|
<CompiledBinding Path="UseWidevine"/>
|
||||||
|
<CompiledBinding Path="RequestSpatial"/>
|
||||||
|
</MultiBinding.Bindings>
|
||||||
|
</MultiBinding>
|
||||||
|
</Grid.IsEnabled>
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
@ -63,6 +89,11 @@
|
|||||||
SelectedItem="{CompiledBinding SpatialAudioCodec}"/>
|
SelectedItem="{CompiledBinding SpatialAudioCodec}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<CheckBox IsChecked="{CompiledBinding CreateCueSheet, Mode=TwoWay}">
|
<CheckBox IsChecked="{CompiledBinding CreateCueSheet, Mode=TwoWay}">
|
||||||
<TextBlock Text="{CompiledBinding CreateCueSheetText}" />
|
<TextBlock Text="{CompiledBinding CreateCueSheetText}" />
|
||||||
</CheckBox>
|
</CheckBox>
|
||||||
|
|||||||
@ -22,21 +22,21 @@ namespace LibationAvalonia.Controls.Settings
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void UseWidevine_IsCheckedChanged(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
public async void Quality_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
|
||||||
{
|
{
|
||||||
if (_viewModel.SpatialSelected)
|
if (sender is CheckBox cbox && cbox.IsChecked is true)
|
||||||
{
|
{
|
||||||
using var accounts = AudibleApiStorage.GetAccountsSettingsPersister();
|
using var accounts = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||||
|
|
||||||
if (!accounts.AccountsSettings.Accounts.Any(a => a.IdentityTokens.DeviceType == AudibleApi.Resources.DeviceType))
|
if (!accounts.AccountsSettings.Accounts.Any(a => a.IdentityTokens.DeviceType == AudibleApi.Resources.DeviceType))
|
||||||
{
|
{
|
||||||
await MessageBox.Show(VisualRoot as Window,
|
if (VisualRoot is Window parent)
|
||||||
"Your must remove account(s) from Libation and then re-add them to enable spatial audiobook downloads.",
|
await MessageBox.Show(parent,
|
||||||
"Spatial Audio Unavailable",
|
"Your must remove account(s) from Libation and then re-add them to enable widwvine content.",
|
||||||
|
"Widevine Content Unavailable",
|
||||||
MessageBoxButtons.OK);
|
MessageBoxButtons.OK);
|
||||||
|
|
||||||
_viewModel.FileDownloadQuality = _viewModel.DownloadQualities[1];
|
_viewModel.UseWidevine = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -114,7 +114,6 @@ Title: {title}
|
|||||||
Author(s): {Book.AuthorNames()}
|
Author(s): {Book.AuthorNames()}
|
||||||
Narrator(s): {Book.NarratorNames()}
|
Narrator(s): {Book.NarratorNames()}
|
||||||
Length: {(Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min")}
|
Length: {(Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min")}
|
||||||
Audio Bitrate: {Book.AudioFormat}
|
|
||||||
Category: {string.Join(", ", Book.LowestCategoryNames())}
|
Category: {string.Join(", ", Book.LowestCategoryNames())}
|
||||||
Purchase Date: {libraryBook.DateAdded:d}
|
Purchase Date: {libraryBook.DateAdded:d}
|
||||||
Language: {Book.Language}
|
Language: {Book.Language}
|
||||||
|
|||||||
@ -67,6 +67,8 @@ namespace LibationAvalonia.ViewModels.Settings
|
|||||||
FileDownloadQuality = DownloadQualities.SingleOrDefault(s => s.Value == config.FileDownloadQuality) ?? DownloadQualities[0];
|
FileDownloadQuality = DownloadQualities.SingleOrDefault(s => s.Value == config.FileDownloadQuality) ?? DownloadQualities[0];
|
||||||
SelectedSampleRate = SampleRates.SingleOrDefault(s => s.Value == config.MaxSampleRate) ?? SampleRates[0];
|
SelectedSampleRate = SampleRates.SingleOrDefault(s => s.Value == config.MaxSampleRate) ?? SampleRates[0];
|
||||||
SelectedEncoderQuality = config.LameEncoderQuality;
|
SelectedEncoderQuality = config.LameEncoderQuality;
|
||||||
|
UseWidevine = config.UseWidevine;
|
||||||
|
RequestSpatial = config.RequestSpatial;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveSettings(Configuration config)
|
public void SaveSettings(Configuration config)
|
||||||
@ -96,12 +98,13 @@ namespace LibationAvalonia.ViewModels.Settings
|
|||||||
config.MaxSampleRate = SelectedSampleRate?.Value ?? config.MaxSampleRate;
|
config.MaxSampleRate = SelectedSampleRate?.Value ?? config.MaxSampleRate;
|
||||||
config.FileDownloadQuality = FileDownloadQuality?.Value ?? config.FileDownloadQuality;
|
config.FileDownloadQuality = FileDownloadQuality?.Value ?? config.FileDownloadQuality;
|
||||||
config.SpatialAudioCodec = SpatialAudioCodec?.Value ?? config.SpatialAudioCodec;
|
config.SpatialAudioCodec = SpatialAudioCodec?.Value ?? config.SpatialAudioCodec;
|
||||||
|
config.UseWidevine = UseWidevine;
|
||||||
|
config.RequestSpatial = RequestSpatial;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AvaloniaList<EnumDisplay<Configuration.DownloadQuality>> DownloadQualities { get; } = new([
|
public AvaloniaList<EnumDisplay<Configuration.DownloadQuality>> DownloadQualities { get; } = new([
|
||||||
new EnumDisplay<Configuration.DownloadQuality>(Configuration.DownloadQuality.Normal),
|
new EnumDisplay<Configuration.DownloadQuality>(Configuration.DownloadQuality.Normal),
|
||||||
new EnumDisplay<Configuration.DownloadQuality>(Configuration.DownloadQuality.High),
|
new EnumDisplay<Configuration.DownloadQuality>(Configuration.DownloadQuality.High),
|
||||||
new EnumDisplay<Configuration.DownloadQuality>(Configuration.DownloadQuality.Spatial, "Spatial (if available)"),
|
|
||||||
]);
|
]);
|
||||||
public AvaloniaList<EnumDisplay<Configuration.SpatialCodec>> SpatialAudioCodecs { get; } = new([
|
public AvaloniaList<EnumDisplay<Configuration.SpatialCodec>> SpatialAudioCodecs { get; } = new([
|
||||||
new EnumDisplay<Configuration.SpatialCodec>(Configuration.SpatialCodec.EC_3, "Dolby Digital Plus (E-AC-3)"),
|
new EnumDisplay<Configuration.SpatialCodec>(Configuration.SpatialCodec.EC_3, "Dolby Digital Plus (E-AC-3)"),
|
||||||
@ -109,6 +112,10 @@ namespace LibationAvalonia.ViewModels.Settings
|
|||||||
]);
|
]);
|
||||||
public AvaloniaList<Configuration.ClipBookmarkFormat> ClipBookmarkFormats { get; } = new(Enum<Configuration.ClipBookmarkFormat>.GetValues());
|
public AvaloniaList<Configuration.ClipBookmarkFormat> ClipBookmarkFormats { get; } = new(Enum<Configuration.ClipBookmarkFormat>.GetValues());
|
||||||
public string FileDownloadQualityText { get; } = Configuration.GetDescription(nameof(Configuration.FileDownloadQuality));
|
public string FileDownloadQualityText { get; } = Configuration.GetDescription(nameof(Configuration.FileDownloadQuality));
|
||||||
|
public string UseWidevineText { get; } = Configuration.GetDescription(nameof(Configuration.UseWidevine));
|
||||||
|
public string UseWidevineTip { get; } = Configuration.GetHelpText(nameof(Configuration.UseWidevine));
|
||||||
|
public string RequestSpatialText { get; } = Configuration.GetDescription(nameof(Configuration.RequestSpatial));
|
||||||
|
public string RequestSpatialTip { get; } = Configuration.GetHelpText(nameof(Configuration.RequestSpatial));
|
||||||
public string SpatialAudioCodecText { get; } = Configuration.GetDescription(nameof(Configuration.SpatialAudioCodec));
|
public string SpatialAudioCodecText { get; } = Configuration.GetDescription(nameof(Configuration.SpatialAudioCodec));
|
||||||
public string SpatialAudioCodecTip { get; } = Configuration.GetHelpText(nameof(Configuration.SpatialAudioCodec));
|
public string SpatialAudioCodecTip { get; } = Configuration.GetHelpText(nameof(Configuration.SpatialAudioCodec));
|
||||||
public string CreateCueSheetText { get; } = Configuration.GetDescription(nameof(Configuration.CreateCueSheet));
|
public string CreateCueSheetText { get; } = Configuration.GetDescription(nameof(Configuration.CreateCueSheet));
|
||||||
@ -133,19 +140,13 @@ namespace LibationAvalonia.ViewModels.Settings
|
|||||||
public string RetainAaxFileTip => Configuration.GetHelpText(nameof(RetainAaxFile));
|
public string RetainAaxFileTip => Configuration.GetHelpText(nameof(RetainAaxFile));
|
||||||
public bool DownloadClipsBookmarks { get => _downloadClipsBookmarks; set => this.RaiseAndSetIfChanged(ref _downloadClipsBookmarks, value); }
|
public bool DownloadClipsBookmarks { get => _downloadClipsBookmarks; set => this.RaiseAndSetIfChanged(ref _downloadClipsBookmarks, value); }
|
||||||
|
|
||||||
public bool SpatialSelected { get; private set; }
|
|
||||||
|
|
||||||
private EnumDisplay<Configuration.DownloadQuality>? _fileDownloadQuality;
|
private bool _useWidevine;
|
||||||
public EnumDisplay<Configuration.DownloadQuality> FileDownloadQuality
|
private bool _requestSpatial;
|
||||||
{
|
public bool UseWidevine { get => _useWidevine; set => this.RaiseAndSetIfChanged(ref _useWidevine, value); }
|
||||||
get => _fileDownloadQuality ?? DownloadQualities[0];
|
public bool RequestSpatial { get => _requestSpatial; set => this.RaiseAndSetIfChanged(ref _requestSpatial, value); }
|
||||||
set
|
|
||||||
{
|
public EnumDisplay<Configuration.DownloadQuality> FileDownloadQuality { get; set; }
|
||||||
SpatialSelected = value?.Value == Configuration.DownloadQuality.Spatial;
|
|
||||||
this.RaiseAndSetIfChanged(ref _fileDownloadQuality, value);
|
|
||||||
this.RaisePropertyChanged(nameof(SpatialSelected));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public EnumDisplay<Configuration.SpatialCodec> SpatialAudioCodec { get; set; }
|
public EnumDisplay<Configuration.SpatialCodec> SpatialAudioCodec { get; set; }
|
||||||
public Configuration.ClipBookmarkFormat ClipBookmarkFormat { get; set; }
|
public Configuration.ClipBookmarkFormat ClipBookmarkFormat { get; set; }
|
||||||
public bool MergeOpeningAndEndCredits { get; set; }
|
public bool MergeOpeningAndEndCredits { get; set; }
|
||||||
|
|||||||
@ -89,6 +89,24 @@ namespace LibationFileManager
|
|||||||
|
|
||||||
AC-4 cannot be converted to MP3.
|
AC-4 cannot be converted to MP3.
|
||||||
""" },
|
""" },
|
||||||
|
{nameof(UseWidevine), """
|
||||||
|
Some audiobooks are only delivered in the highest
|
||||||
|
available quality with special, third-party content
|
||||||
|
protection. Enabling this option will make Libation
|
||||||
|
request audiobooks with Widevine DRM, which may
|
||||||
|
yield higher quality audiobook files. If they are
|
||||||
|
higher quality, however, they will also be encoded
|
||||||
|
with a somewhat uncommon codec (xHE-AAC USAC)
|
||||||
|
which you may have difficulty playing.
|
||||||
|
|
||||||
|
This must be enable to download spatial audiobooks.
|
||||||
|
""" },
|
||||||
|
{nameof(RequestSpatial), """
|
||||||
|
If selected, Libation will request audiobooks in the
|
||||||
|
Dolby Atmos 'Spatial Audio' format. Audiobooks which
|
||||||
|
don't have a spatial audio version will be download
|
||||||
|
as usual based on your other file quality settings.
|
||||||
|
""" },
|
||||||
}
|
}
|
||||||
.AsReadOnly();
|
.AsReadOnly();
|
||||||
|
|
||||||
|
|||||||
@ -246,8 +246,7 @@ namespace LibationFileManager
|
|||||||
public enum DownloadQuality
|
public enum DownloadQuality
|
||||||
{
|
{
|
||||||
High,
|
High,
|
||||||
Normal,
|
Normal
|
||||||
Spatial
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonConverter(typeof(StringEnumConverter))]
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
@ -257,6 +256,12 @@ namespace LibationFileManager
|
|||||||
AC_4
|
AC_4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Description("Use widevine DRM")]
|
||||||
|
public bool UseWidevine { get => GetNonString(defaultValue: true); set => SetNonString(value); }
|
||||||
|
|
||||||
|
[Description("Request Spatial Audio")]
|
||||||
|
public bool RequestSpatial { get => GetNonString(defaultValue: true); set => SetNonString(value); }
|
||||||
|
|
||||||
[Description("Spatial audio codec:")]
|
[Description("Spatial audio codec:")]
|
||||||
public SpatialCodec SpatialAudioCodec { get => GetNonString(defaultValue: SpatialCodec.EC_3); set => SetNonString(value); }
|
public SpatialCodec SpatialAudioCodec { get => GetNonString(defaultValue: SpatialCodec.EC_3); set => SetNonString(value); }
|
||||||
|
|
||||||
|
|||||||
@ -27,9 +27,10 @@ public class BookDto
|
|||||||
public bool IsPodcastParent { get; set; }
|
public bool IsPodcastParent { get; set; }
|
||||||
public bool IsPodcast { get; set; }
|
public bool IsPodcast { get; set; }
|
||||||
|
|
||||||
public int BitRate { get; set; }
|
public int? BitRate { get; set; }
|
||||||
public int SampleRate { get; set; }
|
public int? SampleRate { get; set; }
|
||||||
public int Channels { get; set; }
|
public int? Channels { get; set; }
|
||||||
|
public string? Codec { get; set; }
|
||||||
public DateTime FileDate { get; set; } = DateTime.Now;
|
public DateTime FileDate { get; set; } = DateTime.Now;
|
||||||
public DateTime? DatePublished { get; set; }
|
public DateTime? DatePublished { get; set; }
|
||||||
public string? Language { get; set; }
|
public string? Language { get; set; }
|
||||||
|
|||||||
@ -36,9 +36,10 @@ namespace LibationFileManager.Templates
|
|||||||
public static TemplateTags Series { get; } = new TemplateTags("series", "All series to which the book belongs (if any)");
|
public static TemplateTags Series { get; } = new TemplateTags("series", "All series to which the book belongs (if any)");
|
||||||
public static TemplateTags FirstSeries { get; } = new TemplateTags("first series", "First series");
|
public static TemplateTags FirstSeries { get; } = new TemplateTags("first series", "First series");
|
||||||
public static TemplateTags SeriesNumber { get; } = new TemplateTags("series#", "Number order in series (alias for <first series[{#}]>");
|
public static TemplateTags SeriesNumber { get; } = new TemplateTags("series#", "Number order in series (alias for <first series[{#}]>");
|
||||||
public static TemplateTags Bitrate { get; } = new TemplateTags("bitrate", "File's orig. bitrate");
|
public static TemplateTags Bitrate { get; } = new TemplateTags("bitrate", "Audiobook's source bitrate");
|
||||||
public static TemplateTags SampleRate { get; } = new TemplateTags("samplerate", "File's orig. sample rate");
|
public static TemplateTags SampleRate { get; } = new TemplateTags("samplerate", "Audiobook's source sample rate");
|
||||||
public static TemplateTags Channels { get; } = new TemplateTags("channels", "Number of audio channels");
|
public static TemplateTags Channels { get; } = new TemplateTags("channels", "Audiobook's source audio channel count");
|
||||||
|
public static TemplateTags Codec { get; } = new TemplateTags("codec", "Audiobook's source codec");
|
||||||
public static TemplateTags Account { get; } = new TemplateTags("account", "Audible account of this book");
|
public static TemplateTags Account { get; } = new TemplateTags("account", "Audible account of this book");
|
||||||
public static TemplateTags AccountNickname { get; } = new TemplateTags("account nickname", "Audible account nickname of this book");
|
public static TemplateTags AccountNickname { get; } = new TemplateTags("account nickname", "Audible account nickname of this book");
|
||||||
public static TemplateTags Locale { get; } = new("locale", "Region/country");
|
public static TemplateTags Locale { get; } = new("locale", "Region/country");
|
||||||
|
|||||||
@ -271,9 +271,6 @@ namespace LibationFileManager.Templates
|
|||||||
{ TemplateTags.Language, lb => lb.Language },
|
{ TemplateTags.Language, lb => lb.Language },
|
||||||
//Don't allow formatting of LanguageShort
|
//Don't allow formatting of LanguageShort
|
||||||
{ TemplateTags.LanguageShort, lb =>lb.Language, getLanguageShort },
|
{ TemplateTags.LanguageShort, lb =>lb.Language, getLanguageShort },
|
||||||
{ 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.Account, lb => lb.Account },
|
||||||
{ TemplateTags.AccountNickname, lb => lb.AccountNickname },
|
{ TemplateTags.AccountNickname, lb => lb.AccountNickname },
|
||||||
{ TemplateTags.Locale, lb => lb.Locale },
|
{ TemplateTags.Locale, lb => lb.Locale },
|
||||||
@ -283,6 +280,15 @@ namespace LibationFileManager.Templates
|
|||||||
{ TemplateTags.FileDate, lb => lb.FileDate },
|
{ TemplateTags.FileDate, lb => lb.FileDate },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static readonly PropertyTagCollection<LibraryBookDto> audioFilePropertyTags =
|
||||||
|
new(caseSensative: true, StringFormatter, IntegerFormatter)
|
||||||
|
{
|
||||||
|
{ TemplateTags.Bitrate, lb => lb.BitRate },
|
||||||
|
{ TemplateTags.SampleRate, lb => lb.SampleRate },
|
||||||
|
{ TemplateTags.Channels, lb => lb.Channels },
|
||||||
|
{ TemplateTags.Codec, lb => lb.Codec },
|
||||||
|
};
|
||||||
|
|
||||||
private static readonly List<TagCollection> chapterPropertyTags = new()
|
private static readonly List<TagCollection> chapterPropertyTags = new()
|
||||||
{
|
{
|
||||||
new PropertyTagCollection<LibraryBookDto>(caseSensative: true, StringFormatter)
|
new PropertyTagCollection<LibraryBookDto>(caseSensative: true, StringFormatter)
|
||||||
@ -376,8 +382,7 @@ namespace LibationFileManager.Templates
|
|||||||
public static string Name { get; } = "Folder Template";
|
public static string Name { get; } = "Folder Template";
|
||||||
public static string Description { get; } = Configuration.GetDescription(nameof(Configuration.FolderTemplate)) ?? "";
|
public static string Description { get; } = Configuration.GetDescription(nameof(Configuration.FolderTemplate)) ?? "";
|
||||||
public static string DefaultTemplate { get; } = "<title short> [<id>]";
|
public static string DefaultTemplate { get; } = "<title short> [<id>]";
|
||||||
public static IEnumerable<TagCollection> TagCollections
|
public static IEnumerable<TagCollection> TagCollections { get; } = [filePropertyTags, conditionalTags, folderConditionalTags];
|
||||||
=> new TagCollection[] { filePropertyTags, conditionalTags, folderConditionalTags };
|
|
||||||
|
|
||||||
public override IEnumerable<string> Errors
|
public override IEnumerable<string> Errors
|
||||||
=> TemplateText?.Length >= 2 && Path.IsPathFullyQualified(TemplateText) ? base.Errors.Append(ERROR_FULL_PATH_IS_INVALID) : base.Errors;
|
=> TemplateText?.Length >= 2 && Path.IsPathFullyQualified(TemplateText) ? base.Errors.Append(ERROR_FULL_PATH_IS_INVALID) : base.Errors;
|
||||||
@ -396,7 +401,7 @@ namespace LibationFileManager.Templates
|
|||||||
public static string Name { get; } = "File Template";
|
public static string Name { get; } = "File Template";
|
||||||
public static string Description { get; } = Configuration.GetDescription(nameof(Configuration.FileTemplate)) ?? "";
|
public static string Description { get; } = Configuration.GetDescription(nameof(Configuration.FileTemplate)) ?? "";
|
||||||
public static string DefaultTemplate { get; } = "<title> [<id>]";
|
public static string DefaultTemplate { get; } = "<title> [<id>]";
|
||||||
public static IEnumerable<TagCollection> TagCollections { get; } = new TagCollection[] { filePropertyTags, conditionalTags };
|
public static IEnumerable<TagCollection> TagCollections { get; } = [filePropertyTags, audioFilePropertyTags, conditionalTags];
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ChapterFileTemplate : Templates, ITemplate
|
public class ChapterFileTemplate : Templates, ITemplate
|
||||||
@ -404,7 +409,8 @@ namespace LibationFileManager.Templates
|
|||||||
public static string Name { get; } = "Chapter File Template";
|
public static string Name { get; } = "Chapter File Template";
|
||||||
public static string Description { get; } = Configuration.GetDescription(nameof(Configuration.ChapterFileTemplate)) ?? "";
|
public static string Description { get; } = Configuration.GetDescription(nameof(Configuration.ChapterFileTemplate)) ?? "";
|
||||||
public static string DefaultTemplate { get; } = "<title> [<id>] - <ch# 0> - <ch title>";
|
public static string DefaultTemplate { get; } = "<title> [<id>] - <ch# 0> - <ch title>";
|
||||||
public static IEnumerable<TagCollection> TagCollections { get; } = chapterPropertyTags.Append(filePropertyTags).Append(conditionalTags);
|
public static IEnumerable<TagCollection> TagCollections { get; }
|
||||||
|
= chapterPropertyTags.Append(filePropertyTags).Append(audioFilePropertyTags).Append(conditionalTags);
|
||||||
|
|
||||||
public override IEnumerable<string> Warnings
|
public override IEnumerable<string> Warnings
|
||||||
=> NamingTemplate.TagsInUse.Any(t => t.TagName.In(TemplateTags.ChNumber.TagName, TemplateTags.ChNumber0.TagName))
|
=> NamingTemplate.TagsInUse.Any(t => t.TagName.In(TemplateTags.ChNumber.TagName, TemplateTags.ChNumber0.TagName))
|
||||||
|
|||||||
@ -49,7 +49,6 @@ Title: {title}
|
|||||||
Author(s): {Book.AuthorNames()}
|
Author(s): {Book.AuthorNames()}
|
||||||
Narrator(s): {Book.NarratorNames()}
|
Narrator(s): {Book.NarratorNames()}
|
||||||
Length: {(Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min")}
|
Length: {(Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min")}
|
||||||
Audio Bitrate: {Book.AudioFormat}
|
|
||||||
Category: {string.Join(", ", Book.LowestCategoryNames())}
|
Category: {string.Join(", ", Book.LowestCategoryNames())}
|
||||||
Purchase Date: {_libraryBook.DateAdded:d}
|
Purchase Date: {_libraryBook.DateAdded:d}
|
||||||
Language: {Book.Language}
|
Language: {Book.Language}
|
||||||
|
|||||||
@ -23,6 +23,8 @@ namespace LibationWinForms.Dialogs
|
|||||||
this.stripAudibleBrandingCbox.Text = desc(nameof(config.StripAudibleBrandAudio));
|
this.stripAudibleBrandingCbox.Text = desc(nameof(config.StripAudibleBrandAudio));
|
||||||
this.stripUnabridgedCbox.Text = desc(nameof(config.StripUnabridged));
|
this.stripUnabridgedCbox.Text = desc(nameof(config.StripUnabridged));
|
||||||
this.moveMoovAtomCbox.Text = desc(nameof(config.MoveMoovToBeginning));
|
this.moveMoovAtomCbox.Text = desc(nameof(config.MoveMoovToBeginning));
|
||||||
|
this.useWidevineCbox.Text = desc(nameof(config.UseWidevine));
|
||||||
|
this.requestSpatialCbox.Text = desc(nameof(config.RequestSpatial));
|
||||||
this.spatialCodecLbl.Text = desc(nameof(config.SpatialAudioCodec));
|
this.spatialCodecLbl.Text = desc(nameof(config.SpatialAudioCodec));
|
||||||
|
|
||||||
toolTip.SetToolTip(combineNestedChapterTitlesCbox, Configuration.GetHelpText(nameof(config.CombineNestedChapterTitles)));
|
toolTip.SetToolTip(combineNestedChapterTitlesCbox, Configuration.GetHelpText(nameof(config.CombineNestedChapterTitles)));
|
||||||
@ -34,6 +36,8 @@ namespace LibationWinForms.Dialogs
|
|||||||
toolTip.SetToolTip(mergeOpeningEndCreditsCbox, Configuration.GetHelpText(nameof(config.MergeOpeningAndEndCredits)));
|
toolTip.SetToolTip(mergeOpeningEndCreditsCbox, Configuration.GetHelpText(nameof(config.MergeOpeningAndEndCredits)));
|
||||||
toolTip.SetToolTip(retainAaxFileCbox, Configuration.GetHelpText(nameof(config.RetainAaxFile)));
|
toolTip.SetToolTip(retainAaxFileCbox, Configuration.GetHelpText(nameof(config.RetainAaxFile)));
|
||||||
toolTip.SetToolTip(stripAudibleBrandingCbox, Configuration.GetHelpText(nameof(config.StripAudibleBrandAudio)));
|
toolTip.SetToolTip(stripAudibleBrandingCbox, Configuration.GetHelpText(nameof(config.StripAudibleBrandAudio)));
|
||||||
|
toolTip.SetToolTip(useWidevineCbox, Configuration.GetHelpText(nameof(config.UseWidevine)));
|
||||||
|
toolTip.SetToolTip(requestSpatialCbox, Configuration.GetHelpText(nameof(config.RequestSpatial)));
|
||||||
toolTip.SetToolTip(spatialCodecLbl, Configuration.GetHelpText(nameof(config.SpatialAudioCodec)));
|
toolTip.SetToolTip(spatialCodecLbl, Configuration.GetHelpText(nameof(config.SpatialAudioCodec)));
|
||||||
toolTip.SetToolTip(spatialAudioCodecCb, Configuration.GetHelpText(nameof(config.SpatialAudioCodec)));
|
toolTip.SetToolTip(spatialAudioCodecCb, Configuration.GetHelpText(nameof(config.SpatialAudioCodec)));
|
||||||
|
|
||||||
@ -41,7 +45,6 @@ namespace LibationWinForms.Dialogs
|
|||||||
[
|
[
|
||||||
new EnumDisplay<Configuration.DownloadQuality>(Configuration.DownloadQuality.Normal),
|
new EnumDisplay<Configuration.DownloadQuality>(Configuration.DownloadQuality.Normal),
|
||||||
new EnumDisplay<Configuration.DownloadQuality>(Configuration.DownloadQuality.High),
|
new EnumDisplay<Configuration.DownloadQuality>(Configuration.DownloadQuality.High),
|
||||||
new EnumDisplay<Configuration.DownloadQuality>(Configuration.DownloadQuality.Spatial, "Spatial (if available)"),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
spatialAudioCodecCb.Items.AddRange(
|
spatialAudioCodecCb.Items.AddRange(
|
||||||
@ -76,6 +79,8 @@ namespace LibationWinForms.Dialogs
|
|||||||
downloadClipsBookmarksCbox.Checked = config.DownloadClipsBookmarks;
|
downloadClipsBookmarksCbox.Checked = config.DownloadClipsBookmarks;
|
||||||
fileDownloadQualityCb.SelectedItem = config.FileDownloadQuality;
|
fileDownloadQualityCb.SelectedItem = config.FileDownloadQuality;
|
||||||
spatialAudioCodecCb.SelectedItem = config.SpatialAudioCodec;
|
spatialAudioCodecCb.SelectedItem = config.SpatialAudioCodec;
|
||||||
|
useWidevineCbox.Checked = config.UseWidevine;
|
||||||
|
requestSpatialCbox.Checked = config.RequestSpatial;
|
||||||
|
|
||||||
clipsBookmarksFormatCb.SelectedItem = config.ClipsBookmarksFileFormat;
|
clipsBookmarksFormatCb.SelectedItem = config.ClipsBookmarksFileFormat;
|
||||||
retainAaxFileCbox.Checked = config.RetainAaxFile;
|
retainAaxFileCbox.Checked = config.RetainAaxFile;
|
||||||
@ -118,6 +123,8 @@ namespace LibationWinForms.Dialogs
|
|||||||
config.DownloadCoverArt = downloadCoverArtCbox.Checked;
|
config.DownloadCoverArt = downloadCoverArtCbox.Checked;
|
||||||
config.DownloadClipsBookmarks = downloadClipsBookmarksCbox.Checked;
|
config.DownloadClipsBookmarks = downloadClipsBookmarksCbox.Checked;
|
||||||
config.FileDownloadQuality = ((EnumDisplay<Configuration.DownloadQuality>)fileDownloadQualityCb.SelectedItem).Value;
|
config.FileDownloadQuality = ((EnumDisplay<Configuration.DownloadQuality>)fileDownloadQualityCb.SelectedItem).Value;
|
||||||
|
config.UseWidevine = useWidevineCbox.Checked;
|
||||||
|
config.RequestSpatial = requestSpatialCbox.Checked;
|
||||||
config.SpatialAudioCodec = ((EnumDisplay<Configuration.SpatialCodec>)spatialAudioCodecCb.SelectedItem).Value;
|
config.SpatialAudioCodec = ((EnumDisplay<Configuration.SpatialCodec>)spatialAudioCodecCb.SelectedItem).Value;
|
||||||
config.ClipsBookmarksFileFormat = (Configuration.ClipBookmarkFormat)clipsBookmarksFormatCb.SelectedItem;
|
config.ClipsBookmarksFileFormat = (Configuration.ClipBookmarkFormat)clipsBookmarksFormatCb.SelectedItem;
|
||||||
config.RetainAaxFile = retainAaxFileCbox.Checked;
|
config.RetainAaxFile = retainAaxFileCbox.Checked;
|
||||||
@ -140,7 +147,6 @@ namespace LibationWinForms.Dialogs
|
|||||||
config.ChapterTitleTemplate = chapterTitleTemplateTb.Text;
|
config.ChapterTitleTemplate = chapterTitleTemplateTb.Text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void downloadClipsBookmarksCbox_CheckedChanged(object sender, EventArgs e)
|
private void downloadClipsBookmarksCbox_CheckedChanged(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
clipsBookmarksFormatCb.Enabled = downloadClipsBookmarksCbox.Checked;
|
clipsBookmarksFormatCb.Enabled = downloadClipsBookmarksCbox.Checked;
|
||||||
@ -190,27 +196,28 @@ namespace LibationWinForms.Dialogs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fileDownloadQualityCb_SelectedIndexChanged(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
var selectedSpatial = fileDownloadQualityCb.SelectedItem.Equals(Configuration.DownloadQuality.Spatial);
|
|
||||||
|
|
||||||
if (selectedSpatial)
|
|
||||||
|
private void useWidevineCbox_CheckedChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (useWidevineCbox.Checked)
|
||||||
{
|
{
|
||||||
using var accounts = AudibleApiStorage.GetAccountsSettingsPersister();
|
using var accounts = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||||
|
|
||||||
if (!accounts.AccountsSettings.Accounts.Any(a => a.IdentityTokens.DeviceType == AudibleApi.Resources.DeviceType))
|
if (!accounts.AccountsSettings.Accounts.Any(a => a.IdentityTokens.DeviceType == AudibleApi.Resources.DeviceType))
|
||||||
{
|
{
|
||||||
MessageBox.Show(this,
|
MessageBox.Show(this,
|
||||||
"Your must remove account(s) from Libation and then re-add them to enable spatial audiobook downloads.",
|
"Your must remove account(s) from Libation and then re-add them to enable widwvine content.",
|
||||||
"Spatial Audio Unavailable",
|
"Widevine Content Unavailable",
|
||||||
MessageBoxButtons.OK);
|
MessageBoxButtons.OK);
|
||||||
|
|
||||||
fileDownloadQualityCb.SelectedItem = Configuration.DownloadQuality.High;
|
useWidevineCbox.Checked = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
requestSpatialCbox.Enabled = useWidevineCbox.Checked;
|
||||||
|
spatialCodecLbl.Enabled = spatialAudioCodecCb.Enabled = useWidevineCbox.Checked && requestSpatialCbox.Checked;
|
||||||
|
}
|
||||||
|
|
||||||
spatialCodecLbl.Enabled = spatialAudioCodecCb.Enabled = selectedSpatial;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -84,6 +84,8 @@
|
|||||||
folderTemplateTb = new System.Windows.Forms.TextBox();
|
folderTemplateTb = new System.Windows.Forms.TextBox();
|
||||||
folderTemplateLbl = new System.Windows.Forms.Label();
|
folderTemplateLbl = new System.Windows.Forms.Label();
|
||||||
tab4AudioFileOptions = new System.Windows.Forms.TabPage();
|
tab4AudioFileOptions = new System.Windows.Forms.TabPage();
|
||||||
|
requestSpatialCbox = new System.Windows.Forms.CheckBox();
|
||||||
|
useWidevineCbox = new System.Windows.Forms.CheckBox();
|
||||||
spatialAudioCodecCb = new System.Windows.Forms.ComboBox();
|
spatialAudioCodecCb = new System.Windows.Forms.ComboBox();
|
||||||
spatialCodecLbl = new System.Windows.Forms.Label();
|
spatialCodecLbl = new System.Windows.Forms.Label();
|
||||||
moveMoovAtomCbox = new System.Windows.Forms.CheckBox();
|
moveMoovAtomCbox = new System.Windows.Forms.CheckBox();
|
||||||
@ -306,7 +308,7 @@
|
|||||||
allowLibationFixupCbox.AutoSize = true;
|
allowLibationFixupCbox.AutoSize = true;
|
||||||
allowLibationFixupCbox.Checked = true;
|
allowLibationFixupCbox.Checked = true;
|
||||||
allowLibationFixupCbox.CheckState = System.Windows.Forms.CheckState.Checked;
|
allowLibationFixupCbox.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||||
allowLibationFixupCbox.Location = new System.Drawing.Point(19, 205);
|
allowLibationFixupCbox.Location = new System.Drawing.Point(19, 230);
|
||||||
allowLibationFixupCbox.Name = "allowLibationFixupCbox";
|
allowLibationFixupCbox.Name = "allowLibationFixupCbox";
|
||||||
allowLibationFixupCbox.Size = new System.Drawing.Size(162, 19);
|
allowLibationFixupCbox.Size = new System.Drawing.Size(162, 19);
|
||||||
allowLibationFixupCbox.TabIndex = 11;
|
allowLibationFixupCbox.TabIndex = 11;
|
||||||
@ -772,6 +774,8 @@
|
|||||||
// tab4AudioFileOptions
|
// tab4AudioFileOptions
|
||||||
//
|
//
|
||||||
tab4AudioFileOptions.AutoScroll = true;
|
tab4AudioFileOptions.AutoScroll = true;
|
||||||
|
tab4AudioFileOptions.Controls.Add(requestSpatialCbox);
|
||||||
|
tab4AudioFileOptions.Controls.Add(useWidevineCbox);
|
||||||
tab4AudioFileOptions.Controls.Add(spatialAudioCodecCb);
|
tab4AudioFileOptions.Controls.Add(spatialAudioCodecCb);
|
||||||
tab4AudioFileOptions.Controls.Add(spatialCodecLbl);
|
tab4AudioFileOptions.Controls.Add(spatialCodecLbl);
|
||||||
tab4AudioFileOptions.Controls.Add(moveMoovAtomCbox);
|
tab4AudioFileOptions.Controls.Add(moveMoovAtomCbox);
|
||||||
@ -798,11 +802,38 @@
|
|||||||
tab4AudioFileOptions.Text = "Audio File Options";
|
tab4AudioFileOptions.Text = "Audio File Options";
|
||||||
tab4AudioFileOptions.UseVisualStyleBackColor = true;
|
tab4AudioFileOptions.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
|
// requestSpatialCbox
|
||||||
|
//
|
||||||
|
requestSpatialCbox.AutoSize = true;
|
||||||
|
requestSpatialCbox.CheckAlign = System.Drawing.ContentAlignment.MiddleRight;
|
||||||
|
requestSpatialCbox.Checked = true;
|
||||||
|
requestSpatialCbox.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||||
|
requestSpatialCbox.Location = new System.Drawing.Point(284, 35);
|
||||||
|
requestSpatialCbox.Name = "requestSpatialCbox";
|
||||||
|
requestSpatialCbox.Size = new System.Drawing.Size(138, 19);
|
||||||
|
requestSpatialCbox.TabIndex = 29;
|
||||||
|
requestSpatialCbox.Text = "[RequestSpatial desc]";
|
||||||
|
requestSpatialCbox.UseVisualStyleBackColor = true;
|
||||||
|
requestSpatialCbox.CheckedChanged += useWidevineCbox_CheckedChanged;
|
||||||
|
//
|
||||||
|
// useWidevineCbox
|
||||||
|
//
|
||||||
|
useWidevineCbox.AutoSize = true;
|
||||||
|
useWidevineCbox.Checked = true;
|
||||||
|
useWidevineCbox.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||||
|
useWidevineCbox.Location = new System.Drawing.Point(19, 35);
|
||||||
|
useWidevineCbox.Name = "useWidevineCbox";
|
||||||
|
useWidevineCbox.Size = new System.Drawing.Size(129, 19);
|
||||||
|
useWidevineCbox.TabIndex = 28;
|
||||||
|
useWidevineCbox.Text = "[UseWidevine desc]";
|
||||||
|
useWidevineCbox.UseVisualStyleBackColor = true;
|
||||||
|
useWidevineCbox.CheckedChanged += useWidevineCbox_CheckedChanged;
|
||||||
|
//
|
||||||
// spatialAudioCodecCb
|
// spatialAudioCodecCb
|
||||||
//
|
//
|
||||||
spatialAudioCodecCb.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
spatialAudioCodecCb.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||||
spatialAudioCodecCb.FormattingEnabled = true;
|
spatialAudioCodecCb.FormattingEnabled = true;
|
||||||
spatialAudioCodecCb.Location = new System.Drawing.Point(249, 35);
|
spatialAudioCodecCb.Location = new System.Drawing.Point(249, 60);
|
||||||
spatialAudioCodecCb.Margin = new System.Windows.Forms.Padding(3, 3, 5, 3);
|
spatialAudioCodecCb.Margin = new System.Windows.Forms.Padding(3, 3, 5, 3);
|
||||||
spatialAudioCodecCb.Name = "spatialAudioCodecCb";
|
spatialAudioCodecCb.Name = "spatialAudioCodecCb";
|
||||||
spatialAudioCodecCb.Size = new System.Drawing.Size(173, 23);
|
spatialAudioCodecCb.Size = new System.Drawing.Size(173, 23);
|
||||||
@ -811,7 +842,7 @@
|
|||||||
// spatialCodecLbl
|
// spatialCodecLbl
|
||||||
//
|
//
|
||||||
spatialCodecLbl.AutoSize = true;
|
spatialCodecLbl.AutoSize = true;
|
||||||
spatialCodecLbl.Location = new System.Drawing.Point(19, 37);
|
spatialCodecLbl.Location = new System.Drawing.Point(19, 62);
|
||||||
spatialCodecLbl.Name = "spatialCodecLbl";
|
spatialCodecLbl.Name = "spatialCodecLbl";
|
||||||
spatialCodecLbl.Size = new System.Drawing.Size(143, 15);
|
spatialCodecLbl.Size = new System.Drawing.Size(143, 15);
|
||||||
spatialCodecLbl.TabIndex = 24;
|
spatialCodecLbl.TabIndex = 24;
|
||||||
@ -836,7 +867,6 @@
|
|||||||
fileDownloadQualityCb.Name = "fileDownloadQualityCb";
|
fileDownloadQualityCb.Name = "fileDownloadQualityCb";
|
||||||
fileDownloadQualityCb.Size = new System.Drawing.Size(130, 23);
|
fileDownloadQualityCb.Size = new System.Drawing.Size(130, 23);
|
||||||
fileDownloadQualityCb.TabIndex = 1;
|
fileDownloadQualityCb.TabIndex = 1;
|
||||||
fileDownloadQualityCb.SelectedIndexChanged += fileDownloadQualityCb_SelectedIndexChanged;
|
|
||||||
//
|
//
|
||||||
// fileDownloadQualityLbl
|
// fileDownloadQualityLbl
|
||||||
//
|
//
|
||||||
@ -851,7 +881,7 @@
|
|||||||
// combineNestedChapterTitlesCbox
|
// combineNestedChapterTitlesCbox
|
||||||
//
|
//
|
||||||
combineNestedChapterTitlesCbox.AutoSize = true;
|
combineNestedChapterTitlesCbox.AutoSize = true;
|
||||||
combineNestedChapterTitlesCbox.Location = new System.Drawing.Point(19, 181);
|
combineNestedChapterTitlesCbox.Location = new System.Drawing.Point(19, 206);
|
||||||
combineNestedChapterTitlesCbox.Name = "combineNestedChapterTitlesCbox";
|
combineNestedChapterTitlesCbox.Name = "combineNestedChapterTitlesCbox";
|
||||||
combineNestedChapterTitlesCbox.Size = new System.Drawing.Size(217, 19);
|
combineNestedChapterTitlesCbox.Size = new System.Drawing.Size(217, 19);
|
||||||
combineNestedChapterTitlesCbox.TabIndex = 10;
|
combineNestedChapterTitlesCbox.TabIndex = 10;
|
||||||
@ -862,7 +892,7 @@
|
|||||||
//
|
//
|
||||||
clipsBookmarksFormatCb.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
clipsBookmarksFormatCb.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||||
clipsBookmarksFormatCb.FormattingEnabled = true;
|
clipsBookmarksFormatCb.FormattingEnabled = true;
|
||||||
clipsBookmarksFormatCb.Location = new System.Drawing.Point(285, 107);
|
clipsBookmarksFormatCb.Location = new System.Drawing.Point(285, 132);
|
||||||
clipsBookmarksFormatCb.Name = "clipsBookmarksFormatCb";
|
clipsBookmarksFormatCb.Name = "clipsBookmarksFormatCb";
|
||||||
clipsBookmarksFormatCb.Size = new System.Drawing.Size(67, 23);
|
clipsBookmarksFormatCb.Size = new System.Drawing.Size(67, 23);
|
||||||
clipsBookmarksFormatCb.TabIndex = 6;
|
clipsBookmarksFormatCb.TabIndex = 6;
|
||||||
@ -870,7 +900,7 @@
|
|||||||
// downloadClipsBookmarksCbox
|
// downloadClipsBookmarksCbox
|
||||||
//
|
//
|
||||||
downloadClipsBookmarksCbox.AutoSize = true;
|
downloadClipsBookmarksCbox.AutoSize = true;
|
||||||
downloadClipsBookmarksCbox.Location = new System.Drawing.Point(19, 109);
|
downloadClipsBookmarksCbox.Location = new System.Drawing.Point(19, 134);
|
||||||
downloadClipsBookmarksCbox.Name = "downloadClipsBookmarksCbox";
|
downloadClipsBookmarksCbox.Name = "downloadClipsBookmarksCbox";
|
||||||
downloadClipsBookmarksCbox.Size = new System.Drawing.Size(248, 19);
|
downloadClipsBookmarksCbox.Size = new System.Drawing.Size(248, 19);
|
||||||
downloadClipsBookmarksCbox.TabIndex = 5;
|
downloadClipsBookmarksCbox.TabIndex = 5;
|
||||||
@ -883,7 +913,7 @@
|
|||||||
audiobookFixupsGb.Controls.Add(splitFilesByChapterCbox);
|
audiobookFixupsGb.Controls.Add(splitFilesByChapterCbox);
|
||||||
audiobookFixupsGb.Controls.Add(stripUnabridgedCbox);
|
audiobookFixupsGb.Controls.Add(stripUnabridgedCbox);
|
||||||
audiobookFixupsGb.Controls.Add(stripAudibleBrandingCbox);
|
audiobookFixupsGb.Controls.Add(stripAudibleBrandingCbox);
|
||||||
audiobookFixupsGb.Location = new System.Drawing.Point(6, 229);
|
audiobookFixupsGb.Location = new System.Drawing.Point(6, 254);
|
||||||
audiobookFixupsGb.Name = "audiobookFixupsGb";
|
audiobookFixupsGb.Name = "audiobookFixupsGb";
|
||||||
audiobookFixupsGb.Size = new System.Drawing.Size(416, 114);
|
audiobookFixupsGb.Size = new System.Drawing.Size(416, 114);
|
||||||
audiobookFixupsGb.TabIndex = 19;
|
audiobookFixupsGb.TabIndex = 19;
|
||||||
@ -1324,7 +1354,7 @@
|
|||||||
// mergeOpeningEndCreditsCbox
|
// mergeOpeningEndCreditsCbox
|
||||||
//
|
//
|
||||||
mergeOpeningEndCreditsCbox.AutoSize = true;
|
mergeOpeningEndCreditsCbox.AutoSize = true;
|
||||||
mergeOpeningEndCreditsCbox.Location = new System.Drawing.Point(19, 157);
|
mergeOpeningEndCreditsCbox.Location = new System.Drawing.Point(19, 182);
|
||||||
mergeOpeningEndCreditsCbox.Name = "mergeOpeningEndCreditsCbox";
|
mergeOpeningEndCreditsCbox.Name = "mergeOpeningEndCreditsCbox";
|
||||||
mergeOpeningEndCreditsCbox.Size = new System.Drawing.Size(198, 19);
|
mergeOpeningEndCreditsCbox.Size = new System.Drawing.Size(198, 19);
|
||||||
mergeOpeningEndCreditsCbox.TabIndex = 9;
|
mergeOpeningEndCreditsCbox.TabIndex = 9;
|
||||||
@ -1334,7 +1364,7 @@
|
|||||||
// retainAaxFileCbox
|
// retainAaxFileCbox
|
||||||
//
|
//
|
||||||
retainAaxFileCbox.AutoSize = true;
|
retainAaxFileCbox.AutoSize = true;
|
||||||
retainAaxFileCbox.Location = new System.Drawing.Point(19, 133);
|
retainAaxFileCbox.Location = new System.Drawing.Point(19, 158);
|
||||||
retainAaxFileCbox.Name = "retainAaxFileCbox";
|
retainAaxFileCbox.Name = "retainAaxFileCbox";
|
||||||
retainAaxFileCbox.Size = new System.Drawing.Size(131, 19);
|
retainAaxFileCbox.Size = new System.Drawing.Size(131, 19);
|
||||||
retainAaxFileCbox.TabIndex = 8;
|
retainAaxFileCbox.TabIndex = 8;
|
||||||
@ -1347,7 +1377,7 @@
|
|||||||
downloadCoverArtCbox.AutoSize = true;
|
downloadCoverArtCbox.AutoSize = true;
|
||||||
downloadCoverArtCbox.Checked = true;
|
downloadCoverArtCbox.Checked = true;
|
||||||
downloadCoverArtCbox.CheckState = System.Windows.Forms.CheckState.Checked;
|
downloadCoverArtCbox.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||||
downloadCoverArtCbox.Location = new System.Drawing.Point(19, 85);
|
downloadCoverArtCbox.Location = new System.Drawing.Point(19, 110);
|
||||||
downloadCoverArtCbox.Name = "downloadCoverArtCbox";
|
downloadCoverArtCbox.Name = "downloadCoverArtCbox";
|
||||||
downloadCoverArtCbox.Size = new System.Drawing.Size(162, 19);
|
downloadCoverArtCbox.Size = new System.Drawing.Size(162, 19);
|
||||||
downloadCoverArtCbox.TabIndex = 4;
|
downloadCoverArtCbox.TabIndex = 4;
|
||||||
@ -1360,7 +1390,7 @@
|
|||||||
createCueSheetCbox.AutoSize = true;
|
createCueSheetCbox.AutoSize = true;
|
||||||
createCueSheetCbox.Checked = true;
|
createCueSheetCbox.Checked = true;
|
||||||
createCueSheetCbox.CheckState = System.Windows.Forms.CheckState.Checked;
|
createCueSheetCbox.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||||
createCueSheetCbox.Location = new System.Drawing.Point(19, 61);
|
createCueSheetCbox.Location = new System.Drawing.Point(19, 86);
|
||||||
createCueSheetCbox.Name = "createCueSheetCbox";
|
createCueSheetCbox.Name = "createCueSheetCbox";
|
||||||
createCueSheetCbox.Size = new System.Drawing.Size(145, 19);
|
createCueSheetCbox.Size = new System.Drawing.Size(145, 19);
|
||||||
createCueSheetCbox.TabIndex = 3;
|
createCueSheetCbox.TabIndex = 3;
|
||||||
@ -1531,5 +1561,7 @@
|
|||||||
private System.Windows.Forms.Button applyDisplaySettingsBtn;
|
private System.Windows.Forms.Button applyDisplaySettingsBtn;
|
||||||
private System.Windows.Forms.ComboBox spatialAudioCodecCb;
|
private System.Windows.Forms.ComboBox spatialAudioCodecCb;
|
||||||
private System.Windows.Forms.Label spatialCodecLbl;
|
private System.Windows.Forms.Label spatialCodecLbl;
|
||||||
|
private System.Windows.Forms.CheckBox useWidevineCbox;
|
||||||
|
private System.Windows.Forms.CheckBox requestSpatialCbox;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user