diff --git a/Source/AaxDecrypter/AaxDecrypter.csproj b/Source/AaxDecrypter/AaxDecrypter.csproj
index 7a885fb2..5724667b 100644
--- a/Source/AaxDecrypter/AaxDecrypter.csproj
+++ b/Source/AaxDecrypter/AaxDecrypter.csproj
@@ -13,7 +13,7 @@
-
+
diff --git a/Source/AaxDecrypter/AaxcDownloadConvertBase.cs b/Source/AaxDecrypter/AaxcDownloadConvertBase.cs
index 10f2cc37..dc4eeb66 100644
--- a/Source/AaxDecrypter/AaxcDownloadConvertBase.cs
+++ b/Source/AaxDecrypter/AaxcDownloadConvertBase.cs
@@ -32,6 +32,9 @@ namespace AaxDecrypter
AaxFile.AppleTags.Album = AaxFile.AppleTags.Album?.Replace(" (Unabridged)", "");
}
+ if (DownloadOptions.FixupFile)
+ AaxFile.AppleTags.AppleListBox.EditOrAddTag("TCOM", AaxFile.AppleTags.Narrator);
+
//Finishing configuring lame encoder.
if (DownloadOptions.OutputFormat == OutputFormat.Mp3)
MpegUtil.ConfigureLameOptions(
diff --git a/Source/AppScaffolding/LibationScaffolding.cs b/Source/AppScaffolding/LibationScaffolding.cs
index 2ab51557..05d5a400 100644
--- a/Source/AppScaffolding/LibationScaffolding.cs
+++ b/Source/AppScaffolding/LibationScaffolding.cs
@@ -81,7 +81,6 @@ namespace AppScaffolding
//
Migrations.migrate_to_v6_6_9(config);
- Migrations.migrate_from_7_10_1(config);
}
public static void PopulateMissingConfigValues(Configuration config)
@@ -457,74 +456,5 @@ namespace AppScaffolding
UNSAFE_MigrationHelper.Settings_AddUniqueToArray("Serilog.Enrich", "WithExceptionDetails");
}
}
-
- public static void migrate_from_7_10_1(Configuration config)
- {
- var lastMigrationThrew = config.GetNonString($"{nameof(migrate_from_7_10_1)}_ThrewError");
-
- if (lastMigrationThrew) return;
-
- try
- {
-
- //https://github.com/rmcrackan/Libation/issues/270#issuecomment-1152863629
- //This migration helps fix databases contaminated with the 7.10.1 hack workaround
- //and those with improperly identified or missing series. This does not solve cases
- //where individual episodes are in the db with a valid series link, but said series'
- //parents have not been imported into the database. For those cases, Libation will
- //attempt fixup by retrieving parents from the catalog endpoint
-
- using var context = DbContexts.GetContext();
-
- //This migration removes books and series with SERIES_ prefix that were created
- //as a hack workaround in 7.10.1. Said workaround was removed in 7.10.2
- string removeHackSeries = "delete " +
- "from series " +
- "where AudibleSeriesId like 'SERIES%'";
-
- string removeHackBooks = "delete " +
- "from books " +
- "where AudibleProductId like 'SERIES%'";
-
- //Detect series parents that were added to the database as books with ContentType.Episode,
- //and change them to ContentType.Parent
- string updateContentType =
- "UPDATE books " +
- "SET contenttype = 4 " +
- "WHERE audibleproductid IN (SELECT books.audibleproductid " +
- "FROM books " +
- "INNER JOIN series " +
- "ON ( books.audibleproductid = " +
- "series.audibleseriesid) " +
- "WHERE books.contenttype = 2)";
-
- //Then detect series parents that were added to the database as books with ContentType.Parent
- //but are missing a series link, and add the link (don't know how this happened)
- string addMissingSeriesLink =
- "INSERT INTO seriesbook " +
- "SELECT series.seriesid, " +
- "books.bookid, " +
- "'- 1' " +
- "FROM books " +
- "LEFT OUTER JOIN seriesbook " +
- "ON books.bookid = seriesbook.bookid " +
- "INNER JOIN series " +
- "ON books.audibleproductid = series.audibleseriesid " +
- "WHERE books.contenttype = 4 " +
- "AND seriesbook.seriesid IS NULL";
-
- context.Database.ExecuteSqlRaw(removeHackSeries);
- context.Database.ExecuteSqlRaw(removeHackBooks);
- context.Database.ExecuteSqlRaw(updateContentType);
- context.Database.ExecuteSqlRaw(addMissingSeriesLink);
-
- LibraryCommands.SaveContext(context);
- }
- catch (Exception ex)
- {
- Serilog.Log.Logger.Error(ex, "An error occurred while running database migrations in {0}", nameof(migrate_from_7_10_1));
- config.SetObject($"{nameof(migrate_from_7_10_1)}_ThrewError", true);
- }
- }
}
}
diff --git a/Source/ApplicationServices/LibraryCommands.cs b/Source/ApplicationServices/LibraryCommands.cs
index d49c15aa..a051fa84 100644
--- a/Source/ApplicationServices/LibraryCommands.cs
+++ b/Source/ApplicationServices/LibraryCommands.cs
@@ -126,22 +126,6 @@ namespace ApplicationServices
if (totalCount == 0)
return default;
-
- Log.Logger.Information("Begin scan for orphaned episode parents");
- var newParents = await findAndAddMissingParents(accounts);
- Log.Logger.Information($"Orphan episode scan complete. New parents count {newParents}");
-
- if (newParents >= 0)
- {
- //If any episodes are still orphaned, their series have been
- //removed from the catalog and we'll never be able to find them.
-
- //only do this if findAndAddMissingParents returned >= 0. If it
- //returned < 0, an error happened and there's still a chance that
- //a future successful run will find missing parents.
- removedOrphanedEpisodes();
- }
-
Log.Logger.Information("Begin long-running import");
logTime($"pre {nameof(importIntoDbAsync)}");
var newCount = await importIntoDbAsync(importItems);
@@ -235,84 +219,6 @@ namespace ApplicationServices
return newCount;
}
- static void removedOrphanedEpisodes()
- {
- using var context = DbContexts.GetContext();
- try
- {
- var orphanedEpisodes =
- context
- .GetLibrary_Flat_NoTracking(includeParents: true)
- .FindOrphanedEpisodes();
-
- context.LibraryBooks.RemoveRange(orphanedEpisodes);
- context.Books.RemoveRange(orphanedEpisodes.Select(lb => lb.Book));
-
- }
- catch (Exception ex)
- {
- Serilog.Log.Logger.Error(ex, "An error occurred while trying to remove orphaned episodes from the database");
- }
- }
-
- static async Task findAndAddMissingParents(Account[] accounts)
- {
- using var context = DbContexts.GetContext();
-
- var library = context.GetLibrary_Flat_NoTracking(includeParents: true);
-
- try
- {
- var orphanedEpisodes = library.FindOrphanedEpisodes().ToList();
-
- if (!orphanedEpisodes.Any())
- return -1;
-
- var orphanedSeries =
- orphanedEpisodes
- .SelectMany(lb => lb.Book.SeriesLink)
- .DistinctBy(s => s.Series.AudibleSeriesId)
- .ToList();
-
- // The Catalog endpoint does not require authentication.
- var api = new ApiUnauthenticated(accounts[0].Locale);
-
- var seriesParents = orphanedSeries.Select(o => o.Series.AudibleSeriesId).ToList();
- var items = await api.GetCatalogProductsAsync(seriesParents, CatalogOptions.ResponseGroupOptions.ALL_OPTIONS);
-
- List newParentsImportItems = new();
- foreach (var sp in orphanedSeries)
- {
- var seriesItem = items.First(i => i.Asin == sp.Series.AudibleSeriesId);
-
- if (seriesItem.Relationships is null)
- continue;
-
- var episode = orphanedEpisodes.First(l => l.Book.AudibleProductId == sp.Book.AudibleProductId);
-
- seriesItem.PurchaseDate = new DateTimeOffset(episode.DateAdded);
- seriesItem.Series = new AudibleApi.Common.Series[]
- {
- new AudibleApi.Common.Series{ Asin = seriesItem.Asin, Title = seriesItem.TitleWithSubtitle, Sequence = "-1"}
- };
-
- newParentsImportItems.Add(new ImportItem { DtoItem = seriesItem, AccountId = episode.Account, LocaleName = episode.Book.Locale });
- }
-
- var newCount = new LibraryBookImporter(context)
- .Import(newParentsImportItems);
-
- await context.SaveChangesAsync();
-
- return newCount;
- }
- catch (Exception ex)
- {
- Serilog.Log.Logger.Error(ex, "An error occurred while trying to scan for orphaned episode parents.");
- return -1;
- }
- }
-
public static int SaveContext(LibationContext context)
{
try
@@ -415,14 +321,16 @@ namespace ApplicationServices
this Book book,
string tags = null,
LiberatedStatus? bookStatus = null,
- LiberatedStatus? pdfStatus = null)
- => new[] { book }.UpdateUserDefinedItem(tags, bookStatus, pdfStatus);
+ LiberatedStatus? pdfStatus = null,
+ Rating rating = null)
+ => new[] { book }.UpdateUserDefinedItem(tags, bookStatus, pdfStatus, rating);
public static int UpdateUserDefinedItem(
this IEnumerable books,
string tags = null,
LiberatedStatus? bookStatus = null,
- LiberatedStatus? pdfStatus = null)
+ LiberatedStatus? pdfStatus = null,
+ Rating rating = null)
=> updateUserDefinedItem(
books,
udi => {
@@ -435,6 +343,9 @@ namespace ApplicationServices
// method handles null logic
udi.SetPdfStatus(pdfStatus);
+
+ if (rating is not null)
+ udi.UpdateRating(rating.OverallRating, rating.PerformanceRating, rating.StoryRating);
});
public static int UpdateBookStatus(this Book book, LiberatedStatus bookStatus)
@@ -487,7 +398,10 @@ namespace ApplicationServices
// Attach() NoTracking entities before SaveChanges()
foreach (var book in books)
+ {
context.Attach(book.UserDefinedItem).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
+ context.Attach(book.UserDefinedItem.Rating).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
+ }
var qtyChanges = context.SaveChanges();
if (qtyChanges > 0)
diff --git a/Source/ApplicationServices/SearchEngineCommands.cs b/Source/ApplicationServices/SearchEngineCommands.cs
index efc21c7f..b87326f8 100644
--- a/Source/ApplicationServices/SearchEngineCommands.cs
+++ b/Source/ApplicationServices/SearchEngineCommands.cs
@@ -43,23 +43,18 @@ namespace ApplicationServices
else
{
foreach (var book in books)
- {
- UpdateLiberatedStatus(book);
- UpdateBookTags(book);
- }
+ UpdateUserDefinedItems(book);
}
}
- public static void FullReIndex() => performSafeCommand(e =>
- fullReIndex(e)
- );
+ public static void FullReIndex() => performSafeCommand(fullReIndex);
- internal static void UpdateLiberatedStatus(Book book) => performSafeCommand(e =>
- e.UpdateLiberatedStatus(book)
- );
-
- internal static void UpdateBookTags(Book book) => performSafeCommand(e =>
- e.UpdateTags(book.AudibleProductId, book.UserDefinedItem.Tags)
+ internal static void UpdateUserDefinedItems(Book book) => performSafeCommand(e =>
+ {
+ e.UpdateLiberatedStatus(book);
+ e.UpdateTags(book.AudibleProductId, book.UserDefinedItem.Tags);
+ e.UpdateUserRatings(book);
+ }
);
private static void performSafeCommand(Action action)
@@ -87,7 +82,6 @@ namespace ApplicationServices
isUpdating = true;
action(new SearchEngine());
-
if (!prevIsUpdating)
SearchEngineUpdated?.Invoke(null, null);
}
diff --git a/Source/LibationAvalonia/Controls/DataGridCheckBoxColumnExt.axaml b/Source/LibationAvalonia/Controls/DataGridCheckBoxColumnExt.axaml
deleted file mode 100644
index d51e730d..00000000
--- a/Source/LibationAvalonia/Controls/DataGridCheckBoxColumnExt.axaml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
diff --git a/Source/LibationAvalonia/Controls/DataGridCheckBoxColumnExt.axaml.cs b/Source/LibationAvalonia/Controls/DataGridCheckBoxColumnExt.cs
similarity index 79%
rename from Source/LibationAvalonia/Controls/DataGridCheckBoxColumnExt.axaml.cs
rename to Source/LibationAvalonia/Controls/DataGridCheckBoxColumnExt.cs
index 2c686498..0bf914c9 100644
--- a/Source/LibationAvalonia/Controls/DataGridCheckBoxColumnExt.axaml.cs
+++ b/Source/LibationAvalonia/Controls/DataGridCheckBoxColumnExt.cs
@@ -1,10 +1,11 @@
-using Avalonia.Controls;
+using Avalonia.Controls;
using LibationAvalonia.ViewModels;
using System;
+using System.Linq;
namespace LibationAvalonia.Controls
{
- public partial class DataGridCheckBoxColumnExt : DataGridCheckBoxColumn
+ public class DataGridCheckBoxColumnExt : DataGridCheckBoxColumn
{
protected override IControl GenerateEditingElementDirect(DataGridCell cell, object dataItem)
{
diff --git a/Source/LibationAvalonia/Controls/DataGridTemplateColumnExt.axaml.cs b/Source/LibationAvalonia/Controls/DataGridContextMenus.cs
similarity index 65%
rename from Source/LibationAvalonia/Controls/DataGridTemplateColumnExt.axaml.cs
rename to Source/LibationAvalonia/Controls/DataGridContextMenus.cs
index b46fac89..4ec6d825 100644
--- a/Source/LibationAvalonia/Controls/DataGridTemplateColumnExt.axaml.cs
+++ b/Source/LibationAvalonia/Controls/DataGridContextMenus.cs
@@ -1,12 +1,55 @@
-using Avalonia.Collections;
+using Avalonia.Collections;
using Avalonia.Controls;
-using Avalonia.Markup.Xaml;
using LibationAvalonia.ViewModels;
using System;
using System.Reflection;
namespace LibationAvalonia.Controls
-{
+{
+ internal static class DataGridContextMenus
+ {
+ public static event EventHandler CellContextMenuStripNeeded;
+ private static readonly ContextMenu ContextMenu = new();
+ private static readonly AvaloniaList