Improved performance calculating "liberated" status
This commit is contained in:
parent
d5d72a13f6
commit
1375da2065
@ -25,11 +25,8 @@ namespace FileLiberator
|
|||||||
public DecryptBook DecryptBook { get; } = new DecryptBook();
|
public DecryptBook DecryptBook { get; } = new DecryptBook();
|
||||||
public DownloadPdf DownloadPdf { get; } = new DownloadPdf();
|
public DownloadPdf DownloadPdf { get; } = new DownloadPdf();
|
||||||
|
|
||||||
// ValidateAsync() doesn't need UI context
|
public bool Validate(LibraryBook libraryBook)
|
||||||
public async Task<bool> ValidateAsync(LibraryBook libraryBook)
|
=> !AudibleFileStorage.Audio.Exists(libraryBook.Book.AudibleProductId);
|
||||||
=> await validateAsync_ConfigureAwaitFalse(libraryBook.Book.AudibleProductId).ConfigureAwait(false);
|
|
||||||
private async Task<bool> validateAsync_ConfigureAwaitFalse(string productId)
|
|
||||||
=> !await AudibleFileStorage.Audio.ExistsAsync(productId);
|
|
||||||
|
|
||||||
// do NOT use ConfigureAwait(false) on ProcessAsync()
|
// do NOT use ConfigureAwait(false) on ProcessAsync()
|
||||||
// often calls events which prints to forms in the UI context
|
// often calls events which prints to forms in the UI context
|
||||||
|
|||||||
@ -34,12 +34,9 @@ namespace FileLiberator
|
|||||||
public event EventHandler<string> DecryptCompleted;
|
public event EventHandler<string> DecryptCompleted;
|
||||||
public event EventHandler<string> Completed;
|
public event EventHandler<string> Completed;
|
||||||
|
|
||||||
// ValidateAsync() doesn't need UI context
|
public bool Validate(LibraryBook libraryBook)
|
||||||
public async Task<bool> ValidateAsync(LibraryBook libraryBook)
|
=> AudibleFileStorage.AAX.Exists(libraryBook.Book.AudibleProductId)
|
||||||
=> await validateAsync_ConfigureAwaitFalse(libraryBook.Book.AudibleProductId).ConfigureAwait(false);
|
&& !AudibleFileStorage.Audio.Exists(libraryBook.Book.AudibleProductId);
|
||||||
private async Task<bool> validateAsync_ConfigureAwaitFalse(string productId)
|
|
||||||
=> await AudibleFileStorage.AAX.ExistsAsync(productId)
|
|
||||||
&& !await AudibleFileStorage.Audio.ExistsAsync(productId);
|
|
||||||
|
|
||||||
// do NOT use ConfigureAwait(false) on ProcessAsync()
|
// do NOT use ConfigureAwait(false) on ProcessAsync()
|
||||||
// often calls events which prints to forms in the UI context
|
// often calls events which prints to forms in the UI context
|
||||||
@ -51,13 +48,13 @@ namespace FileLiberator
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var aaxFilename = await AudibleFileStorage.AAX.GetAsync(libraryBook.Book.AudibleProductId);
|
var aaxFilename = AudibleFileStorage.AAX.GetPath(libraryBook.Book.AudibleProductId);
|
||||||
|
|
||||||
if (aaxFilename == null)
|
if (aaxFilename == null)
|
||||||
return new StatusHandler { "aaxFilename parameter is null" };
|
return new StatusHandler { "aaxFilename parameter is null" };
|
||||||
if (!FileUtility.FileExists(aaxFilename))
|
if (!FileUtility.FileExists(aaxFilename))
|
||||||
return new StatusHandler { $"Cannot find AAX file: {aaxFilename}" };
|
return new StatusHandler { $"Cannot find AAX file: {aaxFilename}" };
|
||||||
if (await AudibleFileStorage.Audio.ExistsAsync(libraryBook.Book.AudibleProductId))
|
if (AudibleFileStorage.Audio.Exists(libraryBook.Book.AudibleProductId))
|
||||||
return new StatusHandler { "Cannot find decrypt. Final audio file already exists" };
|
return new StatusHandler { "Cannot find decrypt. Final audio file already exists" };
|
||||||
|
|
||||||
var proposedOutputFile = Path.Combine(AudibleFileStorage.DecryptInProgress, $"[{libraryBook.Book.AudibleProductId}].m4b");
|
var proposedOutputFile = Path.Combine(AudibleFileStorage.DecryptInProgress, $"[{libraryBook.Book.AudibleProductId}].m4b");
|
||||||
@ -72,7 +69,7 @@ namespace FileLiberator
|
|||||||
Dinah.Core.IO.FileExt.SafeDelete(aaxFilename);
|
Dinah.Core.IO.FileExt.SafeDelete(aaxFilename);
|
||||||
|
|
||||||
var statusHandler = new StatusHandler();
|
var statusHandler = new StatusHandler();
|
||||||
var finalAudioExists = await AudibleFileStorage.Audio.ExistsAsync(libraryBook.Book.AudibleProductId);
|
var finalAudioExists = AudibleFileStorage.Audio.Exists(libraryBook.Book.AudibleProductId);
|
||||||
if (!finalAudioExists)
|
if (!finalAudioExists)
|
||||||
statusHandler.AddError("Cannot find final audio file after decryption");
|
statusHandler.AddError("Cannot find final audio file after decryption");
|
||||||
return statusHandler;
|
return statusHandler;
|
||||||
|
|||||||
@ -17,16 +17,16 @@ namespace FileLiberator
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class DownloadBook : DownloadableBase
|
public class DownloadBook : DownloadableBase
|
||||||
{
|
{
|
||||||
public override async Task<bool> ValidateAsync(LibraryBook libraryBook)
|
public override bool Validate(LibraryBook libraryBook)
|
||||||
=> !await AudibleFileStorage.Audio.ExistsAsync(libraryBook.Book.AudibleProductId)
|
=> !AudibleFileStorage.Audio.Exists(libraryBook.Book.AudibleProductId)
|
||||||
&& !await AudibleFileStorage.AAX.ExistsAsync(libraryBook.Book.AudibleProductId);
|
&& !AudibleFileStorage.AAX.Exists(libraryBook.Book.AudibleProductId);
|
||||||
|
|
||||||
public override async Task<StatusHandler> ProcessItemAsync(LibraryBook libraryBook)
|
public override async Task<StatusHandler> ProcessItemAsync(LibraryBook libraryBook)
|
||||||
{
|
{
|
||||||
var tempAaxFilename = getDownloadPath(libraryBook);
|
var tempAaxFilename = getDownloadPath(libraryBook);
|
||||||
var actualFilePath = await downloadBookAsync(libraryBook, tempAaxFilename);
|
var actualFilePath = await downloadBookAsync(libraryBook, tempAaxFilename);
|
||||||
moveBook(libraryBook, actualFilePath);
|
moveBook(libraryBook, actualFilePath);
|
||||||
return await verifyDownloadAsync(libraryBook);
|
return verifyDownload(libraryBook);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string getDownloadPath(LibraryBook libraryBook)
|
private static string getDownloadPath(LibraryBook libraryBook)
|
||||||
@ -58,8 +58,8 @@ namespace FileLiberator
|
|||||||
Invoke_StatusUpdate($"Successfully downloaded. Moved to: {newAaxFilename}");
|
Invoke_StatusUpdate($"Successfully downloaded. Moved to: {newAaxFilename}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<StatusHandler> verifyDownloadAsync(LibraryBook libraryBook)
|
private static StatusHandler verifyDownload(LibraryBook libraryBook)
|
||||||
=> !await AudibleFileStorage.AAX.ExistsAsync(libraryBook.Book.AudibleProductId)
|
=> !AudibleFileStorage.AAX.Exists(libraryBook.Book.AudibleProductId)
|
||||||
? new StatusHandler { "Downloaded AAX file cannot be found" }
|
? new StatusHandler { "Downloaded AAX file cannot be found" }
|
||||||
: new StatusHandler();
|
: new StatusHandler();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,26 +12,26 @@ namespace FileLiberator
|
|||||||
{
|
{
|
||||||
public class DownloadPdf : DownloadableBase
|
public class DownloadPdf : DownloadableBase
|
||||||
{
|
{
|
||||||
public override async Task<bool> ValidateAsync(LibraryBook libraryBook)
|
public override bool Validate(LibraryBook libraryBook)
|
||||||
=> !string.IsNullOrWhiteSpace(getdownloadUrl(libraryBook))
|
=> !string.IsNullOrWhiteSpace(getdownloadUrl(libraryBook))
|
||||||
&& !await AudibleFileStorage.PDF.ExistsAsync(libraryBook.Book.AudibleProductId);
|
&& !AudibleFileStorage.PDF.Exists(libraryBook.Book.AudibleProductId);
|
||||||
|
|
||||||
private static string getdownloadUrl(LibraryBook libraryBook)
|
private static string getdownloadUrl(LibraryBook libraryBook)
|
||||||
=> libraryBook?.Book?.Supplements?.FirstOrDefault()?.Url;
|
=> libraryBook?.Book?.Supplements?.FirstOrDefault()?.Url;
|
||||||
|
|
||||||
public override async Task<StatusHandler> ProcessItemAsync(LibraryBook libraryBook)
|
public override async Task<StatusHandler> ProcessItemAsync(LibraryBook libraryBook)
|
||||||
{
|
{
|
||||||
var proposedDownloadFilePath = await getProposedDownloadFilePathAsync(libraryBook);
|
var proposedDownloadFilePath = getProposedDownloadFilePath(libraryBook);
|
||||||
await downloadPdfAsync(libraryBook, proposedDownloadFilePath);
|
await downloadPdfAsync(libraryBook, proposedDownloadFilePath);
|
||||||
return await verifyDownloadAsync(libraryBook);
|
return verifyDownload(libraryBook);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<string> getProposedDownloadFilePathAsync(LibraryBook libraryBook)
|
private static string getProposedDownloadFilePath(LibraryBook libraryBook)
|
||||||
{
|
{
|
||||||
// if audio file exists, get it's dir. else return base Book dir
|
// if audio file exists, get it's dir. else return base Book dir
|
||||||
var destinationDir =
|
var destinationDir =
|
||||||
// this is safe b/c GetDirectoryName(null) == null
|
// this is safe b/c GetDirectoryName(null) == null
|
||||||
Path.GetDirectoryName(await AudibleFileStorage.Audio.GetAsync(libraryBook.Book.AudibleProductId))
|
Path.GetDirectoryName(AudibleFileStorage.Audio.GetPath(libraryBook.Book.AudibleProductId))
|
||||||
?? AudibleFileStorage.PDF.StorageDirectory;
|
?? AudibleFileStorage.PDF.StorageDirectory;
|
||||||
|
|
||||||
return Path.Combine(destinationDir, Path.GetFileName(getdownloadUrl(libraryBook)));
|
return Path.Combine(destinationDir, Path.GetFileName(getdownloadUrl(libraryBook)));
|
||||||
@ -45,8 +45,8 @@ namespace FileLiberator
|
|||||||
(p) => client.DownloadFileAsync(getdownloadUrl(libraryBook), proposedDownloadFilePath, p));
|
(p) => client.DownloadFileAsync(getdownloadUrl(libraryBook), proposedDownloadFilePath, p));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<StatusHandler> verifyDownloadAsync(LibraryBook libraryBook)
|
private static StatusHandler verifyDownload(LibraryBook libraryBook)
|
||||||
=> !await AudibleFileStorage.PDF.ExistsAsync(libraryBook.Book.AudibleProductId)
|
=> !AudibleFileStorage.PDF.Exists(libraryBook.Book.AudibleProductId)
|
||||||
? new StatusHandler { "Downloaded PDF cannot be found" }
|
? new StatusHandler { "Downloaded PDF cannot be found" }
|
||||||
: new StatusHandler();
|
: new StatusHandler();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,7 +18,7 @@ namespace FileLiberator
|
|||||||
public event EventHandler<string> StatusUpdate;
|
public event EventHandler<string> StatusUpdate;
|
||||||
protected void Invoke_StatusUpdate(string message) => StatusUpdate?.Invoke(this, message);
|
protected void Invoke_StatusUpdate(string message) => StatusUpdate?.Invoke(this, message);
|
||||||
|
|
||||||
public abstract Task<bool> ValidateAsync(LibraryBook libraryBook);
|
public abstract bool Validate(LibraryBook libraryBook);
|
||||||
|
|
||||||
public abstract Task<StatusHandler> ProcessItemAsync(LibraryBook libraryBook);
|
public abstract Task<StatusHandler> ProcessItemAsync(LibraryBook libraryBook);
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@ namespace FileLiberator
|
|||||||
event EventHandler<string> Completed;
|
event EventHandler<string> Completed;
|
||||||
|
|
||||||
/// <returns>True == Valid</returns>
|
/// <returns>True == Valid</returns>
|
||||||
Task<bool> ValidateAsync(LibraryBook libraryBook);
|
bool Validate(LibraryBook libraryBook);
|
||||||
|
|
||||||
/// <returns>True == success</returns>
|
/// <returns>True == success</returns>
|
||||||
Task<StatusHandler> ProcessAsync(LibraryBook libraryBook);
|
Task<StatusHandler> ProcessAsync(LibraryBook libraryBook);
|
||||||
|
|||||||
@ -9,8 +9,7 @@ namespace FileLiberator
|
|||||||
{
|
{
|
||||||
//
|
//
|
||||||
// DO NOT USE ConfigureAwait(false) WITH ProcessAsync() unless ensuring ProcessAsync() implementation is cross-thread compatible
|
// DO NOT USE ConfigureAwait(false) WITH ProcessAsync() unless ensuring ProcessAsync() implementation is cross-thread compatible
|
||||||
// - ValidateAsync() doesn't need UI context. however, each class already uses ConfigureAwait(false)
|
// ProcessAsync() often does a lot with forms in the UI context
|
||||||
// - ProcessAsync() often does a lot with forms in the UI context
|
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
@ -18,7 +17,7 @@ namespace FileLiberator
|
|||||||
/// <returns>Returns either the status handler from the process, or null if all books have been processed</returns>
|
/// <returns>Returns either the status handler from the process, or null if all books have been processed</returns>
|
||||||
public static async Task<StatusHandler> ProcessFirstValidAsync(this IProcessable processable)
|
public static async Task<StatusHandler> ProcessFirstValidAsync(this IProcessable processable)
|
||||||
{
|
{
|
||||||
var libraryBook = await processable.GetNextValidAsync();
|
var libraryBook = processable.GetNextValid();
|
||||||
if (libraryBook == null)
|
if (libraryBook == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@ -30,19 +29,19 @@ namespace FileLiberator
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<LibraryBook> GetNextValidAsync(this IProcessable processable)
|
public static LibraryBook GetNextValid(this IProcessable processable)
|
||||||
{
|
{
|
||||||
var libraryBooks = LibraryQueries.GetLibrary_Flat_NoTracking();
|
var libraryBooks = LibraryQueries.GetLibrary_Flat_NoTracking();
|
||||||
|
|
||||||
foreach (var libraryBook in libraryBooks)
|
foreach (var libraryBook in libraryBooks)
|
||||||
if (await processable.ValidateAsync(libraryBook))
|
if (processable.Validate(libraryBook))
|
||||||
return libraryBook;
|
return libraryBook;
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<StatusHandler> TryProcessAsync(this IProcessable processable, LibraryBook libraryBook)
|
public static async Task<StatusHandler> TryProcessAsync(this IProcessable processable, LibraryBook libraryBook)
|
||||||
=> await processable.ValidateAsync(libraryBook)
|
=> processable.Validate(libraryBook)
|
||||||
? await processable.ProcessAsync(libraryBook)
|
? await processable.ProcessAsync(libraryBook)
|
||||||
: new StatusHandler();
|
: new StatusHandler();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Text.RegularExpressions;
|
||||||
using Dinah.Core;
|
using Dinah.Core;
|
||||||
using Dinah.Core.Collections.Generic;
|
using Dinah.Core.Collections.Generic;
|
||||||
|
|
||||||
@ -21,23 +21,6 @@ namespace FileManager
|
|||||||
public sealed class AudibleFileStorage : Enumeration<AudibleFileStorage>
|
public sealed class AudibleFileStorage : Enumeration<AudibleFileStorage>
|
||||||
{
|
{
|
||||||
#region static
|
#region static
|
||||||
// centralize filetype mappings to ensure uniqueness
|
|
||||||
private static Dictionary<string, FileType> extensionMap => new Dictionary<string, FileType>
|
|
||||||
{
|
|
||||||
[".m4b"] = FileType.Audio,
|
|
||||||
[".mp3"] = FileType.Audio,
|
|
||||||
[".aac"] = FileType.Audio,
|
|
||||||
[".mp4"] = FileType.Audio,
|
|
||||||
[".m4a"] = FileType.Audio,
|
|
||||||
[".ogg"] = FileType.Audio,
|
|
||||||
[".flac"] = FileType.Audio,
|
|
||||||
|
|
||||||
[".aax"] = FileType.AAX,
|
|
||||||
|
|
||||||
[".pdf"] = FileType.PDF,
|
|
||||||
[".zip"] = FileType.PDF,
|
|
||||||
};
|
|
||||||
|
|
||||||
public static AudibleFileStorage Audio { get; }
|
public static AudibleFileStorage Audio { get; }
|
||||||
public static AudibleFileStorage AAX { get; }
|
public static AudibleFileStorage AAX { get; }
|
||||||
public static AudibleFileStorage PDF { get; }
|
public static AudibleFileStorage PDF { get; }
|
||||||
@ -81,9 +64,9 @@ namespace FileManager
|
|||||||
|
|
||||||
// must do this in static ctor, not w/inline properties
|
// must do this in static ctor, not w/inline properties
|
||||||
// static properties init before static ctor so these dir.s would still be null
|
// static properties init before static ctor so these dir.s would still be null
|
||||||
Audio = new AudibleFileStorage(FileType.Audio, BooksDirectory);
|
Audio = new AudibleFileStorage(FileType.Audio, BooksDirectory, "m4b", "mp3", "aac", "mp4", "m4a", "ogg", "flac");
|
||||||
AAX = new AudibleFileStorage(FileType.AAX, DownloadsFinal);
|
AAX = new AudibleFileStorage(FileType.AAX, DownloadsFinal, "aax");
|
||||||
PDF = new AudibleFileStorage(FileType.PDF, BooksDirectory);
|
PDF = new AudibleFileStorage(FileType.PDF, BooksDirectory, "pdf", "zip");
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -92,9 +75,14 @@ namespace FileManager
|
|||||||
|
|
||||||
public string StorageDirectory => DisplayName;
|
public string StorageDirectory => DisplayName;
|
||||||
|
|
||||||
public IEnumerable<string> Extensions => extensionMap.Where(kvp => kvp.Value == FileType).Select(kvp => kvp.Key);
|
private IEnumerable<string> extensions_noDots { get; }
|
||||||
|
private string extAggr { get; }
|
||||||
|
|
||||||
private AudibleFileStorage(FileType fileType, string storageDirectory) : base((int)fileType, storageDirectory) { }
|
private AudibleFileStorage(FileType fileType, string storageDirectory, params string[] extensions) : base((int)fileType, storageDirectory)
|
||||||
|
{
|
||||||
|
extensions_noDots = extensions.Select(ext => ext.Trim('.')).ToList();
|
||||||
|
extAggr = extensions_noDots.Aggregate((a, b) => $"{a}|{b}");
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Example for full books:
|
/// Example for full books:
|
||||||
@ -102,13 +90,10 @@ namespace FileManager
|
|||||||
/// - a directory name has the product id and an audio file is immediately inside
|
/// - a directory name has the product id and an audio file is immediately inside
|
||||||
/// - any audio filename contains the product id
|
/// - any audio filename contains the product id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<bool> ExistsAsync(string productId)
|
public bool Exists(string productId)
|
||||||
=> (await GetAsync(productId).ConfigureAwait(false)) != null;
|
=> GetPath(productId) != null;
|
||||||
|
|
||||||
public async Task<string> GetAsync(string productId)
|
public string GetPath(string productId)
|
||||||
=> await getAsync(productId).ConfigureAwait(false);
|
|
||||||
|
|
||||||
private async Task<string> getAsync(string productId)
|
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
var cachedFile = FilePathCache.GetPath(productId, FileType);
|
var cachedFile = FilePathCache.GetPath(productId, FileType);
|
||||||
@ -116,64 +101,19 @@ namespace FileManager
|
|||||||
return cachedFile;
|
return cachedFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is how files are saved by default. check this method first
|
var firstOrNull =
|
||||||
{
|
Directory
|
||||||
var diskFile_byDirName = (await Task.Run(() => getFile_checkDirName(productId)).ConfigureAwait(false));
|
.EnumerateFiles(StorageDirectory, "*.*", SearchOption.AllDirectories)
|
||||||
if (diskFile_byDirName != null)
|
.FirstOrDefault(s => Regex.IsMatch(s, $@"{productId}.*?\.({extAggr})$", RegexOptions.IgnoreCase));
|
||||||
{
|
|
||||||
FilePathCache.Upsert(productId, FileType, diskFile_byDirName);
|
|
||||||
return diskFile_byDirName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var diskFile_byFileName = (await Task.Run(() => getFile_checkFileName(productId, StorageDirectory, SearchOption.AllDirectories)).ConfigureAwait(false));
|
|
||||||
if (diskFile_byFileName != null)
|
|
||||||
{
|
|
||||||
FilePathCache.Upsert(productId, FileType, diskFile_byFileName);
|
|
||||||
return diskFile_byFileName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (firstOrNull is null)
|
||||||
return null;
|
return null;
|
||||||
|
FilePathCache.Upsert(productId, FileType, firstOrNull);
|
||||||
|
return firstOrNull;
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns audio file if there is a directory where both are true
|
|
||||||
// - the directory name contains the productId
|
|
||||||
// - the directory contains an audio file in it's top dir (not recursively)
|
|
||||||
private string getFile_checkDirName(string productId)
|
|
||||||
{
|
|
||||||
foreach (var d in Directory.EnumerateDirectories(StorageDirectory, "*.*", SearchOption.AllDirectories))
|
|
||||||
{
|
|
||||||
if (!fileHasId(d, productId))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var firstAudio = Directory
|
|
||||||
.EnumerateFiles(d, "*.*", SearchOption.TopDirectoryOnly)
|
|
||||||
.FirstOrDefault(f => IsFileTypeMatch(f));
|
|
||||||
if (firstAudio != null)
|
|
||||||
return firstAudio;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns audio file if there is an file where both are true
|
|
||||||
// - the file name contains the productId
|
|
||||||
// - the file is an audio type
|
|
||||||
private string getFile_checkFileName(string productId, string dir, SearchOption searchOption)
|
|
||||||
=> Directory
|
|
||||||
.EnumerateFiles(dir, "*.*", searchOption)
|
|
||||||
.FirstOrDefault(f => fileHasId(f, productId) && IsFileTypeMatch(f));
|
|
||||||
|
|
||||||
public bool IsFileTypeMatch(string filename)
|
|
||||||
=> Extensions.ContainsInsensative(Path.GetExtension(filename));
|
|
||||||
|
|
||||||
public bool IsFileTypeMatch(FileInfo fileInfo)
|
public bool IsFileTypeMatch(FileInfo fileInfo)
|
||||||
=> Extensions.ContainsInsensative(fileInfo.Extension);
|
=> extensions_noDots.ContainsInsensative(fileInfo.Extension.Trim('.'));
|
||||||
|
|
||||||
// use GetFileName, NOT GetFileNameWithoutExtension. This tests files AND directories. if the dir has a dot in the final part of the path, it will be treated like the file extension
|
|
||||||
private static bool fileHasId(string file, string productId)
|
|
||||||
=> Path.GetFileName(file).ContainsInsensitive(productId);
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,7 @@ namespace FileManager
|
|||||||
public string Path { get; set; }
|
public string Path { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<CacheEntry> inMemoryCache = new List<CacheEntry>();
|
static List<CacheEntry> inMemoryCache { get; } = new List<CacheEntry>();
|
||||||
|
|
||||||
public static string JsonFile => Path.Combine(Configuration.Instance.LibationFiles, "FilePaths.json");
|
public static string JsonFile => Path.Combine(Configuration.Instance.LibationFiles, "FilePaths.json");
|
||||||
|
|
||||||
|
|||||||
@ -30,7 +30,7 @@ namespace LibationWinForm.BookLiberation
|
|||||||
|
|
||||||
static async Task<StatusHandler> ProcessValidateLibraryBookAsync(IProcessable processable, LibraryBook libraryBook)
|
static async Task<StatusHandler> ProcessValidateLibraryBookAsync(IProcessable processable, LibraryBook libraryBook)
|
||||||
{
|
{
|
||||||
if (!await processable.ValidateAsync(libraryBook))
|
if (!processable.Validate(libraryBook))
|
||||||
return new StatusHandler { "Validation failed" };
|
return new StatusHandler { "Validation failed" };
|
||||||
return await processable.ProcessAsync(libraryBook);
|
return await processable.ProcessAsync(libraryBook);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,7 +37,7 @@ namespace LibationWinForm
|
|||||||
beginPdfBackupsToolStripMenuItem_format = beginPdfBackupsToolStripMenuItem.Text;
|
beginPdfBackupsToolStripMenuItem_format = beginPdfBackupsToolStripMenuItem.Text;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void Form1_Load(object sender, EventArgs e)
|
private void Form1_Load(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
// call static ctor. There are bad race conditions if static ctor is first executed when we're running in parallel in setBackupCountsAsync()
|
// call static ctor. There are bad race conditions if static ctor is first executed when we're running in parallel in setBackupCountsAsync()
|
||||||
var foo = FilePathCache.JsonFile;
|
var foo = FilePathCache.JsonFile;
|
||||||
@ -59,7 +59,7 @@ namespace LibationWinForm
|
|||||||
backupsCountsLbl.Text = "[Calculating backed up book quantities]";
|
backupsCountsLbl.Text = "[Calculating backed up book quantities]";
|
||||||
pdfsCountsLbl.Text = "[Calculating backed up PDFs]";
|
pdfsCountsLbl.Text = "[Calculating backed up PDFs]";
|
||||||
|
|
||||||
await setBackupCountsAsync();
|
setBackupCounts();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,52 +103,34 @@ namespace LibationWinForm
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region bottom: backup counts
|
#region bottom: backup counts
|
||||||
private async Task setBackupCountsAsync()
|
private void setBackupCounts()
|
||||||
{
|
{
|
||||||
var books = LibraryQueries.GetLibrary_Flat_NoTracking()
|
var books = LibraryQueries.GetLibrary_Flat_NoTracking()
|
||||||
.Select(sp => sp.Book)
|
.Select(sp => sp.Book)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
await setBookBackupCountsAsync(books).ConfigureAwait(false);
|
setBookBackupCounts(books);
|
||||||
await setPdfBackupCountsAsync(books).ConfigureAwait(false);
|
setPdfBackupCounts(books);
|
||||||
}
|
}
|
||||||
enum AudioFileState { full, aax, none }
|
enum AudioFileState { full, aax, none }
|
||||||
private async Task setBookBackupCountsAsync(IEnumerable<Book> books)
|
private void setBookBackupCounts(IEnumerable<Book> books)
|
||||||
{
|
{
|
||||||
var libraryProductIds = books
|
AudioFileState getAudioFileState(string productId)
|
||||||
.Select(b => b.AudibleProductId)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
var noProgress = 0;
|
|
||||||
var downloadedOnly = 0;
|
|
||||||
var fullyBackedUp = 0;
|
|
||||||
|
|
||||||
|
|
||||||
//// serial
|
|
||||||
//foreach (var productId in libraryProductIds)
|
|
||||||
//{
|
|
||||||
// if (await AudibleFileStorage.Audio.ExistsAsync(productId))
|
|
||||||
// fullyBackedUp++;
|
|
||||||
// else if (await AudibleFileStorage.AAX.ExistsAsync(productId))
|
|
||||||
// downloadedOnly++;
|
|
||||||
// else
|
|
||||||
// noProgress++;
|
|
||||||
//}
|
|
||||||
|
|
||||||
// parallel
|
|
||||||
async Task<AudioFileState> getAudioFileStateAsync(string productId)
|
|
||||||
{
|
{
|
||||||
if (await AudibleFileStorage.Audio.ExistsAsync(productId))
|
if (AudibleFileStorage.Audio.Exists(productId))
|
||||||
return AudioFileState.full;
|
return AudioFileState.full;
|
||||||
if (await AudibleFileStorage.AAX.ExistsAsync(productId))
|
if (AudibleFileStorage.AAX.Exists(productId))
|
||||||
return AudioFileState.aax;
|
return AudioFileState.aax;
|
||||||
return AudioFileState.none;
|
return AudioFileState.none;
|
||||||
}
|
}
|
||||||
var tasks = libraryProductIds.Select(productId => getAudioFileStateAsync(productId));
|
|
||||||
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
|
var results = books
|
||||||
fullyBackedUp = results.Count(r => r == AudioFileState.full);
|
.AsParallel()
|
||||||
downloadedOnly = results.Count(r => r == AudioFileState.aax);
|
.Select(b => getAudioFileState(b.AudibleProductId))
|
||||||
noProgress = results.Count(r => r == AudioFileState.none);
|
.ToList();
|
||||||
|
var fullyBackedUp = results.Count(r => r == AudioFileState.full);
|
||||||
|
var downloadedOnly = results.Count(r => r == AudioFileState.aax);
|
||||||
|
var noProgress = results.Count(r => r == AudioFileState.none);
|
||||||
|
|
||||||
// update bottom numbers
|
// update bottom numbers
|
||||||
var pending = noProgress + downloadedOnly;
|
var pending = noProgress + downloadedOnly;
|
||||||
@ -166,32 +148,15 @@ namespace LibationWinForm
|
|||||||
menuStrip1.UIThread(() => beginBookBackupsToolStripMenuItem.Enabled = pending > 0);
|
menuStrip1.UIThread(() => beginBookBackupsToolStripMenuItem.Enabled = pending > 0);
|
||||||
menuStrip1.UIThread(() => beginBookBackupsToolStripMenuItem.Text = string.Format(beginBookBackupsToolStripMenuItem_format, menuItemText));
|
menuStrip1.UIThread(() => beginBookBackupsToolStripMenuItem.Text = string.Format(beginBookBackupsToolStripMenuItem_format, menuItemText));
|
||||||
}
|
}
|
||||||
private async Task setPdfBackupCountsAsync(IEnumerable<Book> books)
|
private void setPdfBackupCounts(IEnumerable<Book> books)
|
||||||
{
|
{
|
||||||
var libraryProductIds = books
|
var boolResults = books
|
||||||
|
.AsParallel()
|
||||||
.Where(b => b.Supplements.Any())
|
.Where(b => b.Supplements.Any())
|
||||||
.Select(b => b.AudibleProductId)
|
.Select(b => AudibleFileStorage.PDF.Exists(b.AudibleProductId))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
var downloaded = boolResults.Count(r => r);
|
||||||
int notDownloaded;
|
var notDownloaded = boolResults.Count(r => !r);
|
||||||
int downloaded;
|
|
||||||
|
|
||||||
//// serial
|
|
||||||
//notDownloaded = 0;
|
|
||||||
//downloaded = 0;
|
|
||||||
//foreach (var productId in libraryProductIds)
|
|
||||||
//{
|
|
||||||
// if (await AudibleFileStorage.PDF.ExistsAsync(productId))
|
|
||||||
// downloaded++;
|
|
||||||
// else
|
|
||||||
// notDownloaded++;
|
|
||||||
//}
|
|
||||||
|
|
||||||
// parallel
|
|
||||||
var tasks = libraryProductIds.Select(productId => AudibleFileStorage.PDF.ExistsAsync(productId));
|
|
||||||
var boolResults = await Task.WhenAll(tasks).ConfigureAwait(false);
|
|
||||||
downloaded = boolResults.Count(r => r);
|
|
||||||
notDownloaded = boolResults.Count(r => !r);
|
|
||||||
|
|
||||||
// update bottom numbers
|
// update bottom numbers
|
||||||
var text
|
var text
|
||||||
@ -258,7 +223,7 @@ namespace LibationWinForm
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region index menu
|
#region index menu
|
||||||
private async void scanLibraryToolStripMenuItem_Click(object sender, EventArgs e)
|
private void scanLibraryToolStripMenuItem_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
using var dialog = new IndexLibraryDialog();
|
using var dialog = new IndexLibraryDialog();
|
||||||
dialog.ShowDialog();
|
dialog.ShowDialog();
|
||||||
@ -270,7 +235,7 @@ namespace LibationWinForm
|
|||||||
|
|
||||||
// update backup counts if we have new library items
|
// update backup counts if we have new library items
|
||||||
if (newAdded > 0)
|
if (newAdded > 0)
|
||||||
await setBackupCountsAsync();
|
setBackupCounts();
|
||||||
|
|
||||||
if (totalProcessed > 0)
|
if (totalProcessed > 0)
|
||||||
reloadGrid();
|
reloadGrid();
|
||||||
@ -278,21 +243,21 @@ namespace LibationWinForm
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region liberate menu
|
#region liberate menu
|
||||||
private async void setBackupCountsAsync(object _, string __) => await setBackupCountsAsync();
|
private void setBackupCounts(object _, string __) => setBackupCounts();
|
||||||
|
|
||||||
private async void beginBookBackupsToolStripMenuItem_Click(object sender, EventArgs e)
|
private async void beginBookBackupsToolStripMenuItem_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
var backupBook = BookLiberation.ProcessorAutomationController.GetWiredUpBackupBook();
|
var backupBook = BookLiberation.ProcessorAutomationController.GetWiredUpBackupBook();
|
||||||
backupBook.DownloadBook.Completed += setBackupCountsAsync;
|
backupBook.DownloadBook.Completed += setBackupCounts;
|
||||||
backupBook.DecryptBook.Completed += setBackupCountsAsync;
|
backupBook.DecryptBook.Completed += setBackupCounts;
|
||||||
backupBook.DownloadPdf.Completed += setBackupCountsAsync;
|
backupBook.DownloadPdf.Completed += setBackupCounts;
|
||||||
await BookLiberation.ProcessorAutomationController.RunAutomaticBackup(backupBook);
|
await BookLiberation.ProcessorAutomationController.RunAutomaticBackup(backupBook);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void beginPdfBackupsToolStripMenuItem_Click(object sender, EventArgs e)
|
private async void beginPdfBackupsToolStripMenuItem_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
var downloadPdf = BookLiberation.ProcessorAutomationController.GetWiredUpDownloadPdf();
|
var downloadPdf = BookLiberation.ProcessorAutomationController.GetWiredUpDownloadPdf();
|
||||||
downloadPdf.Completed += setBackupCountsAsync;
|
downloadPdf.Completed += setBackupCounts;
|
||||||
await BookLiberation.ProcessorAutomationController.RunAutomaticDownload(downloadPdf);
|
await BookLiberation.ProcessorAutomationController.RunAutomaticDownload(downloadPdf);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@ -168,8 +168,8 @@ namespace LibationWinForm
|
|||||||
get
|
get
|
||||||
{
|
{
|
||||||
var print
|
var print
|
||||||
= FileManager.AudibleFileStorage.Audio.ExistsAsync(book.AudibleProductId).GetAwaiter().GetResult() ? "Liberated"
|
= FileManager.AudibleFileStorage.Audio.Exists(book.AudibleProductId) ? "Liberated"
|
||||||
: FileManager.AudibleFileStorage.AAX.ExistsAsync(book.AudibleProductId).GetAwaiter().GetResult() ? "DRM"
|
: FileManager.AudibleFileStorage.AAX.Exists(book.AudibleProductId) ? "DRM"
|
||||||
: "NOT d/l'ed";
|
: "NOT d/l'ed";
|
||||||
|
|
||||||
if (!book.Supplements.Any())
|
if (!book.Supplements.Any())
|
||||||
@ -178,7 +178,7 @@ namespace LibationWinForm
|
|||||||
print += "\r\n";
|
print += "\r\n";
|
||||||
|
|
||||||
var downloadStatuses = book.Supplements
|
var downloadStatuses = book.Supplements
|
||||||
.Select(d => FileManager.AudibleFileStorage.PDF.ExistsAsync(book.AudibleProductId).GetAwaiter().GetResult())
|
.Select(d => FileManager.AudibleFileStorage.PDF.Exists(book.AudibleProductId))
|
||||||
// break delayed execution right now!
|
// break delayed execution right now!
|
||||||
.ToList();
|
.ToList();
|
||||||
var count = downloadStatuses.Count;
|
var count = downloadStatuses.Count;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user