Merge pull request #296 from Mbucari/master
Error handling inside all click handlers
This commit is contained in:
commit
10bdddb262
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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))]
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
202
Source/_Tests/FileLiberator.Tests/DownloadDecryptBookTests.cs
Normal file
202
Source/_Tests/FileLiberator.Tests/DownloadDecryptBookTests.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user