Merge pull request #296 from Mbucari/master

Error handling inside all click handlers
This commit is contained in:
rmcrackan 2022-06-24 07:15:32 -04:00 committed by GitHub
commit 10bdddb262
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 357 additions and 101 deletions

View File

@ -88,5 +88,13 @@ namespace DataLayer
s.Series.AudibleSeriesId == parent.Book.AudibleProductId
) == true
).ToList();
public static IEnumerable<LibraryBook> UnLiberated(this IEnumerable<LibraryBook> bookList)
=> bookList
.Where(
lb =>
lb.Book.UserDefinedItem.BookStatus is LiberatedStatus.NotLiberated or LiberatedStatus.PartialDownload
|| lb.Book.UserDefinedItem.PdfStatus is LiberatedStatus.NotLiberated or LiberatedStatus.PartialDownload
);
}
}

View File

@ -165,7 +165,7 @@ namespace FileLiberator
LameConfig = GetLameOptions(config)
};
var chapters = getChapters(contentLic.ContentMetadata.ChapterInfo.Chapters).OrderBy(c => c.StartOffsetMs).ToList();
var chapters = flattenChapters(contentLic.ContentMetadata.ChapterInfo.Chapters).OrderBy(c => c.StartOffsetMs).ToList();
if (config.AllowLibationFixup || outputFormat == OutputFormat.Mp3)
{
@ -192,7 +192,7 @@ namespace FileLiberator
return dlOptions;
}
private List<AudibleApi.Common.Chapter> getChapters(IEnumerable<AudibleApi.Common.Chapter> chapters)
public static List<AudibleApi.Common.Chapter> flattenChapters(IEnumerable<AudibleApi.Common.Chapter> chapters, string titleConcat = ": ")
{
List<AudibleApi.Common.Chapter> chaps = new();
@ -200,20 +200,14 @@ namespace FileLiberator
{
if (c.Chapters is not null)
{
var firstSub = new AudibleApi.Common.Chapter
{
Title = $"{c.Title}: {c.Chapters[0].Title}",
StartOffsetMs = c.StartOffsetMs,
StartOffsetSec = c.StartOffsetSec,
LengthMs = c.LengthMs + c.Chapters[0].LengthMs
};
c.Chapters[0].StartOffsetMs = c.StartOffsetMs;
c.Chapters[0].StartOffsetSec = c.StartOffsetSec;
c.Chapters[0].LengthMs += c.LengthMs;
chaps.Add(firstSub);
var children = getChapters(c.Chapters[1..]);
var children = flattenChapters(c.Chapters);
foreach (var child in children)
child.Title = string.IsNullOrEmpty(c.Title) ? child.Title : $"{c.Title}: {child.Title}";
child.Title = $"{c.Title}{titleConcat}{child.Title}";
chaps.AddRange(children);
}

View File

@ -7,7 +7,7 @@ using System.Linq;
namespace FileManager
{
public class Replacement
public class Replacement : ICloneable
{
public const int FIXED_COUNT = 6;
@ -30,6 +30,8 @@ namespace FileManager
Mandatory = mandatory;
}
public object Clone() => new Replacement(CharacterToReplace, ReplacementString, Description, Mandatory);
public void Update(char charToReplace, string replacementString, string description)
{
ReplacementString = replacementString;
@ -53,6 +55,7 @@ namespace FileManager
public static Replacement OpenAngleBracket(string replacement) => new('<', replacement, "Open Angle Bracket");
public static Replacement CloseAngleBracket(string replacement) => new('>', replacement, "Close Angle Bracket");
public static Replacement Pipe(string replacement) => new('|', replacement, "Vertical Line");
}
[JsonConverter(typeof(ReplacementCharactersConverter))]

View File

@ -29,14 +29,14 @@
private void InitializeComponent()
{
this.dataGridView1 = new System.Windows.Forms.DataGridView();
this.charToReplaceCol = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.replacementStringCol = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.descriptionCol = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.defaultsBtn = new System.Windows.Forms.Button();
this.loFiDefaultsBtn = new System.Windows.Forms.Button();
this.saveBtn = new System.Windows.Forms.Button();
this.cancelBtn = new System.Windows.Forms.Button();
this.minDefaultBtn = new System.Windows.Forms.Button();
this.charToReplaceCol = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.replacementStringCol = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.descriptionCol = new System.Windows.Forms.DataGridViewTextBoxColumn();
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
this.SuspendLayout();
//
@ -54,6 +54,7 @@
this.descriptionCol});
this.dataGridView1.Location = new System.Drawing.Point(12, 12);
this.dataGridView1.Name = "dataGridView1";
this.dataGridView1.RowHeadersWidthSizeMode = System.Windows.Forms.DataGridViewRowHeadersWidthSizeMode.DisableResizing;
this.dataGridView1.RowTemplate.Height = 25;
this.dataGridView1.Size = new System.Drawing.Size(498, 393);
this.dataGridView1.TabIndex = 0;
@ -61,6 +62,31 @@
this.dataGridView1.UserDeletingRow += new System.Windows.Forms.DataGridViewRowCancelEventHandler(this.dataGridView1_UserDeletingRow);
this.dataGridView1.Resize += new System.EventHandler(this.dataGridView1_Resize);
//
// charToReplaceCol
//
this.charToReplaceCol.HeaderText = "Char to Replace";
this.charToReplaceCol.MinimumWidth = 70;
this.charToReplaceCol.Name = "charToReplaceCol";
this.charToReplaceCol.Resizable = System.Windows.Forms.DataGridViewTriState.False;
this.charToReplaceCol.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
this.charToReplaceCol.Width = 70;
//
// replacementStringCol
//
this.replacementStringCol.HeaderText = "Replacement Text";
this.replacementStringCol.MinimumWidth = 85;
this.replacementStringCol.Name = "replacementStringCol";
this.replacementStringCol.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
this.replacementStringCol.Width = 85;
//
// descriptionCol
//
this.descriptionCol.HeaderText = "Description";
this.descriptionCol.MinimumWidth = 100;
this.descriptionCol.Name = "descriptionCol";
this.descriptionCol.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
this.descriptionCol.Width = 200;
//
// defaultsBtn
//
this.defaultsBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
@ -116,28 +142,6 @@
this.minDefaultBtn.UseVisualStyleBackColor = true;
this.minDefaultBtn.Click += new System.EventHandler(this.minDefaultBtn_Click);
//
// charToReplaceCol
//
this.charToReplaceCol.HeaderText = "Char to Replace";
this.charToReplaceCol.MinimumWidth = 70;
this.charToReplaceCol.Name = "charToReplaceCol";
this.charToReplaceCol.Resizable = System.Windows.Forms.DataGridViewTriState.False;
this.charToReplaceCol.Width = 70;
//
// replacementStringCol
//
this.replacementStringCol.HeaderText = "Replacement Text";
this.replacementStringCol.MinimumWidth = 85;
this.replacementStringCol.Name = "replacementStringCol";
this.replacementStringCol.Width = 85;
//
// descriptionCol
//
this.descriptionCol.HeaderText = "Description";
this.descriptionCol.MinimumWidth = 100;
this.descriptionCol.Name = "descriptionCol";
this.descriptionCol.Width = 200;
//
// EditReplacementChars
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);

View File

@ -29,8 +29,8 @@ namespace LibationWinForms.Dialogs
{
var r = replacements[i];
int row = dataGridView1.Rows.Add(r.CharacterToReplace, r.ReplacementString, r.Description);
dataGridView1.Rows[row].Tag = r;
int row = dataGridView1.Rows.Add(r.CharacterToReplace.ToString(), r.ReplacementString, r.Description);
dataGridView1.Rows[row].Tag = r.Clone();
if (r.Mandatory)
@ -90,7 +90,7 @@ namespace LibationWinForms.Dialogs
{
dataGridView1.Rows[e.RowIndex].ErrorText = $"Only 1 {charToReplaceCol.HeaderText} per entry";
}
else if (e.RowIndex >= Replacement.FIXED_COUNT &&
else if (dataGridView1.Rows[e.RowIndex].Tag is Replacement repl && !repl.Mandatory &&
dataGridView1.Rows
.Cast<DataGridViewRow>()
.Where(r => r.Index != e.RowIndex)

View File

@ -1,4 +1,5 @@
using System;
using DataLayer;
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
@ -10,11 +11,24 @@ namespace LibationWinForms
private void Configure_Liberate() { }
//GetLibrary_Flat_NoTracking() may take a long time on a hugh library. so run in new thread
private async void beginBookBackupsToolStripMenuItem_Click(object _ = null, EventArgs __ = null)
private void beginBookBackupsToolStripMenuItem_Click(object _ = null, EventArgs __ = null)
{
SetQueueCollapseState(false);
await Task.Run(() => processBookQueue1.AddDownloadDecrypt(ApplicationServices.DbContexts.GetLibrary_Flat_NoTracking()
.Where(lb => lb.Book.UserDefinedItem.PdfStatus is DataLayer.LiberatedStatus.NotLiberated || lb.Book.UserDefinedItem.BookStatus is DataLayer.LiberatedStatus.NotLiberated)));
try
{
SetQueueCollapseState(false);
Serilog.Log.Logger.Information("Begin backing up all library books");
processBookQueue1.AddDownloadDecrypt(
ApplicationServices.DbContexts
.GetLibrary_Flat_NoTracking()
.UnLiberated()
);
}
catch (Exception ex)
{
Serilog.Log.Logger.Error(ex, "An error occurred while backing up all library books");
}
}
private async void beginPdfBackupsToolStripMenuItem_Click(object sender, EventArgs e)

View File

@ -22,27 +22,36 @@ namespace LibationWinForms
this.Width = width;
}
private void ProductsDisplay_LiberateClicked(object sender, LibraryBook e)
private void ProductsDisplay_LiberateClicked(object sender, LibraryBook libraryBook)
{
if (e.Book.UserDefinedItem.BookStatus != LiberatedStatus.Liberated)
try
{
SetQueueCollapseState(false);
processBookQueue1.AddDownloadDecrypt(e);
}
else if (e.Book.UserDefinedItem.PdfStatus is not null and LiberatedStatus.NotLiberated)
{
SetQueueCollapseState(false);
processBookQueue1.AddDownloadPdf(e);
}
else if (e.Book.Audio_Exists())
{
// liberated: open explorer to file
var filePath = AudibleFileStorage.Audio.GetPath(e.Book.AudibleProductId);
if (!Go.To.File(filePath?.ShortPathName))
if (libraryBook.Book.UserDefinedItem.BookStatus is LiberatedStatus.NotLiberated or LiberatedStatus.PartialDownload)
{
var suffix = string.IsNullOrWhiteSpace(filePath) ? "" : $":\r\n{filePath}";
MessageBox.Show($"File not found" + suffix);
Serilog.Log.Logger.Information("Begin single book backup of {libraryBook}", libraryBook);
SetQueueCollapseState(false);
processBookQueue1.AddDownloadDecrypt(libraryBook);
}
else if (libraryBook.Book.UserDefinedItem.PdfStatus is LiberatedStatus.NotLiberated)
{
Serilog.Log.Logger.Information("Begin single pdf backup of {libraryBook}", libraryBook);
SetQueueCollapseState(false);
processBookQueue1.AddDownloadPdf(libraryBook);
}
else if (libraryBook.Book.Audio_Exists())
{
// liberated: open explorer to file
var filePath = AudibleFileStorage.Audio.GetPath(libraryBook.Book.AudibleProductId);
if (!Go.To.File(filePath?.ShortPathName))
{
var suffix = string.IsNullOrWhiteSpace(filePath) ? "" : $":\r\n{filePath}";
MessageBox.Show($"File not found" + suffix);
}
}
}
catch (Exception ex)
{
Serilog.Log.Logger.Error(ex, "An error occurred while handling the stop light button click for {libraryBook}", libraryBook);
}
}

View File

@ -3,6 +3,7 @@ using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using ApplicationServices;
using DataLayer;
using Dinah.Core.Threading;
using LibationWinForms.Dialogs;
@ -48,10 +49,24 @@ namespace LibationWinForms
});
}
private async void liberateVisible(object sender, EventArgs e)
private void liberateVisible(object sender, EventArgs e)
{
SetQueueCollapseState(false);
await Task.Run(() => processBookQueue1.AddDownloadDecrypt(productsDisplay.GetVisible()));
try
{
SetQueueCollapseState(false);
Serilog.Log.Logger.Information("Begin backing up visible library books");
processBookQueue1.AddDownloadDecrypt(
productsDisplay
.GetVisible()
.UnLiberated()
);
}
catch (Exception ex)
{
Serilog.Log.Logger.Error(ex, "An error occurred while backing up visible library books");
}
}
private void replaceTagsToolStripMenuItem_Click(object sender, EventArgs e)

