mirror of
https://github.com/rmcrackan/Libation.git
synced 2025-12-31 09:58:43 -05:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a72c3f069b | ||
|
|
1fcacb9cfb | ||
|
|
a3542c53e2 | ||
|
|
9e44a95ba2 | ||
|
|
204e77008b |
@@ -11,6 +11,13 @@ using Serilog;
|
||||
|
||||
namespace ApplicationServices
|
||||
{
|
||||
// subtly different from DataLayer.LiberatedStatus
|
||||
// - DataLayer.LiberatedStatus: has no concept of partially downloaded
|
||||
// - ApplicationServices.LiberatedState: has no concept of Error/skipped
|
||||
public enum LiberatedState { NotDownloaded, PartialDownload, Liberated }
|
||||
|
||||
public enum PdfState { NoPdf, Downloaded, NotDownloaded }
|
||||
|
||||
public static class LibraryCommands
|
||||
{
|
||||
#region FULL LIBRARY scan and import
|
||||
@@ -112,6 +119,9 @@ namespace ApplicationServices
|
||||
|
||||
var udi = book.UserDefinedItem;
|
||||
|
||||
if (udi.Tags == newTags)
|
||||
return 0;
|
||||
|
||||
// Attach() NoTracking entities before SaveChanges()
|
||||
udi.Tags = newTags;
|
||||
context.Attach(udi).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
|
||||
@@ -137,6 +147,9 @@ namespace ApplicationServices
|
||||
|
||||
var udi = libraryBook.Book.UserDefinedItem;
|
||||
|
||||
if (udi.BookStatus == liberatedStatus && udi.BookLocation == finalAudioPath)
|
||||
return 0;
|
||||
|
||||
// Attach() NoTracking entities before SaveChanges()
|
||||
udi.BookStatus = liberatedStatus;
|
||||
udi.BookLocation = finalAudioPath;
|
||||
@@ -162,6 +175,9 @@ namespace ApplicationServices
|
||||
|
||||
var udi = libraryBook.Book.UserDefinedItem;
|
||||
|
||||
if (udi.PdfStatus == liberatedStatus)
|
||||
return 0;
|
||||
|
||||
// Attach() NoTracking entities before SaveChanges()
|
||||
udi.PdfStatus = liberatedStatus;
|
||||
context.Attach(udi).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
|
||||
@@ -177,12 +193,18 @@ namespace ApplicationServices
|
||||
}
|
||||
#endregion
|
||||
|
||||
// this is a query, not command so maybe I should make a LibraryQueries. except there's already one of those...
|
||||
private enum AudioFileState { full, aax, none }
|
||||
private static AudioFileState getAudioFileState(string productId)
|
||||
=> TransitionalFileLocator.Audio_Exists(productId) ? AudioFileState.full
|
||||
: TransitionalFileLocator.AAXC_Exists(productId) ? AudioFileState.aax
|
||||
: AudioFileState.none;
|
||||
// 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)
|
||||
=> TransitionalFileLocator.Audio_Exists(book) ? LiberatedState.Liberated
|
||||
: TransitionalFileLocator.AAXC_Exists(book) ? LiberatedState.PartialDownload
|
||||
: LiberatedState.NotDownloaded;
|
||||
|
||||
public static PdfState Pdf_Status(Book book)
|
||||
=> !book.Supplements.Any() ? PdfState.NoPdf
|
||||
: TransitionalFileLocator.PDF_Exists(book) ? PdfState.Downloaded
|
||||
: PdfState.NotDownloaded;
|
||||
|
||||
public record LibraryStats(int booksFullyBackedUp, int booksDownloadedOnly, int booksNoProgress, int pdfsDownloaded, int pdfsNotDownloaded) { }
|
||||
public static LibraryStats GetCounts()
|
||||
{
|
||||
@@ -190,21 +212,21 @@ namespace ApplicationServices
|
||||
|
||||
var results = libraryBooks
|
||||
.AsParallel()
|
||||
.Select(lb => getAudioFileState(lb.Book.AudibleProductId))
|
||||
.Select(lb => Liberated_Status(lb.Book))
|
||||
.ToList();
|
||||
var booksFullyBackedUp = results.Count(r => r == AudioFileState.full);
|
||||
var booksDownloadedOnly = results.Count(r => r == AudioFileState.aax);
|
||||
var booksNoProgress = results.Count(r => r == AudioFileState.none);
|
||||
var booksFullyBackedUp = results.Count(r => r == LiberatedState.Liberated);
|
||||
var booksDownloadedOnly = results.Count(r => r == LiberatedState.PartialDownload);
|
||||
var booksNoProgress = results.Count(r => r == LiberatedState.NotDownloaded);
|
||||
|
||||
Log.Logger.Information("Book counts. {@DebugInfo}", new { total = results.Count, booksFullyBackedUp, booksDownloadedOnly, booksNoProgress });
|
||||
|
||||
var boolResults = libraryBooks
|
||||
.AsParallel()
|
||||
.Where(lb => lb.Book.Supplements.Any())
|
||||
.Select(lb => TransitionalFileLocator.PDF_Exists(lb.Book.AudibleProductId))
|
||||
.Select(lb => Pdf_Status(lb.Book))
|
||||
.ToList();
|
||||
var pdfsDownloaded = boolResults.Count(r => r);
|
||||
var pdfsNotDownloaded = boolResults.Count(r => !r);
|
||||
var pdfsDownloaded = boolResults.Count(r => r == PdfState.Downloaded);
|
||||
var pdfsNotDownloaded = boolResults.Count(r => r == PdfState.NotDownloaded);
|
||||
|
||||
Log.Logger.Information("PDF counts. {@DebugInfo}", new { total = boolResults.Count, pdfsDownloaded, pdfsNotDownloaded });
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ namespace ApplicationServices
|
||||
{
|
||||
public static class SearchEngineCommands
|
||||
{
|
||||
public static void FullReIndex()
|
||||
public static void FullReIndex(SearchEngine engine = null)
|
||||
{
|
||||
var engine = new SearchEngine(DbContexts.GetContext());
|
||||
engine.CreateNewIndex();
|
||||
engine ??= new SearchEngine();
|
||||
engine.CreateNewIndex(DbContexts.GetContext());
|
||||
}
|
||||
|
||||
public static SearchResultSet Search(string searchString) => performSearchEngineFunc_safe(e =>
|
||||
@@ -27,28 +27,28 @@ namespace ApplicationServices
|
||||
|
||||
private static void performSearchEngineAction_safe(Action<SearchEngine> action)
|
||||
{
|
||||
var engine = new SearchEngine(DbContexts.GetContext());
|
||||
var engine = new SearchEngine();
|
||||
try
|
||||
{
|
||||
action(engine);
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
FullReIndex();
|
||||
FullReIndex(engine);
|
||||
action(engine);
|
||||
}
|
||||
}
|
||||
|
||||
private static T performSearchEngineFunc_safe<T>(Func<SearchEngine, T> action)
|
||||
{
|
||||
var engine = new SearchEngine(DbContexts.GetContext());
|
||||
var engine = new SearchEngine();
|
||||
try
|
||||
{
|
||||
return action(engine);
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
FullReIndex();
|
||||
FullReIndex(engine);
|
||||
return action(engine);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,41 +9,38 @@ namespace ApplicationServices
|
||||
{
|
||||
public static class TransitionalFileLocator
|
||||
{
|
||||
public static string Audio_GetPath(string productId)
|
||||
public static string Audio_GetPath(Book book)
|
||||
{
|
||||
var book = DbContexts.GetContext().GetBook_Flat_NoTracking(productId);
|
||||
var loc = book?.UserDefinedItem?.BookLocation ?? "";
|
||||
if (File.Exists(loc))
|
||||
return loc;
|
||||
|
||||
return AudibleFileStorage.Audio.GetPath(productId);
|
||||
return AudibleFileStorage.Audio.GetPath(book.AudibleProductId);
|
||||
}
|
||||
|
||||
public static bool PDF_Exists(string productId)
|
||||
public static bool PDF_Exists(Book book)
|
||||
{
|
||||
var book = DbContexts.GetContext().GetBook_Flat_NoTracking(productId);
|
||||
var status = book?.UserDefinedItem?.PdfStatus;
|
||||
if (status.HasValue && status.Value == LiberatedStatus.Liberated)
|
||||
return true;
|
||||
|
||||
return AudibleFileStorage.PDF.Exists(productId);
|
||||
return AudibleFileStorage.PDF.Exists(book.AudibleProductId);
|
||||
}
|
||||
|
||||
public static bool Audio_Exists(string productId)
|
||||
public static bool Audio_Exists(Book book)
|
||||
{
|
||||
var book = DbContexts.GetContext().GetBook_Flat_NoTracking(productId);
|
||||
var status = book?.UserDefinedItem?.BookStatus;
|
||||
// true since Error == libhack
|
||||
if (status != LiberatedStatus.NotLiberated)
|
||||
if (status.HasValue && status.Value != LiberatedStatus.NotLiberated)
|
||||
return true;
|
||||
|
||||
return AudibleFileStorage.Audio.Exists(productId);
|
||||
return AudibleFileStorage.Audio.Exists(book.AudibleProductId);
|
||||
}
|
||||
|
||||
public static bool AAXC_Exists(string productId)
|
||||
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(productId);
|
||||
return AudibleFileStorage.AAXC.Exists(book.AudibleProductId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace DataLayer
|
||||
{
|
||||
// only library importing should use tracking. All else should be NoTracking.
|
||||
// only library importing should directly query Book. All else should use LibraryBook
|
||||
public static class BookQueries
|
||||
{
|
||||
public static Book GetBook_Flat_NoTracking(this LibationContext context, string productId)
|
||||
|
||||
@@ -4,6 +4,8 @@ using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace DataLayer
|
||||
{
|
||||
// only library importing should use tracking. All else should be NoTracking.
|
||||
// only library importing should directly query Book. All else should use LibraryBook
|
||||
public static class LibraryQueries
|
||||
{
|
||||
//// tracking is a bad idea for main grid. it prevents anything else from updating entities unless getting them from the grid
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace FileLiberator
|
||||
public DownloadPdf DownloadPdf { get; } = new DownloadPdf();
|
||||
|
||||
public bool Validate(LibraryBook libraryBook)
|
||||
=> !ApplicationServices.TransitionalFileLocator.Audio_Exists(libraryBook.Book.AudibleProductId);
|
||||
=> !ApplicationServices.TransitionalFileLocator.Audio_Exists(libraryBook.Book);
|
||||
|
||||
// do NOT use ConfigureAwait(false) on ProcessAsync()
|
||||
// often calls events which prints to forms in the UI context
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace FileLiberator
|
||||
|
||||
public bool Validate(LibraryBook libraryBook)
|
||||
{
|
||||
var path = ApplicationServices.TransitionalFileLocator.Audio_GetPath(libraryBook.Book.AudibleProductId);
|
||||
var path = ApplicationServices.TransitionalFileLocator.Audio_GetPath(libraryBook.Book);
|
||||
return path?.ToLower()?.EndsWith(".m4b") == true && !File.Exists(Mp3FileName(path));
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace FileLiberator
|
||||
|
||||
try
|
||||
{
|
||||
var m4bPath = ApplicationServices.TransitionalFileLocator.Audio_GetPath(libraryBook.Book.AudibleProductId);
|
||||
var m4bPath = ApplicationServices.TransitionalFileLocator.Audio_GetPath(libraryBook.Book);
|
||||
|
||||
m4bBook = new Mp4File(m4bPath, FileAccess.Read);
|
||||
m4bBook.ConversionProgressUpdate += M4bBook_ConversionProgressUpdate;
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace FileLiberator
|
||||
|
||||
try
|
||||
{
|
||||
if (ApplicationServices.TransitionalFileLocator.Audio_Exists(libraryBook.Book.AudibleProductId))
|
||||
if (ApplicationServices.TransitionalFileLocator.Audio_Exists(libraryBook.Book))
|
||||
return new StatusHandler { "Cannot find decrypt. Final audio file already exists" };
|
||||
|
||||
var outputAudioFilename = await aaxToM4bConverterDecryptAsync(AudibleFileStorage.DownloadsInProgress, AudibleFileStorage.DecryptInProgress, libraryBook);
|
||||
@@ -46,7 +46,7 @@ namespace FileLiberator
|
||||
// moves files and returns dest dir
|
||||
_ = moveFilesToBooksDir(libraryBook.Book, outputAudioFilename);
|
||||
|
||||
var finalAudioExists = ApplicationServices.TransitionalFileLocator.Audio_Exists(libraryBook.Book.AudibleProductId);
|
||||
var finalAudioExists = ApplicationServices.TransitionalFileLocator.Audio_Exists(libraryBook.Book);
|
||||
if (!finalAudioExists)
|
||||
return new StatusHandler { "Cannot find final audio file after decryption" };
|
||||
|
||||
@@ -219,7 +219,7 @@ namespace FileLiberator
|
||||
}
|
||||
|
||||
public bool Validate(LibraryBook libraryBook)
|
||||
=> !ApplicationServices.TransitionalFileLocator.Audio_Exists(libraryBook.Book.AudibleProductId);
|
||||
=> !ApplicationServices.TransitionalFileLocator.Audio_Exists(libraryBook.Book);
|
||||
|
||||
public void Cancel()
|
||||
{
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace FileLiberator
|
||||
{
|
||||
public override bool Validate(LibraryBook libraryBook)
|
||||
=> !string.IsNullOrWhiteSpace(getdownloadUrl(libraryBook))
|
||||
&& !ApplicationServices.TransitionalFileLocator.PDF_Exists(libraryBook.Book.AudibleProductId);
|
||||
&& !ApplicationServices.TransitionalFileLocator.PDF_Exists(libraryBook.Book);
|
||||
|
||||
public override async Task<StatusHandler> ProcessItemAsync(LibraryBook libraryBook)
|
||||
{
|
||||
@@ -32,7 +32,7 @@ namespace FileLiberator
|
||||
private static string getProposedDownloadFilePath(LibraryBook libraryBook)
|
||||
{
|
||||
// if audio file exists, get it's dir. else return base Book dir
|
||||
var existingPath = Path.GetDirectoryName(ApplicationServices.TransitionalFileLocator.Audio_GetPath(libraryBook.Book.AudibleProductId));
|
||||
var existingPath = Path.GetDirectoryName(ApplicationServices.TransitionalFileLocator.Audio_GetPath(libraryBook.Book));
|
||||
var file = getdownloadUrl(libraryBook);
|
||||
|
||||
if (existingPath != null)
|
||||
@@ -61,7 +61,7 @@ namespace FileLiberator
|
||||
}
|
||||
|
||||
private static StatusHandler verifyDownload(LibraryBook libraryBook)
|
||||
=> !ApplicationServices.TransitionalFileLocator.PDF_Exists(libraryBook.Book.AudibleProductId)
|
||||
=> !ApplicationServices.TransitionalFileLocator.PDF_Exists(libraryBook.Book)
|
||||
? new StatusHandler { "Downloaded PDF cannot be found" }
|
||||
: new StatusHandler();
|
||||
}
|
||||
|
||||
@@ -23,13 +23,6 @@ namespace FileLiberator
|
||||
.GetLibrary_Flat_NoTracking()
|
||||
.Where(libraryBook => processable.Validate(libraryBook));
|
||||
|
||||
public static LibraryBook GetSingleLibraryBook(string productId)
|
||||
{
|
||||
using var context = DbContexts.GetContext();
|
||||
var libraryBook = context.GetLibraryBook_Flat_NoTracking(productId);
|
||||
return libraryBook;
|
||||
}
|
||||
|
||||
public static async Task<StatusHandler> ProcessSingleAsync(this IProcessable processable, LibraryBook libraryBook)
|
||||
{
|
||||
if (!processable.Validate(libraryBook))
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<!-- <PublishSingleFile>true</PublishSingleFile> -->
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
|
||||
<Version>5.4.4.1</Version>
|
||||
<Version>5.4.8.1</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -361,7 +361,7 @@ namespace LibationLauncher
|
||||
config.ConfigureLogging();
|
||||
|
||||
// Fwd Console to serilog.
|
||||
// Serilog also write to Console (should probably change this) so it might be asking for trouble.
|
||||
// Serilog also writes to Console (should probably change this) so it might be asking for trouble.
|
||||
// SerilogTextWriter needs to be more robust and tested. Esp the Write() methods.
|
||||
// Empirical testing so far has shown no issues.
|
||||
Console.SetOut(new MultiTextWriter(origOut, new SerilogTextWriter()));
|
||||
@@ -378,6 +378,11 @@ namespace LibationLauncher
|
||||
Log.Logger.Information("Begin Libation. {@DebugInfo}", new
|
||||
{
|
||||
Version = BuildVersion.ToString(),
|
||||
#if DEBUG
|
||||
Mode = "Debug",
|
||||
#else
|
||||
Mode = "Release",
|
||||
#endif
|
||||
|
||||
LogLevel_Verbose_Enabled = Log.Logger.IsVerboseEnabled(),
|
||||
LogLevel_Debug_Enabled = Log.Logger.IsDebugEnabled(),
|
||||
|
||||
@@ -18,8 +18,6 @@ namespace LibationSearchEngine
|
||||
{
|
||||
public const Lucene.Net.Util.Version Version = Lucene.Net.Util.Version.LUCENE_30;
|
||||
|
||||
private LibationContext context { get; }
|
||||
|
||||
// not customizable. don't move to config
|
||||
private static string SearchEngineDirectory { get; }
|
||||
= new System.IO.DirectoryInfo(Configuration.Instance.LibationFiles).CreateSubdirectory("SearchEngine").FullName;
|
||||
@@ -32,8 +30,6 @@ namespace LibationSearchEngine
|
||||
// the workaround which allows displaying all books when query is empty
|
||||
public const string ALL_QUERY = "*:*";
|
||||
|
||||
public SearchEngine(LibationContext context) => this.context = context;
|
||||
|
||||
#region index rules
|
||||
private static ReadOnlyDictionary<string, Func<LibraryBook, string>> idIndexRules { get; }
|
||||
= new ReadOnlyDictionary<string, Func<LibraryBook, string>>(
|
||||
@@ -202,7 +198,7 @@ namespace LibationSearchEngine
|
||||
/// create new. ie: full re-index
|
||||
/// </summary>
|
||||
/// <param name="overwrite"></param>
|
||||
public void CreateNewIndex(bool overwrite = true)
|
||||
public void CreateNewIndex(LibationContext context, bool overwrite = true)
|
||||
{
|
||||
// 300 titles: 200- 400 ms
|
||||
// 1021 titles: 1777-2250 ms
|
||||
@@ -235,7 +231,7 @@ namespace LibationSearchEngine
|
||||
}
|
||||
|
||||
/// <summary>Long running. Use await Task.Run(() => UpdateBook(productId))</summary>
|
||||
public void UpdateBook(string productId)
|
||||
public void UpdateBook(LibationContext context, string productId)
|
||||
{
|
||||
var libraryBook = context.GetLibraryBook_Flat_NoTracking(productId);
|
||||
var term = new Term(_ID_, productId);
|
||||
|
||||
@@ -51,15 +51,14 @@ namespace LibationWinForms.BookLiberation
|
||||
|
||||
public static class ProcessorAutomationController
|
||||
{
|
||||
public static async Task BackupSingleBookAsync(string productId, EventHandler<LibraryBook> completedAction = null)
|
||||
public static async Task BackupSingleBookAsync(LibraryBook libraryBook, EventHandler<LibraryBook> completedAction = null)
|
||||
{
|
||||
Serilog.Log.Logger.Information("Begin " + nameof(BackupSingleBookAsync) + " {@DebugInfo}", new { productId });
|
||||
Serilog.Log.Logger.Information("Begin backup single {@DebugInfo}", new { libraryBook?.Book?.AudibleProductId });
|
||||
|
||||
var backupBook = getWiredUpBackupBook(completedAction);
|
||||
|
||||
(Action unsubscribeEvents, LogMe logMe) = attachToBackupsForm(backupBook);
|
||||
|
||||
var libraryBook = IProcessableExt.GetSingleLibraryBook(productId);
|
||||
// continue even if libraryBook is null. we'll display even that in the processing box
|
||||
await new BackupSingle(logMe, backupBook, libraryBook).RunBackupAsync();
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace LibationWinForms.Dialogs
|
||||
{
|
||||
partial class EditTagsDialog
|
||||
partial class BookDetailsDialog
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
@@ -3,15 +3,15 @@ using System.Windows.Forms;
|
||||
|
||||
namespace LibationWinForms.Dialogs
|
||||
{
|
||||
public partial class EditTagsDialog : Form
|
||||
public partial class BookDetailsDialog : Form
|
||||
{
|
||||
public string NewTags { get; private set; }
|
||||
|
||||
public EditTagsDialog()
|
||||
public BookDetailsDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
public EditTagsDialog(string title, string rawTags) : this()
|
||||
public BookDetailsDialog(string title, string rawTags) : this()
|
||||
{
|
||||
this.Text = $"Edit Tags - {title}";
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using ApplicationServices;
|
||||
using DataLayer;
|
||||
|
||||
namespace LibationWinForms
|
||||
@@ -13,29 +14,24 @@ namespace LibationWinForms
|
||||
private Book book => libraryBook.Book;
|
||||
|
||||
public Book GetBook() => book;
|
||||
public LibraryBook GetLibraryBook() => libraryBook;
|
||||
|
||||
public GridEntry(LibraryBook libraryBook) => this.libraryBook = libraryBook;
|
||||
|
||||
// hide from public fields from Data Source GUI with [Browsable(false)]
|
||||
|
||||
[Browsable(false)]
|
||||
public string AudibleProductId => book.AudibleProductId;
|
||||
[Browsable(false)]
|
||||
public string Tags => book.UserDefinedItem.Tags;
|
||||
[Browsable(false)]
|
||||
public IEnumerable<string> TagsEnumerated => book.UserDefinedItem.TagsEnumerated;
|
||||
|
||||
public enum LiberatedState { NotDownloaded, PartialDownload, Liberated }
|
||||
[Browsable(false)]
|
||||
public LiberatedState Liberated_Status
|
||||
=> ApplicationServices.TransitionalFileLocator.Audio_Exists(book.AudibleProductId) ? LiberatedState.Liberated
|
||||
: ApplicationServices.TransitionalFileLocator.AAXC_Exists(book.AudibleProductId) ? LiberatedState.PartialDownload
|
||||
: LiberatedState.NotDownloaded;
|
||||
|
||||
public enum PdfState { NoPdf, Downloaded, NotDownloaded }
|
||||
public string PictureId => book.PictureId;
|
||||
[Browsable(false)]
|
||||
public PdfState Pdf_Status
|
||||
=> !book.Supplements.Any() ? PdfState.NoPdf
|
||||
: ApplicationServices.TransitionalFileLocator.PDF_Exists(book.AudibleProductId) ? PdfState.Downloaded
|
||||
: PdfState.NotDownloaded;
|
||||
public LiberatedState Liberated_Status => LibraryCommands.Liberated_Status(book);
|
||||
[Browsable(false)]
|
||||
public PdfState Pdf_Status => LibraryCommands.Pdf_Status(book);
|
||||
|
||||
// displayValues is what gets displayed
|
||||
// the value that gets returned from the property is the cell's value
|
||||
|
||||
@@ -39,15 +39,26 @@ namespace LibationWinForms
|
||||
public ProductsGrid()
|
||||
{
|
||||
InitializeComponent();
|
||||
formatDataGridView();
|
||||
addLiberateButtons();
|
||||
addEditTagsButtons();
|
||||
formatColumns();
|
||||
formatDataGridView();
|
||||
addLiberateButtons();
|
||||
addEditTagsButtons();
|
||||
formatColumns();
|
||||
|
||||
manageLiveImageUpdateSubscriptions();
|
||||
|
||||
enableDoubleBuffering();
|
||||
}
|
||||
|
||||
private void formatDataGridView()
|
||||
private void enableDoubleBuffering()
|
||||
{
|
||||
var propertyInfo = dataGridView.GetType().GetProperty("DoubleBuffered", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
|
||||
|
||||
//var before = (bool)propertyInfo.GetValue(dataGridView);
|
||||
propertyInfo.SetValue(dataGridView, true, null);
|
||||
//var after = (bool)propertyInfo.GetValue(dataGridView);
|
||||
}
|
||||
|
||||
private void formatDataGridView()
|
||||
{
|
||||
dataGridView.Dock = DockStyle.Fill;
|
||||
dataGridView.AllowUserToAddRows = false;
|
||||
@@ -123,25 +134,25 @@ namespace LibationWinForms
|
||||
{
|
||||
var libState = liberatedStatus switch
|
||||
{
|
||||
GridEntry.LiberatedState.Liberated => "Liberated",
|
||||
GridEntry.LiberatedState.PartialDownload => "File has been at least\r\npartially downloaded",
|
||||
GridEntry.LiberatedState.NotDownloaded => "Book NOT downloaded",
|
||||
LiberatedState.Liberated => "Liberated",
|
||||
LiberatedState.PartialDownload => "File has been at least\r\npartially downloaded",
|
||||
LiberatedState.NotDownloaded => "Book NOT downloaded",
|
||||
_ => throw new Exception("Unexpected liberation state")
|
||||
};
|
||||
|
||||
var pdfState = pdfStatus switch
|
||||
{
|
||||
GridEntry.PdfState.Downloaded => "\r\nPDF downloaded",
|
||||
GridEntry.PdfState.NotDownloaded => "\r\nPDF NOT downloaded",
|
||||
GridEntry.PdfState.NoPdf => "",
|
||||
PdfState.Downloaded => "\r\nPDF downloaded",
|
||||
PdfState.NotDownloaded => "\r\nPDF NOT downloaded",
|
||||
PdfState.NoPdf => "",
|
||||
_ => throw new Exception("Unexpected PDF state")
|
||||
};
|
||||
|
||||
var text = libState + pdfState;
|
||||
|
||||
if (liberatedStatus == GridEntry.LiberatedState.NotDownloaded ||
|
||||
liberatedStatus == GridEntry.LiberatedState.PartialDownload ||
|
||||
pdfStatus == GridEntry.PdfState.NotDownloaded)
|
||||
if (liberatedStatus == LiberatedState.NotDownloaded ||
|
||||
liberatedStatus == LiberatedState.PartialDownload ||
|
||||
pdfStatus == PdfState.NotDownloaded)
|
||||
text += "\r\nClick to complete";
|
||||
|
||||
//DEBUG//cell.Value = text;
|
||||
@@ -151,14 +162,14 @@ namespace LibationWinForms
|
||||
// draw img
|
||||
{
|
||||
var image_lib
|
||||
= liberatedStatus == GridEntry.LiberatedState.NotDownloaded ? "red"
|
||||
: liberatedStatus == GridEntry.LiberatedState.PartialDownload ? "yellow"
|
||||
: liberatedStatus == GridEntry.LiberatedState.Liberated ? "green"
|
||||
= liberatedStatus == LiberatedState.NotDownloaded ? "red"
|
||||
: liberatedStatus == LiberatedState.PartialDownload ? "yellow"
|
||||
: liberatedStatus == LiberatedState.Liberated ? "green"
|
||||
: throw new Exception("Unexpected liberation state");
|
||||
var image_pdf
|
||||
= pdfStatus == GridEntry.PdfState.NoPdf ? ""
|
||||
: pdfStatus == GridEntry.PdfState.NotDownloaded ? "_pdf_no"
|
||||
: pdfStatus == GridEntry.PdfState.Downloaded ? "_pdf_yes"
|
||||
= pdfStatus == PdfState.NoPdf ? ""
|
||||
: pdfStatus == PdfState.NotDownloaded ? "_pdf_no"
|
||||
: pdfStatus == PdfState.Downloaded ? "_pdf_yes"
|
||||
: throw new Exception("Unexpected PDF state");
|
||||
var image = (Bitmap)Properties.Resources.ResourceManager.GetObject($"liberate_{image_lib}{image_pdf}");
|
||||
drawImage(e, image);
|
||||
@@ -169,25 +180,26 @@ namespace LibationWinForms
|
||||
{
|
||||
if (!isColumnValid(e, LIBERATE))
|
||||
return;
|
||||
|
||||
var productId = getGridEntry(e.RowIndex).GetBook().AudibleProductId;
|
||||
|
||||
var libraryBook = getGridEntry(e.RowIndex).GetLibraryBook();
|
||||
|
||||
// liberated: open explorer to file
|
||||
if (TransitionalFileLocator.Audio_Exists(productId))
|
||||
if (TransitionalFileLocator.Audio_Exists(libraryBook.Book))
|
||||
{
|
||||
var filePath = TransitionalFileLocator.Audio_GetPath(productId);
|
||||
var filePath = TransitionalFileLocator.Audio_GetPath(libraryBook.Book);
|
||||
if (!Go.To.File(filePath))
|
||||
MessageBox.Show($"File not found:\r\n{filePath}");
|
||||
return;
|
||||
}
|
||||
|
||||
await BookLiberation.ProcessorAutomationController.BackupSingleBookAsync(productId, (_, __) => RefreshRow(productId));
|
||||
// else: liberate
|
||||
await BookLiberation.ProcessorAutomationController.BackupSingleBookAsync(libraryBook, (_, __) => RefreshRow(libraryBook.Book.AudibleProductId));
|
||||
}
|
||||
#endregion
|
||||
|
||||
public void RefreshRow(string productId)
|
||||
{
|
||||
var rowId = getRowId((ge) => ge.GetBook().AudibleProductId == productId);
|
||||
var rowId = getRowId((ge) => ge.AudibleProductId == productId);
|
||||
|
||||
// update cells incl Liberate button text
|
||||
dataGridView.InvalidateRow(rowId);
|
||||
@@ -243,11 +255,11 @@ namespace LibationWinForms
|
||||
// EditTagsDialog should display better-formatted title
|
||||
liveGridEntry.TryDisplayValue(nameof(liveGridEntry.Title), out string value);
|
||||
|
||||
var editTagsForm = new EditTagsDialog(value, liveGridEntry.Tags);
|
||||
if (editTagsForm.ShowDialog() != DialogResult.OK)
|
||||
var bookDetailsForm = new BookDetailsDialog(value, liveGridEntry.Tags);
|
||||
if (bookDetailsForm.ShowDialog() != DialogResult.OK)
|
||||
return;
|
||||
|
||||
var qtyChanges = LibraryCommands.UpdateTags(liveGridEntry.GetBook(), editTagsForm.NewTags);
|
||||
var qtyChanges = LibraryCommands.UpdateTags(liveGridEntry.GetBook(), bookDetailsForm.NewTags);
|
||||
if (qtyChanges == 0)
|
||||
return;
|
||||
|
||||
@@ -318,7 +330,7 @@ namespace LibationWinForms
|
||||
=> dataGridView.UIThread(() => updateRowImage(pictureId));
|
||||
private void updateRowImage(string pictureId)
|
||||
{
|
||||
var rowId = getRowId((ge) => ge.GetBook().PictureId == pictureId);
|
||||
var rowId = getRowId((ge) => ge.PictureId == pictureId);
|
||||
if (rowId > -1)
|
||||
dataGridView.InvalidateRow(rowId);
|
||||
}
|
||||
@@ -386,7 +398,7 @@ namespace LibationWinForms
|
||||
currencyManager.SuspendBinding();
|
||||
{
|
||||
for (var r = dataGridView.RowCount - 1; r >= 0; r--)
|
||||
dataGridView.Rows[r].Visible = productIds.Contains(getGridEntry(r).GetBook().AudibleProductId);
|
||||
dataGridView.Rows[r].Visible = productIds.Contains(getGridEntry(r).AudibleProductId);
|
||||
}
|
||||
currencyManager.ResumeBinding();
|
||||
VisibleCountChanged?.Invoke(this, dataGridView.AsEnumerable().Count(r => r.Visible));
|
||||
|
||||
Reference in New Issue
Block a user