Fix temp file reuse/cleanup. Add retain aax option.

This commit is contained in:
Michael Bucari-Tovo 2022-05-08 11:08:23 -06:00
parent 8af60b56b6
commit 05f25a88c6
13 changed files with 90 additions and 55 deletions

View File

@ -10,7 +10,7 @@ namespace AaxDecrypter
protected AaxFile AaxFile; protected AaxFile AaxFile;
protected AaxcDownloadConvertBase(string outFileName, string cacheDirectory, DownloadLicense dlLic) protected AaxcDownloadConvertBase(string outFileName, string cacheDirectory, DownloadOptions dlLic)
: base(outFileName, cacheDirectory, dlLic) { } : base(outFileName, cacheDirectory, dlLic) { }
/// <summary>Setting cover art by this method will insert the art into the audiobook metadata</summary> /// <summary>Setting cover art by this method will insert the art into the audiobook metadata</summary>
@ -46,7 +46,7 @@ namespace AaxDecrypter
OnDecryptProgressUpdate(zeroProgress); OnDecryptProgressUpdate(zeroProgress);
AaxFile.SetDecryptionKey(DownloadLicense.AudibleKey, DownloadLicense.AudibleIV); AaxFile.SetDecryptionKey(DownloadOptions.AudibleKey, DownloadOptions.AudibleIV);
return zeroProgress; return zeroProgress;
} }
@ -68,7 +68,7 @@ namespace AaxDecrypter
if (double.IsNormal(estTimeRemaining)) if (double.IsNormal(estTimeRemaining))
OnDecryptTimeRemaining(TimeSpan.FromSeconds(estTimeRemaining)); OnDecryptTimeRemaining(TimeSpan.FromSeconds(estTimeRemaining));
var progressPercent = e.ProcessPosition.TotalSeconds / duration.TotalSeconds; var progressPercent = (e.ProcessPosition / e.TotalDuration);
OnDecryptProgressUpdate( OnDecryptProgressUpdate(
new DownloadProgress new DownloadProgress

View File

@ -18,13 +18,13 @@ namespace AaxDecrypter
private static TimeSpan minChapterLength { get; } = TimeSpan.FromSeconds(3); private static TimeSpan minChapterLength { get; } = TimeSpan.FromSeconds(3);
private List<string> multiPartFilePaths { get; } = new List<string>(); private List<string> multiPartFilePaths { get; } = new List<string>();
public AaxcDownloadMultiConverter(string outFileName, string cacheDirectory, DownloadLicense dlLic, public AaxcDownloadMultiConverter(string outFileName, string cacheDirectory, DownloadOptions dlLic,
Func<MultiConvertFileProperties, string> multipartFileNameCallback = null) Func<MultiConvertFileProperties, string> multipartFileNameCallback = null)
: base(outFileName, cacheDirectory, dlLic) : base(outFileName, cacheDirectory, dlLic)
{ {
Steps = new StepSequence Steps = new StepSequence
{ {
Name = "Download and Convert Aaxc To " + DownloadLicense.OutputFormat, Name = "Download and Convert Aaxc To " + DownloadOptions.OutputFormat,
["Step 1: Get Aaxc Metadata"] = Step_GetMetadata, ["Step 1: Get Aaxc Metadata"] = Step_GetMetadata,
["Step 2: Download Decrypted Audiobook"] = Step_DownloadAudiobookAsMultipleFilesPerChapter, ["Step 2: Download Decrypted Audiobook"] = Step_DownloadAudiobookAsMultipleFilesPerChapter,
@ -61,10 +61,10 @@ That naming may not be desirable for everyone, but it's an easy change to instea
{ {
var zeroProgress = Step_DownloadAudiobook_Start(); var zeroProgress = Step_DownloadAudiobook_Start();
var chapters = DownloadLicense.ChapterInfo.Chapters.ToList(); var chapters = DownloadOptions.ChapterInfo.Chapters.ToList();
// Ensure split files are at least minChapterLength in duration. // Ensure split files are at least minChapterLength in duration.
var splitChapters = new ChapterInfo(DownloadLicense.ChapterInfo.StartOffset); var splitChapters = new ChapterInfo(DownloadOptions.ChapterInfo.StartOffset);
var runningTotal = TimeSpan.Zero; var runningTotal = TimeSpan.Zero;
string title = ""; string title = "";
@ -89,7 +89,7 @@ That naming may not be desirable for everyone, but it's an easy change to instea
ConversionResult result; ConversionResult result;
AaxFile.ConversionProgressUpdate += AaxFile_ConversionProgressUpdate; AaxFile.ConversionProgressUpdate += AaxFile_ConversionProgressUpdate;
if (DownloadLicense.OutputFormat == OutputFormat.M4b) if (DownloadOptions.OutputFormat == OutputFormat.M4b)
result = ConvertToMultiMp4a(splitChapters); result = ConvertToMultiMp4a(splitChapters);
else else
result = ConvertToMultiMp3(splitChapters); result = ConvertToMultiMp3(splitChapters);
@ -97,13 +97,7 @@ That naming may not be desirable for everyone, but it's an easy change to instea
Step_DownloadAudiobook_End(zeroProgress); Step_DownloadAudiobook_End(zeroProgress);
var success = result == ConversionResult.NoErrorsDetected; return result == ConversionResult.NoErrorsDetected;
if (success)
foreach (var path in multiPartFilePaths)
OnFileCreated(path);
return success;
} }
private ConversionResult ConvertToMultiMp4a(ChapterInfo splitChapters) private ConversionResult ConvertToMultiMp4a(ChapterInfo splitChapters)
@ -111,7 +105,7 @@ That naming may not be desirable for everyone, but it's an easy change to instea
var chapterCount = 0; var chapterCount = 0;
return AaxFile.ConvertToMultiMp4a(splitChapters, newSplitCallback => return AaxFile.ConvertToMultiMp4a(splitChapters, newSplitCallback =>
createOutputFileStream(++chapterCount, splitChapters, newSplitCallback), createOutputFileStream(++chapterCount, splitChapters, newSplitCallback),
DownloadLicense.TrimOutputToChapterLength); DownloadOptions.TrimOutputToChapterLength);
} }
private ConversionResult ConvertToMultiMp3(ChapterInfo splitChapters) private ConversionResult ConvertToMultiMp3(ChapterInfo splitChapters)
@ -121,7 +115,7 @@ That naming may not be desirable for everyone, but it's an easy change to instea
{ {
createOutputFileStream(++chapterCount, splitChapters, newSplitCallback); createOutputFileStream(++chapterCount, splitChapters, newSplitCallback);
((NAudio.Lame.LameConfig)newSplitCallback.UserState).ID3.Track = chapterCount.ToString(); ((NAudio.Lame.LameConfig)newSplitCallback.UserState).ID3.Track = chapterCount.ToString();
}, null, DownloadLicense.TrimOutputToChapterLength); }, null, DownloadOptions.TrimOutputToChapterLength);
} }
private void createOutputFileStream(int currentChapter, ChapterInfo splitChapters, NewSplitCallback newSplitCallback) private void createOutputFileStream(int currentChapter, ChapterInfo splitChapters, NewSplitCallback newSplitCallback)
@ -140,6 +134,8 @@ That naming may not be desirable for everyone, but it's an easy change to instea
FileUtility.SaferDelete(fileName); FileUtility.SaferDelete(fileName);
newSplitCallback.OutputFile = File.Open(fileName, FileMode.OpenOrCreate); newSplitCallback.OutputFile = File.Open(fileName, FileMode.OpenOrCreate);
OnFileCreated(fileName);
} }
} }
} }