View File

@ -50,45 +50,52 @@ namespace LibationWinForms.GridView
#region Button controls
private void DataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
// handle grid button click: https://stackoverflow.com/a/13687844
if (e.RowIndex < 0)
return;
try
{
// handle grid button click: https://stackoverflow.com/a/13687844
if (e.RowIndex < 0)
return;
var entry = getGridEntry(e.RowIndex);
if (entry is LibraryBookEntry lbEntry)
{
if (e.ColumnIndex == liberateGVColumn.Index)
LiberateClicked?.Invoke(lbEntry);
else if (e.ColumnIndex == tagAndDetailsGVColumn.Index)
DetailsClicked?.Invoke(lbEntry);
else if (e.ColumnIndex == descriptionGVColumn.Index)
DescriptionClicked?.Invoke(lbEntry, gridEntryDataGridView.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false));
else if (e.ColumnIndex == coverGVColumn.Index)
CoverClicked?.Invoke(lbEntry);
}
else if (entry is SeriesEntry sEntry)
{
if (e.ColumnIndex == liberateGVColumn.Index)
var entry = getGridEntry(e.RowIndex);
if (entry is LibraryBookEntry lbEntry)
{
if (sEntry.Liberate.Expanded)
bindingList.CollapseItem(sEntry);
else
bindingList.ExpandItem(sEntry);
sEntry.NotifyPropertyChanged(nameof(sEntry.Liberate));
VisibleCountChanged?.Invoke(this, bindingList.BookEntries().Count());
if (e.ColumnIndex == liberateGVColumn.Index)
LiberateClicked?.Invoke(lbEntry);
else if (e.ColumnIndex == tagAndDetailsGVColumn.Index)
DetailsClicked?.Invoke(lbEntry);
else if (e.ColumnIndex == descriptionGVColumn.Index)
DescriptionClicked?.Invoke(lbEntry, gridEntryDataGridView.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false));
else if (e.ColumnIndex == coverGVColumn.Index)
CoverClicked?.Invoke(lbEntry);
}
else if (e.ColumnIndex == descriptionGVColumn.Index)
DescriptionClicked?.Invoke(sEntry, gridEntryDataGridView.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false));
else if (e.ColumnIndex == coverGVColumn.Index)
CoverClicked?.Invoke(sEntry);
}
else if (entry is SeriesEntry sEntry)
{
if (e.ColumnIndex == liberateGVColumn.Index)
{
if (sEntry.Liberate.Expanded)
bindingList.CollapseItem(sEntry);
else
bindingList.ExpandItem(sEntry);
if (e.ColumnIndex == removeGVColumn.Index)
sEntry.NotifyPropertyChanged(nameof(sEntry.Liberate));
VisibleCountChanged?.Invoke(this, bindingList.BookEntries().Count());
}
else if (e.ColumnIndex == descriptionGVColumn.Index)
DescriptionClicked?.Invoke(sEntry, gridEntryDataGridView.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false));
else if (e.ColumnIndex == coverGVColumn.Index)
CoverClicked?.Invoke(sEntry);
}
if (e.ColumnIndex == removeGVColumn.Index)
{
gridEntryDataGridView.CommitEdit(DataGridViewDataErrorContexts.Commit);
RemovableCountChanged?.Invoke(this, EventArgs.Empty);
}
}
catch(Exception ex)
{
gridEntryDataGridView.CommitEdit(DataGridViewDataErrorContexts.Commit);
RemovableCountChanged?.Invoke(this, EventArgs.Empty);
Serilog.Log.Logger.Error(ex, $"An error was encountered while processing a user click in the {nameof(ProductsGrid)}");
}
}

