refactor out most of TransitionalFileLocator. Almost done with new stateful is-liberated paradigm
This commit is contained in:
parent
a639857ec6
commit
aa56bb74a1
@ -259,13 +259,13 @@ namespace ApplicationServices
|
|||||||
// below are queries, not commands. maybe I should make a LibraryQueries. except there's already one of those...
|
// below are queries, not commands. maybe I should make a LibraryQueries. except there's already one of those...
|
||||||
|
|
||||||
public static LiberatedState Liberated_Status(Book book)
|
public static LiberatedState Liberated_Status(Book book)
|
||||||
=> TransitionalFileLocator.Audio_Exists(book) ? LiberatedState.Liberated
|
=> book.Audio_Exists ? LiberatedState.Liberated
|
||||||
: TransitionalFileLocator.AAXC_Exists(book) ? LiberatedState.PartialDownload
|
: FileManager.AudibleFileStorage.AaxcExists(book.AudibleProductId) ? LiberatedState.PartialDownload
|
||||||
: LiberatedState.NotDownloaded;
|
: LiberatedState.NotDownloaded;
|
||||||
|
|
||||||
public static PdfState Pdf_Status(Book book)
|
public static PdfState Pdf_Status(Book book)
|
||||||
=> !book.Supplements.Any() ? PdfState.NoPdf
|
=> !book.Supplements.Any() ? PdfState.NoPdf
|
||||||
: TransitionalFileLocator.PDF_Exists(book) ? PdfState.Downloaded
|
: book.PDF_Exists ? PdfState.Downloaded
|
||||||
: PdfState.NotDownloaded;
|
: PdfState.NotDownloaded;
|
||||||
|
|
||||||
public record LibraryStats(int booksFullyBackedUp, int booksDownloadedOnly, int booksNoProgress, int pdfsDownloaded, int pdfsNotDownloaded) { }
|
public record LibraryStats(int booksFullyBackedUp, int booksDownloadedOnly, int booksNoProgress, int pdfsDownloaded, int pdfsNotDownloaded) { }
|
||||||
|
|||||||
@ -17,30 +17,5 @@ namespace ApplicationServices
|
|||||||
|
|
||||||
return AudibleFileStorage.Audio.GetPath(book.AudibleProductId);
|
return AudibleFileStorage.Audio.GetPath(book.AudibleProductId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool PDF_Exists(Book book)
|
|
||||||
{
|
|
||||||
var status = book?.UserDefinedItem?.PdfStatus;
|
|
||||||
if (status.HasValue && status.Value == LiberatedStatus.Liberated)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return AudibleFileStorage.PDF.Exists(book.AudibleProductId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool Audio_Exists(Book book)
|
|
||||||
{
|
|
||||||
var status = book?.UserDefinedItem?.BookStatus;
|
|
||||||
// true since Error == libhack
|
|
||||||
if (status.HasValue && status.Value != LiberatedStatus.NotLiberated)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return AudibleFileStorage.Audio.Exists(book.AudibleProductId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool AAXC_Exists(Book book)
|
|
||||||
{
|
|
||||||
// this one will actually stay the same. centralizing helps with organization in the interim though
|
|
||||||
return AudibleFileStorage.AAXC.Exists(book.AudibleProductId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -51,6 +51,25 @@ namespace DataLayer
|
|||||||
// is owned, not optional 1:1
|
// is owned, not optional 1:1
|
||||||
public UserDefinedItem UserDefinedItem { get; private set; }
|
public UserDefinedItem UserDefinedItem { get; private set; }
|
||||||
|
|
||||||
|
// UserDefinedItem convenience properties
|
||||||
|
public bool Audio_Exists
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var status = UserDefinedItem?.BookStatus;
|
||||||
|
// true since Error == libhack
|
||||||
|
return status.HasValue && status.Value != LiberatedStatus.NotLiberated;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public bool PDF_Exists
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var status = UserDefinedItem?.PdfStatus;
|
||||||
|
return (status.HasValue && status.Value == LiberatedStatus.Liberated);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// is owned, not optional 1:1
|
// is owned, not optional 1:1
|
||||||
/// <summary>The product's aggregate community rating</summary>
|
/// <summary>The product's aggregate community rating</summary>
|
||||||
public Rating Rating { get; private set; } = new Rating(0, 0, 0);
|
public Rating Rating { get; private set; } = new Rating(0, 0, 0);
|
||||||
|
|||||||
@ -37,7 +37,7 @@ namespace FileLiberator
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (ApplicationServices.TransitionalFileLocator.Audio_Exists(libraryBook.Book))
|
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" };
|
||||||
|
|
||||||
var outputAudioFilename = await aaxToM4bConverterDecryptAsync(AudibleFileStorage.DownloadsInProgress, AudibleFileStorage.DecryptInProgress, libraryBook);
|
var outputAudioFilename = await aaxToM4bConverterDecryptAsync(AudibleFileStorage.DownloadsInProgress, AudibleFileStorage.DecryptInProgress, libraryBook);
|
||||||
@ -49,8 +49,7 @@ namespace FileLiberator
|
|||||||
// moves files and returns dest dir
|
// moves files and returns dest dir
|
||||||
_ = moveFilesToBooksDir(libraryBook.Book, outputAudioFilename);
|
_ = moveFilesToBooksDir(libraryBook.Book, outputAudioFilename);
|
||||||
|
|
||||||
var finalAudioExists = ApplicationServices.TransitionalFileLocator.Audio_Exists(libraryBook.Book);
|
if (!libraryBook.Book.Audio_Exists)
|
||||||
if (!finalAudioExists)
|
|
||||||
return new StatusHandler { "Cannot find final audio file after decryption" };
|
return new StatusHandler { "Cannot find final audio file after decryption" };
|
||||||
|
|
||||||
// only need to update if success. if failure, it will remain at 0 == NotLiberated
|
// only need to update if success. if failure, it will remain at 0 == NotLiberated
|
||||||
@ -222,8 +221,7 @@ namespace FileLiberator
|
|||||||
throw new Exception(errorString("Locale"));
|
throw new Exception(errorString("Locale"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Validate(LibraryBook libraryBook)
|
public bool Validate(LibraryBook libraryBook) => !libraryBook.Book.Audio_Exists;
|
||||||
=> !ApplicationServices.TransitionalFileLocator.Audio_Exists(libraryBook.Book);
|
|
||||||
|
|
||||||
public void Cancel()
|
public void Cancel()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -15,7 +15,7 @@ namespace FileLiberator
|
|||||||
{
|
{
|
||||||
public override bool Validate(LibraryBook libraryBook)
|
public override bool Validate(LibraryBook libraryBook)
|
||||||
=> !string.IsNullOrWhiteSpace(getdownloadUrl(libraryBook))
|
=> !string.IsNullOrWhiteSpace(getdownloadUrl(libraryBook))
|
||||||
&& !ApplicationServices.TransitionalFileLocator.PDF_Exists(libraryBook.Book);
|
&& !libraryBook.Book.PDF_Exists;
|
||||||
|
|
||||||
public override async Task<StatusHandler> ProcessItemAsync(LibraryBook libraryBook)
|
public override async Task<StatusHandler> ProcessItemAsync(LibraryBook libraryBook)
|
||||||
{
|
{
|
||||||
@ -39,7 +39,7 @@ namespace FileLiberator
|
|||||||
return Path.Combine(existingPath, Path.GetFileName(file));
|
return Path.Combine(existingPath, Path.GetFileName(file));
|
||||||
|
|
||||||
var full = FileUtility.GetValidFilename(
|
var full = FileUtility.GetValidFilename(
|
||||||
AudibleFileStorage.PDF.StorageDirectory,
|
AudibleFileStorage.PdfStorageDirectory,
|
||||||
libraryBook.Book.Title,
|
libraryBook.Book.Title,
|
||||||
Path.GetExtension(file),
|
Path.GetExtension(file),
|
||||||
libraryBook.Book.AudibleProductId);
|
libraryBook.Book.AudibleProductId);
|
||||||
@ -61,7 +61,7 @@ namespace FileLiberator
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static StatusHandler verifyDownload(LibraryBook libraryBook)
|
private static StatusHandler verifyDownload(LibraryBook libraryBook)
|
||||||
=> !ApplicationServices.TransitionalFileLocator.PDF_Exists(libraryBook.Book)
|
=> !libraryBook.Book.PDF_Exists
|
||||||
? new StatusHandler { "Downloaded PDF cannot be found" }
|
? new StatusHandler { "Downloaded PDF cannot be found" }
|
||||||
: new StatusHandler();
|
: new StatusHandler();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,14 +15,14 @@ namespace FileManager
|
|||||||
protected abstract string[] Extensions { get; }
|
protected abstract string[] Extensions { get; }
|
||||||
public abstract string StorageDirectory { get; }
|
public abstract string StorageDirectory { get; }
|
||||||
|
|
||||||
|
public static string DownloadsInProgress => Directory.CreateDirectory(Path.Combine(Configuration.Instance.InProgress, "DownloadsInProgress")).FullName;
|
||||||
|
public static string DecryptInProgress => Directory.CreateDirectory(Path.Combine(Configuration.Instance.InProgress, "DecryptInProgress")).FullName;
|
||||||
|
public static string PdfStorageDirectory => BooksDirectory;
|
||||||
|
public static bool AaxcExists(string productId) => AAXC.Exists(productId);
|
||||||
|
|
||||||
#region static
|
#region static
|
||||||
public static AudioFileStorage Audio { get; } = new AudioFileStorage();
|
public static AudioFileStorage Audio { get; } = new AudioFileStorage();
|
||||||
public static AudibleFileStorage AAXC { get; } = new AaxcFileStorage();
|
public static AaxcFileStorage AAXC { get; } = new AaxcFileStorage();
|
||||||
public static AudibleFileStorage PDF { get; } = new PdfFileStorage();
|
|
||||||
|
|
||||||
public static string DownloadsInProgress => Directory.CreateDirectory(Path.Combine(Configuration.Instance.InProgress, "DownloadsInProgress")).FullName;
|
|
||||||
|
|
||||||
public static string DecryptInProgress => Directory.CreateDirectory(Path.Combine(Configuration.Instance.InProgress, "DecryptInProgress")).FullName;
|
|
||||||
|
|
||||||
public static string BooksDirectory
|
public static string BooksDirectory
|
||||||
{
|
{
|
||||||
@ -34,13 +34,13 @@ namespace FileManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BackgroundFileSystem BookDirectoryFiles { get; set; }
|
internal static BackgroundFileSystem BookDirectoryFiles { get; set; }
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region instance
|
#region instance
|
||||||
public FileType FileType => (FileType)Value;
|
public FileType FileType => (FileType)Value;
|
||||||
|
|
||||||
private IEnumerable<string> extensions_noDots { get; }
|
protected IEnumerable<string> extensions_noDots { get; }
|
||||||
private string extAggr { get; }
|
private string extAggr { get; }
|
||||||
|
|
||||||
protected AudibleFileStorage(FileType fileType) : base((int)fileType, fileType.ToString())
|
protected AudibleFileStorage(FileType fileType) : base((int)fileType, fileType.ToString())
|
||||||
@ -50,19 +50,6 @@ namespace FileManager
|
|||||||
BookDirectoryFiles ??= new BackgroundFileSystem(BooksDirectory, "*.*", SearchOption.AllDirectories);
|
BookDirectoryFiles ??= new BackgroundFileSystem(BooksDirectory, "*.*", SearchOption.AllDirectories);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Refresh()
|
|
||||||
{
|
|
||||||
BookDirectoryFiles.RefreshFiles();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Example for full books:
|
|
||||||
/// Search recursively in _books directory. Full book exists if either are true
|
|
||||||
/// - a directory name has the product id and an audio file is immediately inside
|
|
||||||
/// - any audio filename contains the product id
|
|
||||||
/// </summary>
|
|
||||||
public bool Exists(string productId) => GetPath(productId) != null;
|
|
||||||
|
|
||||||
public string GetPath(string productId)
|
public string GetPath(string productId)
|
||||||
{
|
{
|
||||||
var cachedFile = FilePathCache.GetPath(productId, FileType);
|
var cachedFile = FilePathCache.GetPath(productId, FileType);
|
||||||
@ -103,21 +90,6 @@ namespace FileManager
|
|||||||
FilePathCache.Upsert(productId, FileType, firstOrNull);
|
FilePathCache.Upsert(productId, FileType, firstOrNull);
|
||||||
return firstOrNull;
|
return firstOrNull;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetDestDir(string title, string asin)
|
|
||||||
{
|
|
||||||
// to prevent the paths from getting too long, we don't need after the 1st ":" for the folder
|
|
||||||
var underscoreIndex = title.IndexOf(':');
|
|
||||||
var titleDir
|
|
||||||
= underscoreIndex < 4
|
|
||||||
? title
|
|
||||||
: title.Substring(0, underscoreIndex);
|
|
||||||
var finalDir = FileUtility.GetValidFilename(StorageDirectory, titleDir, null, asin);
|
|
||||||
return finalDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsFileTypeMatch(FileInfo fileInfo)
|
|
||||||
=> extensions_noDots.ContainsInsensative(fileInfo.Extension.Trim('.'));
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,6 +106,8 @@ namespace FileManager
|
|||||||
|
|
||||||
public AudioFileStorage() : base(FileType.Audio) { }
|
public AudioFileStorage() : base(FileType.Audio) { }
|
||||||
|
|
||||||
|
public void Refresh() => BookDirectoryFiles.RefreshFiles();
|
||||||
|
|
||||||
public string CreateSkipFile(string title, string asin, string contents = null)
|
public string CreateSkipFile(string title, string asin, string contents = null)
|
||||||
{
|
{
|
||||||
var destinationDir = GetDestDir(title, asin);
|
var destinationDir = GetDestDir(title, asin);
|
||||||
@ -144,6 +118,21 @@ namespace FileManager
|
|||||||
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string GetDestDir(string title, string asin)
|
||||||
|
{
|
||||||
|
// to prevent the paths from getting too long, we don't need after the 1st ":" for the folder
|
||||||
|
var underscoreIndex = title.IndexOf(':');
|
||||||
|
var titleDir
|
||||||
|
= underscoreIndex < 4
|
||||||
|
? title
|
||||||
|
: title.Substring(0, underscoreIndex);
|
||||||
|
var finalDir = FileUtility.GetValidFilename(StorageDirectory, titleDir, null, asin);
|
||||||
|
return finalDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsFileTypeMatch(FileInfo fileInfo)
|
||||||
|
=> extensions_noDots.ContainsInsensative(fileInfo.Extension.Trim('.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AaxcFileStorage : AudibleFileStorage
|
public class AaxcFileStorage : AudibleFileStorage
|
||||||
@ -156,17 +145,13 @@ namespace FileManager
|
|||||||
public override string StorageDirectory => DownloadsInProgress;
|
public override string StorageDirectory => DownloadsInProgress;
|
||||||
|
|
||||||
public AaxcFileStorage() : base(FileType.AAXC) { }
|
public AaxcFileStorage() : base(FileType.AAXC) { }
|
||||||
}
|
|
||||||
|
|
||||||
public class PdfFileStorage : AudibleFileStorage
|
/// <summary>
|
||||||
{
|
/// Example for full books:
|
||||||
protected override string[] Extensions { get; } = new[] { "pdf", "zip" };
|
/// Search recursively in _books directory. Full book exists if either are true
|
||||||
|
/// - a directory name has the product id and an audio file is immediately inside
|
||||||
// we always want to use the latest config value, therefore
|
/// - any audio filename contains the product id
|
||||||
// - DO use 'get' arrow "=>"
|
/// </summary>
|
||||||
// - do NOT use assign "="
|
public bool Exists(string productId) => GetPath(productId) != null;
|
||||||
public override string StorageDirectory => BooksDirectory;
|
|
||||||
|
|
||||||
public PdfFileStorage() : base(FileType.PDF) { }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,14 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace FileManager
|
namespace FileManager
|
||||||
{
|
{
|
||||||
class BackgroundFileSystem
|
/// <summary>
|
||||||
|
/// Tracks actual locations of files. This is especially useful for clicking button to navigate to the book's files.
|
||||||
|
///
|
||||||
|
/// Note: this is no longer how Libation manages "Liberated" state. That is not statefully managed in the database.
|
||||||
|
/// This paradigm is what allows users to manually choose to not download books. Also allows them to manually toggle
|
||||||
|
/// this state and download again.
|
||||||
|
/// </summary>
|
||||||
|
internal class BackgroundFileSystem
|
||||||
{
|
{
|
||||||
public string RootDirectory { get; private set; }
|
public string RootDirectory { get; private set; }
|
||||||
public string SearchPattern { get; private set; }
|
public string SearchPattern { get; private set; }
|
||||||
|
|||||||
@ -9,6 +9,7 @@ namespace FileManager
|
|||||||
{
|
{
|
||||||
public static class FilePathCache
|
public static class FilePathCache
|
||||||
{
|
{
|
||||||
|
private const string FILENAME = "FileLocations.json";
|
||||||
internal class CacheEntry
|
internal class CacheEntry
|
||||||
{
|
{
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
@ -18,7 +19,7 @@ namespace FileManager
|
|||||||
|
|
||||||
private static Cache<CacheEntry> cache { get; } = new Cache<CacheEntry>();
|
private static Cache<CacheEntry> cache { get; } = new Cache<CacheEntry>();
|
||||||
|
|
||||||
private static string jsonFile => Path.Combine(Configuration.Instance.LibationFiles, "FilePaths.json");
|
private static string jsonFile => Path.Combine(Configuration.Instance.LibationFiles, FILENAME);
|
||||||
|
|
||||||
static FilePathCache()
|
static FilePathCache()
|
||||||
{
|
{
|
||||||
@ -84,7 +85,7 @@ namespace FileManager
|
|||||||
try { resave(); }
|
try { resave(); }
|
||||||
catch (IOException ex)
|
catch (IOException ex)
|
||||||
{
|
{
|
||||||
Serilog.Log.Logger.Error(ex, "Error saving FilePaths.json");
|
Serilog.Log.Logger.Error(ex, $"Error saving {FILENAME}");
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
<!-- <PublishSingleFile>true</PublishSingleFile> -->
|
<!-- <PublishSingleFile>true</PublishSingleFile> -->
|
||||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
|
|
||||||
<Version>5.5.1.5</Version>
|
<Version>5.5.1.11</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
|||||||
@ -52,7 +52,7 @@ namespace LibationLauncher
|
|||||||
|
|
||||||
migrate_to_v5_0_0(config);
|
migrate_to_v5_0_0(config);
|
||||||
migrate_to_v5_2_0__post_config(config);
|
migrate_to_v5_2_0__post_config(config);
|
||||||
//migrate_to_v5_4_1(config);// comment out until after vacation
|
migrate_to_v5_5_0(config);
|
||||||
|
|
||||||
ensureSerilogConfig(config);
|
ensureSerilogConfig(config);
|
||||||
configureLogging(config);
|
configureLogging(config);
|
||||||
@ -233,19 +233,11 @@ namespace LibationLauncher
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region migrate to v5.4.1 see comment
|
#region migrate to v5.5.0. FilePaths.json => db. long running. fire and forget
|
||||||
// this 'migration' is a bit different. it intentionally runs each time Libation is started. its job will be fulfilled when I eventually
|
private static void migrate_to_v5_5_0(Configuration config)
|
||||||
// implement the portion which removes FilePaths.json, at which time this method will be a proper migration
|
=> new System.Threading.Thread(() => migrate_to_v5_5_0_thread(config)) { IsBackground = true }.Start();
|
||||||
//
|
private static void migrate_to_v5_5_0_thread(Configuration config)
|
||||||
// I'm iterating through safe steps toward getting rid of the live scanner except to track audiobook files as a convenience
|
|
||||||
// such as clicking the stop light to open its location. live scanning will be replaced with state tracking in the database.
|
|
||||||
|
|
||||||
// FilePaths.json => db. long running. fire and forget
|
|
||||||
private static void migrate_to_v5_4_1(Configuration config)
|
|
||||||
=> new System.Threading.Thread(() => migrate_to_v5_4_1_thread(config)) { IsBackground = true }.Start();
|
|
||||||
private static void migrate_to_v5_4_1_thread(Configuration config)
|
|
||||||
{
|
{
|
||||||
var debugStopwatch = System.Diagnostics.Stopwatch.StartNew();
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var filePaths = Path.Combine(config.LibationFiles, "FilePaths.json");
|
var filePaths = Path.Combine(config.LibationFiles, "FilePaths.json");
|
||||||
@ -289,13 +281,13 @@ namespace LibationLauncher
|
|||||||
}
|
}
|
||||||
|
|
||||||
context.SaveChanges();
|
context.SaveChanges();
|
||||||
|
|
||||||
|
File.Delete(filePaths);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log.Logger.Error(ex, "Error attempting to insert FilePaths into db");
|
Log.Logger.Error(ex, "Error attempting to insert FilePaths into db");
|
||||||
}
|
}
|
||||||
debugStopwatch.Stop();
|
|
||||||
var debugTotal = debugStopwatch.Elapsed;
|
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|||||||
@ -139,9 +139,7 @@ namespace LibationSearchEngine
|
|||||||
return authors.Intersect(narrators).Any();
|
return authors.Intersect(narrators).Any();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool isLiberated(Book book)
|
private static bool isLiberated(Book book) => book.UserDefinedItem.BookStatus == LiberatedStatus.Liberated;
|
||||||
=> book.UserDefinedItem.BookStatus == LiberatedStatus.Liberated
|
|
||||||
|| AudibleFileStorage.Audio.Exists(book.AudibleProductId);
|
|
||||||
private static bool liberatedError(Book book) => book.UserDefinedItem.BookStatus == LiberatedStatus.Error;
|
private static bool liberatedError(Book book) => book.UserDefinedItem.BookStatus == LiberatedStatus.Error;
|
||||||
|
|
||||||
// use these common fields in the "all" default search field
|
// use these common fields in the "all" default search field
|
||||||
|
|||||||
@ -77,7 +77,7 @@ namespace LibationWinForms
|
|||||||
var libraryBook = liveGridEntry.LibraryBook;
|
var libraryBook = liveGridEntry.LibraryBook;
|
||||||
|
|
||||||
// liberated: open explorer to file
|
// liberated: open explorer to file
|
||||||
if (TransitionalFileLocator.Audio_Exists(libraryBook.Book))
|
if (libraryBook.Book.Audio_Exists)
|
||||||
{
|
{
|
||||||
var filePath = TransitionalFileLocator.Audio_GetPath(libraryBook.Book);
|
var filePath = TransitionalFileLocator.Audio_GetPath(libraryBook.Book);
|
||||||
if (!Go.To.File(filePath))
|
if (!Go.To.File(filePath))
|
||||||
|
|||||||
@ -67,7 +67,7 @@ alternate book id (eg BK_RAND_006061) is called 'sku' , 'sku_lite' , 'prod_id' ,
|
|||||||
-- begin SOLUTION LAYOUT ---------------------------------------------------------------------------------------------------------------------
|
-- begin SOLUTION LAYOUT ---------------------------------------------------------------------------------------------------------------------
|
||||||
do NOT combine jsons for
|
do NOT combine jsons for
|
||||||
- audible-scraped persistence: library, book details
|
- audible-scraped persistence: library, book details
|
||||||
- libation-generated persistence: FilePaths.json
|
- libation-generated persistence: FileLocations.json
|
||||||
- user-defined persistence: BookTags.json
|
- user-defined persistence: BookTags.json
|
||||||
-- end SOLUTION LAYOUT ---------------------------------------------------------------------------------------------------------------------
|
-- end SOLUTION LAYOUT ---------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user