View File

@ -11,12 +11,12 @@ namespace AaxDecrypter
{ {
protected override StepSequence Steps { get; } protected override StepSequence Steps { get; }
public AaxcDownloadSingleConverter(string outFileName, string cacheDirectory, DownloadLicense dlLic) public AaxcDownloadSingleConverter(string outFileName, string cacheDirectory, DownloadOptions dlLic)
: base(outFileName, cacheDirectory, dlLic) : base(outFileName, cacheDirectory, dlLic)
{ {
Steps = new StepSequence Steps = new StepSequence
{ {
Name = "Download and Convert Aaxc To " + DownloadLicense.OutputFormat, Name = "Download and Convert Aaxc To " + DownloadOptions.OutputFormat,
["Step 1: Get Aaxc Metadata"] = Step_GetMetadata, ["Step 1: Get Aaxc Metadata"] = Step_GetMetadata,
["Step 2: Download Decrypted Audiobook"] = Step_DownloadAudiobookAsSingleFile, ["Step 2: Download Decrypted Audiobook"] = Step_DownloadAudiobookAsSingleFile,
@ -32,15 +32,16 @@ namespace AaxDecrypter
FileUtility.SaferDelete(OutputFileName); FileUtility.SaferDelete(OutputFileName);
var outputFile = File.Open(OutputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite); var outputFile = File.Open(OutputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite);
OnFileCreated(OutputFileName);
AaxFile.ConversionProgressUpdate += AaxFile_ConversionProgressUpdate; AaxFile.ConversionProgressUpdate += AaxFile_ConversionProgressUpdate;
var decryptionResult var decryptionResult
= DownloadLicense.OutputFormat == OutputFormat.M4b = DownloadOptions.OutputFormat == OutputFormat.M4b
? AaxFile.ConvertToMp4a(outputFile, DownloadLicense.ChapterInfo, DownloadLicense.TrimOutputToChapterLength) ? AaxFile.ConvertToMp4a(outputFile, DownloadOptions.ChapterInfo, DownloadOptions.TrimOutputToChapterLength)
: AaxFile.ConvertToMp3(outputFile, null, DownloadLicense.ChapterInfo, DownloadLicense.TrimOutputToChapterLength); : AaxFile.ConvertToMp3(outputFile, null, DownloadOptions.ChapterInfo, DownloadOptions.TrimOutputToChapterLength);
AaxFile.ConversionProgressUpdate -= AaxFile_ConversionProgressUpdate; AaxFile.ConversionProgressUpdate -= AaxFile_ConversionProgressUpdate;
DownloadLicense.ChapterInfo = AaxFile.Chapters; DownloadOptions.ChapterInfo = AaxFile.Chapters;
Step_DownloadAudiobook_End(zeroProgress); Step_DownloadAudiobook_End(zeroProgress);

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using Dinah.Core; using Dinah.Core;
using Dinah.Core.Net.Http; using Dinah.Core.Net.Http;
@ -20,8 +21,9 @@ namespace AaxDecrypter
public event EventHandler<string> FileCreated; public event EventHandler<string> FileCreated;
protected bool IsCanceled { get; set; } protected bool IsCanceled { get; set; }
protected string OutputFileName { get; private set; } protected string OutputFileName { get; private set; }
protected DownloadLicense DownloadLicense { get; } protected DownloadOptions DownloadOptions { get; }
protected NetworkFileStream InputFileStream => (nfsPersister ??= OpenNetworkFileStream()).NetworkFileStream; protected NetworkFileStream InputFileStream => (nfsPersister ??= OpenNetworkFileStream()).NetworkFileStream;
// Don't give the property a 'set'. This should have to be an obvious choice; not accidental // Don't give the property a 'set'. This should have to be an obvious choice; not accidental
@ -31,9 +33,9 @@ namespace AaxDecrypter
private NetworkFileStreamPersister nfsPersister; private NetworkFileStreamPersister nfsPersister;
private string jsonDownloadState { get; } private string jsonDownloadState { get; }
private string tempFile => Path.ChangeExtension(jsonDownloadState, ".tmp"); public string TempFilePath { get; }
protected AudiobookDownloadBase(string outFileName, string cacheDirectory, DownloadLicense dlLic) protected AudiobookDownloadBase(string outFileName, string cacheDirectory, DownloadOptions dlLic)
{ {
OutputFileName = ArgumentValidator.EnsureNotNullOrWhiteSpace(outFileName, nameof(outFileName)); OutputFileName = ArgumentValidator.EnsureNotNullOrWhiteSpace(outFileName, nameof(outFileName));
@ -43,9 +45,11 @@ namespace AaxDecrypter
if (!Directory.Exists(cacheDirectory)) if (!Directory.Exists(cacheDirectory))
throw new DirectoryNotFoundException($"Directory does not exist: {nameof(cacheDirectory)}"); throw new DirectoryNotFoundException($"Directory does not exist: {nameof(cacheDirectory)}");
jsonDownloadState = Path.Combine(cacheDirectory, Path.ChangeExtension(OutputFileName, ".json"));
DownloadLicense = ArgumentValidator.EnsureNotNull(dlLic, nameof(dlLic)); jsonDownloadState = Path.Combine(cacheDirectory, Path.GetFileName(Path.ChangeExtension(OutputFileName, ".json")));
TempFilePath = Path.ChangeExtension(jsonDownloadState, ".tmp");
DownloadOptions = ArgumentValidator.EnsureNotNull(dlLic, nameof(dlLic));
// delete file after validation is complete // delete file after validation is complete
FileUtility.SaferDelete(OutputFileName); FileUtility.SaferDelete(OutputFileName);
@ -99,7 +103,7 @@ namespace AaxDecrypter
{ {
var path = Path.ChangeExtension(OutputFileName, ".cue"); var path = Path.ChangeExtension(OutputFileName, ".cue");
path = FileUtility.GetValidFilename(path); path = FileUtility.GetValidFilename(path);
File.WriteAllText(path, Cue.CreateContents(Path.GetFileName(OutputFileName), DownloadLicense.ChapterInfo)); File.WriteAllText(path, Cue.CreateContents(Path.GetFileName(OutputFileName), DownloadOptions.ChapterInfo));
OnFileCreated(path); OnFileCreated(path);
} }
catch (Exception ex) catch (Exception ex)
@ -110,10 +114,28 @@ namespace AaxDecrypter
} }
protected bool Step_Cleanup() protected bool Step_Cleanup()
{
bool success = !IsCanceled;
if (success)
{ {
FileUtility.SaferDelete(jsonDownloadState); FileUtility.SaferDelete(jsonDownloadState);
FileUtility.SaferDelete(tempFile);
return !IsCanceled; if (DownloadOptions.RetainEncryptedFile)
{
string aaxPath = Path.ChangeExtension(TempFilePath, ".aax");
FileUtility.SaferMove(TempFilePath, aaxPath);
OnFileCreated(aaxPath);
}
else
FileUtility.SaferDelete(TempFilePath);
}
else
{
FileUtility.SaferDelete(OutputFileName);
}
return success;
} }
private NetworkFileStreamPersister OpenNetworkFileStream() private NetworkFileStreamPersister OpenNetworkFileStream()
@ -126,13 +148,13 @@ namespace AaxDecrypter
var nfsp = new NetworkFileStreamPersister(jsonDownloadState); var nfsp = new NetworkFileStreamPersister(jsonDownloadState);
// If More than ~1 hour has elapsed since getting the download url, it will expire. // If More than ~1 hour has elapsed since getting the download url, it will expire.
// The new url will be to the same file. // The new url will be to the same file.
nfsp.NetworkFileStream.SetUriForSameFile(new Uri(DownloadLicense.DownloadUrl)); nfsp.NetworkFileStream.SetUriForSameFile(new Uri(DownloadOptions.DownloadUrl));
return nfsp; return nfsp;
} }
catch catch
{ {
FileUtility.SaferDelete(jsonDownloadState); FileUtility.SaferDelete(jsonDownloadState);
FileUtility.SaferDelete(tempFile); FileUtility.SaferDelete(TempFilePath);
return NewNetworkFilePersister(); return NewNetworkFilePersister();
} }
} }
@ -141,10 +163,10 @@ namespace AaxDecrypter
{ {
var headers = new System.Net.WebHeaderCollection var headers = new System.Net.WebHeaderCollection
{ {
{ "User-Agent", DownloadLicense.UserAgent } { "User-Agent", DownloadOptions.UserAgent }
}; };
var networkFileStream = new NetworkFileStream(tempFile, new Uri(DownloadLicense.DownloadUrl), 0, headers); var networkFileStream = new NetworkFileStream(TempFilePath, new Uri(DownloadOptions.DownloadUrl), 0, headers);
return new NetworkFileStreamPersister(networkFileStream, jsonDownloadState); return new NetworkFileStreamPersister(networkFileStream, jsonDownloadState);
} }
} }