View File

@ -0,0 +1,202 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using AudibleApi.Common;
using Dinah.Core;
using FileManager;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace FileLiberator.Tests
{
[TestClass]
public class DownloadDecryptBookTests
{
[TestMethod]
public void HierarchicalChapters_Flatten()
{
var expected = new Chapter[]
{
new()
{
Title = "Book 1: Part 1: Chapter 1",
StartOffsetMs = 0,
StartOffsetSec = 0,
LengthMs = 100000,
},
new()
{
Title = "Book 1: Part 1: Chapter 2",
StartOffsetMs = 100000,
StartOffsetSec = 100,
LengthMs = 100000,
},
new()
{
Title = "Book 1: Part 2: Chapter 3",
StartOffsetMs = 200000,
StartOffsetSec = 200,
LengthMs = 100000,
},
new()
{
Title = "Book 1: Part 2: Chapter 4",
StartOffsetMs = 300000,
StartOffsetSec = 300,
LengthMs = 100000,
},
new()
{
Title = "Book 2: Part 3: Chapter 5",
StartOffsetMs = 400000,
StartOffsetSec = 400,
LengthMs = 100000,
},
new()
{
Title = "Book 2: Part 3: Chapter 6",
StartOffsetMs = 500000,
StartOffsetSec = 500,
LengthMs = 100000,
},
new()
{
Title = "Book 2: Part 4: Chapter 7",
StartOffsetMs = 600000,
StartOffsetSec = 600,
LengthMs = 100000,
},
new()
{
Title = "Book 2: Part 4: Chapter 8",
StartOffsetMs = 700000,
StartOffsetSec = 700,
LengthMs = 100000,
}
};
var hierarchicalChapters = new Chapter[]
{
new()
{
Title = "Book 1",
StartOffsetMs = 0,
StartOffsetSec = 0,
LengthMs = 2000,
Chapters = new Chapter[]
{
new()
{ Title = "Part 1",
StartOffsetMs = 2000,
StartOffsetSec = 2,
LengthMs = 2000,
Chapters = new Chapter[]
{
new()
{ Title = "Chapter 1",
StartOffsetMs = 4000,
StartOffsetSec = 4,
LengthMs = 96000,
},
new()
{ Title = "Chapter 2",
StartOffsetMs = 100000,
StartOffsetSec = 100,
LengthMs = 100000,
},
}
},
new()
{ Title = "Part 2",
StartOffsetMs = 200000,
StartOffsetSec = 200,
LengthMs = 2000,
Chapters = new Chapter[]
{
new()
{ Title = "Chapter 3",
StartOffsetMs = 202000,
StartOffsetSec = 202,
LengthMs = 98000,
},
new()
{ Title = "Chapter 4",
StartOffsetMs = 300000,
StartOffsetSec = 300,
LengthMs = 100000,
},
}
}
}
},
new()
{
Title = "Book 2",
StartOffsetMs = 400000,
StartOffsetSec = 400,
LengthMs = 2000,
Chapters = new Chapter[]
{
new()
{ Title = "Part 3",
StartOffsetMs = 402000,
StartOffsetSec = 402,
LengthMs = 2000,
Chapters = new Chapter[]
{
new()
{ Title = "Chapter 5",
StartOffsetMs = 404000,
StartOffsetSec = 404,
LengthMs = 96000,
},
new()
{ Title = "Chapter 6",
StartOffsetMs = 500000,
StartOffsetSec = 500,
LengthMs = 100000,
},
}
},
new()
{ Title = "Part 4",
StartOffsetMs = 600000,
StartOffsetSec = 600,
LengthMs = 2000,
Chapters = new Chapter[]
{
new()
{ Title = "Chapter 7",
StartOffsetMs = 602000,
StartOffsetSec = 602,
LengthMs = 98000,
},
new()
{ Title = "Chapter 8",
StartOffsetMs = 700000,
StartOffsetSec = 700,
LengthMs = 100000,
},
}
}
}
}
};
var flatChapters = DownloadDecryptBook.flattenChapters(hierarchicalChapters);
flatChapters.Count.Should().Be(expected.Length);
for (int i = 0; i < flatChapters.Count; i++)
{
flatChapters[i].Title.Should().Be(expected[i].Title);
flatChapters[i].StartOffsetMs.Should().Be(expected[i].StartOffsetMs);
flatChapters[i].StartOffsetSec.Should().Be(expected[i].StartOffsetSec);
flatChapters[i].LengthMs.Should().Be(expected[i].LengthMs);
flatChapters[i].Chapters.Should().BeNull();
}
}
}
}