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 d67b5332..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 diff --git a/Source/LibationAvalonia/Controls/DataGridContextMenus.cs b/Source/LibationAvalonia/Controls/DataGridContextMenus.cs index 37044c51..0afcd02c 100644 --- a/Source/LibationAvalonia/Controls/DataGridContextMenus.cs +++ b/Source/LibationAvalonia/Controls/DataGridContextMenus.cs @@ -6,25 +6,6 @@ using System.Reflection; namespace LibationAvalonia.Controls { - public class DataGridCellContextMenuStripNeededEventArgs - { - private static readonly MethodInfo GetCellValueMethod; - static DataGridCellContextMenuStripNeededEventArgs() - { - GetCellValueMethod = typeof(DataGridColumn).GetMethod("GetCellValue", BindingFlags.NonPublic | BindingFlags.Instance); - } - - private static string GetCellValue(DataGridColumn column, object item) - => GetCellValueMethod.Invoke(column, new object[] { item, column.ClipboardContentBinding })?.ToString() ?? ""; - - public string CellClipboardContents => GetCellValue(Column, GridEntry); - public DataGridColumn Column { get; init; } - public GridEntry GridEntry { get; init; } - public ContextMenu ContextMenu { get; init; } - public AvaloniaList ContextMenuItems - => ContextMenu.Items as AvaloniaList; - } - internal static class DataGridContextMenus { public static event EventHandler CellContextMenuStripNeeded; @@ -40,7 +21,7 @@ namespace LibationAvalonia.Controls public static void AttachContextMenuToCell(this DataGridCell cell) { - if (cell.ContextMenu is null) + if (cell is not null && cell.ContextMenu is null) { cell.ContextRequested += Cell_ContextRequested; cell.ContextMenu = ContextMenu; @@ -68,4 +49,23 @@ namespace LibationAvalonia.Controls e.Handled = true; } } + + public class DataGridCellContextMenuStripNeededEventArgs + { + private static readonly MethodInfo GetCellValueMethod; + static DataGridCellContextMenuStripNeededEventArgs() + { + GetCellValueMethod = typeof(DataGridColumn).GetMethod("GetCellValue", BindingFlags.NonPublic | BindingFlags.Instance); + } + + private static string GetCellValue(DataGridColumn column, object item) + => GetCellValueMethod.Invoke(column, new object[] { item, column.ClipboardContentBinding })?.ToString() ?? ""; + + public string CellClipboardContents => GetCellValue(Column, GridEntry); + public DataGridColumn Column { get; init; } + public GridEntry GridEntry { get; init; } + public ContextMenu ContextMenu { get; init; } + public AvaloniaList ContextMenuItems + => ContextMenu.Items as AvaloniaList; + } } diff --git a/Source/LibationAvalonia/Program.cs b/Source/LibationAvalonia/Program.cs index 2718b8b3..d820eb28 100644 --- a/Source/LibationAvalonia/Program.cs +++ b/Source/LibationAvalonia/Program.cs @@ -13,8 +13,6 @@ namespace LibationAvalonia { static class Program { - private static string EXE_DIR = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); - static void Main() { //***********************************************// diff --git a/Source/LibationAvalonia/Views/MainWindow.axaml.cs b/Source/LibationAvalonia/Views/MainWindow.axaml.cs index d91bb4a8..6d4e5a86 100644 --- a/Source/LibationAvalonia/Views/MainWindow.axaml.cs +++ b/Source/LibationAvalonia/Views/MainWindow.axaml.cs @@ -20,7 +20,7 @@ namespace LibationAvalonia.Views { public event EventHandler Load; public event EventHandler> LibraryLoaded; - private MainWindowViewModel _viewModel; + private readonly MainWindowViewModel _viewModel; public MainWindow() { @@ -135,11 +135,9 @@ namespace LibationAvalonia.Views try { System.Net.Http.HttpClient cli = new(); - using (var fs = System.IO.File.OpenWrite(zipFile)) - { - using (var dlStream = await cli.GetStreamAsync(new Uri(upgradeProperties.ZipUrl))) - await dlStream.CopyToAsync(fs); - } + using var fs = System.IO.File.OpenWrite(zipFile); + using var dlStream = await cli.GetStreamAsync(new Uri(upgradeProperties.ZipUrl)); + await dlStream.CopyToAsync(fs); } catch (Exception ex) { diff --git a/Source/LibationWinForms/GridView/MyRatingGridViewColumn.cs b/Source/LibationWinForms/GridView/MyRatingGridViewColumn.cs index 2c360ec1..56893822 100644 --- a/Source/LibationWinForms/GridView/MyRatingGridViewColumn.cs +++ b/Source/LibationWinForms/GridView/MyRatingGridViewColumn.cs @@ -43,7 +43,7 @@ namespace LibationWinForms.GridView protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts) { if (value is Rating rating) - { + { ToolTipText = "Click to change ratings"; var starString = rating.ToStarString(); @@ -53,6 +53,9 @@ namespace LibationWinForms.GridView base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, string.Empty, string.Empty, errorText, cellStyle, advancedBorderStyle, paintParts); } + protected override object GetFormattedValue(object value, int rowIndex, ref DataGridViewCellStyle cellStyle, TypeConverter valueTypeConverter, TypeConverter formattedValueTypeConverter, DataGridViewDataErrorContexts context) + => value is Rating rating ? rating.ToStarString() : value?.ToString(); + public override object ParseFormattedValue(object formattedValue, DataGridViewCellStyle cellStyle, TypeConverter formattedValueTypeConverter, TypeConverter valueTypeConverter) => formattedValue; } diff --git a/Source/LibationWinForms/GridView/ProductsGrid.cs b/Source/LibationWinForms/GridView/ProductsGrid.cs index 0ac93377..78d0d260 100644 --- a/Source/LibationWinForms/GridView/ProductsGrid.cs +++ b/Source/LibationWinForms/GridView/ProductsGrid.cs @@ -120,7 +120,7 @@ namespace LibationWinForms.GridView try { var dgv = (DataGridView)sender; - var text = dgv[e.ColumnIndex, e.RowIndex].Value.ToString(); + var text = dgv[e.ColumnIndex, e.RowIndex].FormattedValue.ToString(); InteropFactory.Create().CopyTextToClipboard(text); } catch { } diff --git a/Source/LoadByOS/WindowsConfigApp/Form1.Designer.cs b/Source/LoadByOS/WindowsConfigApp/Form1.Designer.cs deleted file mode 100644 index 30118c1b..00000000 --- a/Source/LoadByOS/WindowsConfigApp/Form1.Designer.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace WindowsConfigApp -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Text = "Form1"; - } - - #endregion - } -} \ No newline at end of file diff --git a/Source/LoadByOS/WindowsConfigApp/Form1.cs b/Source/LoadByOS/WindowsConfigApp/Form1.cs deleted file mode 100644 index d3265e4e..00000000 --- a/Source/LoadByOS/WindowsConfigApp/Form1.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace WindowsConfigApp -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - } -} \ No newline at end of file diff --git a/Source/LoadByOS/WindowsConfigApp/Program.cs b/Source/LoadByOS/WindowsConfigApp/Program.cs index 93f5d8c6..1786ce7b 100644 --- a/Source/LoadByOS/WindowsConfigApp/Program.cs +++ b/Source/LoadByOS/WindowsConfigApp/Program.cs @@ -7,7 +7,6 @@ namespace WindowsConfigApp public override Type InteropFunctionsType => typeof(WinInterop); public override Type[] ReferencedTypes => new Type[] { - typeof(Form1), typeof(Bitmap), typeof(Dinah.Core.WindowsDesktop.GitClient), typeof(Accessibility.IAccIdentity),