View File

@ -3,7 +3,7 @@ using Dinah.Core;
namespace AaxDecrypter namespace AaxDecrypter
{ {
public class DownloadLicense public class DownloadOptions
{ {
public string DownloadUrl { get; } public string DownloadUrl { get; }
public string UserAgent { get; } public string UserAgent { get; }
@ -11,9 +11,10 @@ namespace AaxDecrypter
public string AudibleIV { get; init; } public string AudibleIV { get; init; }
public OutputFormat OutputFormat { get; init; } public OutputFormat OutputFormat { get; init; }
public bool TrimOutputToChapterLength { get; init; } public bool TrimOutputToChapterLength { get; init; }
public bool RetainEncryptedFile { get; init; }
public ChapterInfo ChapterInfo { get; set; } public ChapterInfo ChapterInfo { get; set; }
public DownloadLicense(string downloadUrl, string userAgent) public DownloadOptions(string downloadUrl, string userAgent)
{ {
DownloadUrl = ArgumentValidator.EnsureNotNullOrEmpty(downloadUrl, nameof(downloadUrl)); DownloadUrl = ArgumentValidator.EnsureNotNullOrEmpty(downloadUrl, nameof(downloadUrl));
UserAgent = ArgumentValidator.EnsureNotNullOrEmpty(userAgent, nameof(userAgent)); UserAgent = ArgumentValidator.EnsureNotNullOrEmpty(userAgent, nameof(userAgent));

View File

@ -10,7 +10,7 @@ namespace AaxDecrypter
{ {
protected override StepSequence Steps { get; } protected override StepSequence Steps { get; }
public UnencryptedAudiobookDownloader(string outFileName, string cacheDirectory, DownloadLicense dlLic) public UnencryptedAudiobookDownloader(string outFileName, string cacheDirectory, DownloadOptions dlLic)
: base(outFileName, cacheDirectory, dlLic) : base(outFileName, cacheDirectory, dlLic)
{ {
Steps = new StepSequence Steps = new StepSequence

View File

@ -84,6 +84,9 @@ namespace AppScaffolding
if (!config.Exists(nameof(config.StripAudibleBrandAudio))) if (!config.Exists(nameof(config.StripAudibleBrandAudio)))
config.StripAudibleBrandAudio = false; config.StripAudibleBrandAudio = false;
if (!config.Exists(nameof(config.RetainAaxFile)))
config.RetainAaxFile = false;
if (!config.Exists(nameof(config.FolderTemplate))) if (!config.Exists(nameof(config.FolderTemplate)))
config.FolderTemplate = Templates.Folder.DefaultTemplate; config.FolderTemplate = Templates.Folder.DefaultTemplate;

View File

@ -39,7 +39,7 @@ namespace FileLiberator
/// 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 _, LibraryBook libraryBook, string extension)
=> Templates.File.GetFilename(libraryBook.ToDto(), AudibleFileStorage.DecryptInProgressDirectory, extension); => Templates.File.GetFilename(libraryBook.ToDto(), AudibleFileStorage.DecryptInProgressDirectory, extension, returnFirstExisting: true);
/// <summary> /// <summary>
/// PDF: audio file does not exist /// PDF: audio file does not exist

View File

@ -61,7 +61,12 @@ namespace FileLiberator
// decrypt failed // decrypt failed
if (!success) if (!success)
{
foreach (var tmpFile in entries)
FileUtility.SaferDelete(tmpFile.Path);
return new StatusHandler { "Decrypt failed" }; return new StatusHandler { "Decrypt failed" };
}
// moves new files from temp dir to final dest // moves new files from temp dir to final dest
var movedAudioFile = moveFilesToBooksDir(libraryBook, entries); var movedAudioFile = moveFilesToBooksDir(libraryBook, entries);
@ -92,7 +97,7 @@ namespace FileLiberator
var api = await libraryBook.GetApiAsync(); var api = await libraryBook.GetApiAsync();
var contentLic = await api.GetDownloadLicenseAsync(libraryBook.Book.AudibleProductId); var contentLic = await api.GetDownloadLicenseAsync(libraryBook.Book.AudibleProductId);
var audiobookDlLic = BuildDownloadLicense(config, contentLic); var audiobookDlLic = BuildDownloadOptions(config, contentLic);
var outFileName = AudibleFileStorage.Audio.GetInProgressFilename(libraryBook, audiobookDlLic.OutputFormat.ToString().ToLower()); var outFileName = AudibleFileStorage.Audio.GetInProgressFilename(libraryBook, audiobookDlLic.OutputFormat.ToString().ToLower());
var cacheDir = AudibleFileStorage.DownloadsInProgressDirectory; var cacheDir = AudibleFileStorage.DownloadsInProgressDirectory;
@ -132,28 +137,28 @@ namespace FileLiberator
} }
} }
private static DownloadLicense BuildDownloadLicense(Configuration config, AudibleApi.Common.ContentLicense contentLic) private static DownloadOptions BuildDownloadOptions(Configuration config, AudibleApi.Common.ContentLicense contentLic)
{ {
//I assume if ContentFormat == "MPEG" that the delivered file is an unencrypted mp3. //I assume if ContentFormat == "MPEG" that the delivered file is an unencrypted mp3.
//I also assume that if DrmType != Adrm, the file will be an mp3. //I also assume that if DrmType != Adrm, the file will be an mp3.
//These assumptions may be wrong, and only time and bug reports will tell. //These assumptions may be wrong, and only time and bug reports will tell.
var outputFormat =
contentLic?.ContentMetadata?.ContentReference?.ContentFormat == "MPEG" || bool encrypted = contentLic.DrmType == AudibleApi.Common.DrmType.Adrm;
(config.AllowLibationFixup && config.DecryptToLossy) ?
var outputFormat = !encrypted || (config.AllowLibationFixup && config.DecryptToLossy) ?
OutputFormat.Mp3 : OutputFormat.M4b; OutputFormat.Mp3 : OutputFormat.M4b;
var audiobookDlLic = new DownloadLicense var audiobookDlLic = new DownloadOptions
( (
contentLic?.ContentMetadata?.ContentUrl?.OfflineUrl, contentLic?.ContentMetadata?.ContentUrl?.OfflineUrl,
Resources.USER_AGENT Resources.USER_AGENT
) )
{ {
AudibleKey = contentLic?.Voucher?.Key, AudibleKey = contentLic?.Voucher?.Key,
AudibleIV = contentLic?.Voucher?.Iv, AudibleIV = contentLic?.Voucher?.Iv,
OutputFormat = outputFormat, OutputFormat = outputFormat,
TrimOutputToChapterLength = config.StripAudibleBrandAudio TrimOutputToChapterLength = config.StripAudibleBrandAudio,
RetainEncryptedFile = config.RetainAaxFile && encrypted
}; };
if (config.AllowLibationFixup || outputFormat == OutputFormat.Mp3) if (config.AllowLibationFixup || outputFormat == OutputFormat.Mp3)

View File

@ -29,14 +29,14 @@ namespace FileManager
public string IllegalCharacterReplacements { get; set; } public string IllegalCharacterReplacements { get; set; }
/// <summary>Generate a valid path for this file or directory</summary> /// <summary>Generate a valid path for this file or directory</summary>
public string GetFilePath() public string GetFilePath(bool returnFirstExisting = false)
{ {
var filename = Template; var filename = Template;
foreach (var r in ParameterReplacements) foreach (var r in ParameterReplacements)
filename = filename.Replace($"<{formatKey(r.Key)}>", formatValue(r.Value)); filename = filename.Replace($"<{formatKey(r.Key)}>", formatValue(r.Value));
return FileUtility.GetValidFilename(filename, IllegalCharacterReplacements); return FileUtility.GetValidFilename(filename, IllegalCharacterReplacements, returnFirstExisting);
} }
private static string formatKey(string key) private static string formatKey(string key)

View File

@ -48,7 +48,7 @@ namespace FileManager
/// <br/>- ensure uniqueness /// <br/>- ensure uniqueness
/// <br/>- enforce max file length /// <br/>- enforce max file length
/// </summary> /// </summary>
public static string GetValidFilename(string path, string illegalCharacterReplacements = "") public static string GetValidFilename(string path, string illegalCharacterReplacements = "", bool returnFirstExisting = false)
{ {
ArgumentValidator.EnsureNotNull(path, nameof(path)); ArgumentValidator.EnsureNotNull(path, nameof(path));
@ -69,7 +69,7 @@ namespace FileManager
fullfilename = removeInvalidWhitespace(fullfilename); fullfilename = removeInvalidWhitespace(fullfilename);
var i = 0; var i = 0;
while (File.Exists(fullfilename)) while (File.Exists(fullfilename) && !returnFirstExisting)
{ {
var increm = $" ({++i})"; var increm = $" ({++i})";
fullfilename = fileStem.Truncate(MAX_FILENAME_LENGTH - increm.Length - extension.Length) + increm + extension; fullfilename = fileStem.Truncate(MAX_FILENAME_LENGTH - increm.Length - extension.Length) + increm + extension;

View File

@ -117,6 +117,13 @@ namespace LibationFileManager
set => persistentDictionary.SetNonString(nameof(SplitFilesByChapter), value); set => persistentDictionary.SetNonString(nameof(SplitFilesByChapter), value);
} }
[Description("Retain the Aax file after successfully decrypting")]
public bool RetainAaxFile
{
get => persistentDictionary.GetNonString<bool>(nameof(RetainAaxFile));
set => persistentDictionary.SetNonString(nameof(RetainAaxFile), value);
}
public enum BadBookAction public enum BadBookAction
{ {
[Description("Ask each time what action to take.")] [Description("Ask each time what action to take.")]

View File

@ -230,9 +230,9 @@ namespace LibationFileManager
#region to file name #region to file name
/// <summary>USES LIVE CONFIGURATION VALUES</summary> /// <summary>USES LIVE CONFIGURATION VALUES</summary>
public string GetFilename(LibraryBookDto libraryBookDto, string dirFullPath, string extension) public string GetFilename(LibraryBookDto libraryBookDto, string dirFullPath, string extension, bool returnFirstExisting = false)
=> getFileNamingTemplate(libraryBookDto, Configuration.Instance.FileTemplate, dirFullPath, extension) => getFileNamingTemplate(libraryBookDto, Configuration.Instance.FileTemplate, dirFullPath, extension)
.GetFilePath(); .GetFilePath(returnFirstExisting);
#endregion #endregion
} }