Added a poc marker for where a bookmark resides.
Added a marker where a highlight might reside too. Can move forward with a proper implementation.
This commit is contained in:
parent
e5d949161e
commit
32ee60e1de
14 changed files with 257 additions and 76 deletions
|
@ -57,7 +57,7 @@ public interface IBookService
|
|||
/// <param name="targetDirectory">Where the files will be extracted to. If doesn't exist, will be created.</param>
|
||||
void ExtractPdfImages(string fileFilePath, string targetDirectory);
|
||||
Task<ICollection<BookChapterItem>> GenerateTableOfContents(Chapter chapter);
|
||||
Task<string> GetBookPage(int page, int chapterId, string cachedEpubPath, string baseUrl);
|
||||
Task<string> GetBookPage(int page, int chapterId, string cachedEpubPath, string baseUrl, List<PersonalToCDto> ptocBookmarks);
|
||||
Task<Dictionary<string, int>> CreateKeyToPageMappingAsync(EpubBookRef book);
|
||||
}
|
||||
|
||||
|
@ -321,6 +321,71 @@ public class BookService : IBookService
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For each bookmark on this page, inject a specialized icon
|
||||
/// </summary>
|
||||
/// <param name="doc"></param>
|
||||
/// <param name="book"></param>
|
||||
/// <param name="ptocBookmarks"></param>
|
||||
private static void InjectPTOCBookmarks(HtmlDocument doc, EpubBookRef book, List<PersonalToCDto> ptocBookmarks)
|
||||
{
|
||||
if (ptocBookmarks.Count == 0) return;
|
||||
|
||||
foreach (var bookmark in ptocBookmarks.Where(b => !string.IsNullOrEmpty(b.BookScrollId)))
|
||||
{
|
||||
var unscopedSelector = bookmark.BookScrollId.Replace("//BODY/APP-ROOT[1]/DIV[1]/DIV[1]/DIV[1]/APP-BOOK-READER[1]/DIV[1]/DIV[2]/DIV[1]/DIV[1]/DIV[1]", "//BODY").ToLowerInvariant();
|
||||
var elem = doc.DocumentNode.SelectSingleNode(unscopedSelector);
|
||||
elem?.PrependChild(HtmlNode.CreateNode($"<i class='fa-solid fa-bookmark ps-1 pe-1' role='button' id='ptoc-{bookmark.Id}' title='{bookmark.Title}'></i>"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void InjectHighlights(HtmlDocument doc, EpubBookRef book, List<PersonalToCDto> ptocBookmarks)
|
||||
{
|
||||
if (ptocBookmarks.Count == 0) return;
|
||||
|
||||
foreach (var bookmark in ptocBookmarks.Where(b => !string.IsNullOrEmpty(b.BookScrollId)))
|
||||
{
|
||||
var unscopedSelector = bookmark.BookScrollId.Replace("//BODY/APP-ROOT[1]/DIV[1]/DIV[1]/DIV[1]/APP-BOOK-READER[1]/DIV[1]/DIV[2]/DIV[1]/DIV[1]/DIV[1]", "//BODY").ToLowerInvariant();
|
||||
var elem = doc.DocumentNode.SelectSingleNode(unscopedSelector);
|
||||
if (elem == null) continue;
|
||||
|
||||
// For this POC, assume we have 16 characters highlighted and those characters are: "For the past few"
|
||||
// Get the original text content
|
||||
var originalText = elem.InnerText;
|
||||
|
||||
// For POC: highlight first 16 characters
|
||||
const int highlightLength = 16;
|
||||
|
||||
if (originalText.Length > highlightLength)
|
||||
{
|
||||
var highlightedText = originalText.Substring(0, highlightLength);
|
||||
var remainingText = originalText.Substring(highlightLength);
|
||||
|
||||
// Clear the existing content
|
||||
elem.RemoveAllChildren();
|
||||
|
||||
// Create the highlight element with the first 16 characters
|
||||
var highlightNode = HtmlNode.CreateNode($"<app-epub-highlight>{highlightedText}</app-epub-highlight>");
|
||||
elem.AppendChild(highlightNode);
|
||||
|
||||
// Add the remaining text as a text node
|
||||
var remainingTextNode = HtmlNode.CreateNode(remainingText);
|
||||
elem.AppendChild(remainingTextNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If text is shorter than highlight length, wrap it all
|
||||
var highlightNode = HtmlNode.CreateNode($"<app-epub-highlight>{originalText}</app-epub-highlight>");
|
||||
elem.RemoveAllChildren();
|
||||
elem.AppendChild(highlightNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private static void ScopeImages(HtmlDocument doc, EpubBookRef book, string apiBase)
|
||||
{
|
||||
var images = doc.DocumentNode.SelectNodes("//img")
|
||||
|
@ -1016,8 +1081,9 @@ public class BookService : IBookService
|
|||
/// <param name="body">Body element from the epub</param>
|
||||
/// <param name="mappings">Epub mappings</param>
|
||||
/// <param name="page">Page number we are loading</param>
|
||||
/// <param name="ptocBookmarks">Ptoc Bookmarks to tie against</param>
|
||||
/// <returns></returns>
|
||||
private async Task<string> ScopePage(HtmlDocument doc, EpubBookRef book, string apiBase, HtmlNode body, Dictionary<string, int> mappings, int page)
|
||||
private async Task<string> ScopePage(HtmlDocument doc, EpubBookRef book, string apiBase, HtmlNode body, Dictionary<string, int> mappings, int page, List<PersonalToCDto> ptocBookmarks)
|
||||
{
|
||||
await InlineStyles(doc, book, apiBase, body);
|
||||
|
||||
|
@ -1025,6 +1091,13 @@ public class BookService : IBookService
|
|||
|
||||
ScopeImages(doc, book, apiBase);
|
||||
|
||||
// Inject PTOC Bookmark Icons
|
||||
InjectPTOCBookmarks(doc, book, ptocBookmarks);
|
||||
|
||||
|
||||
// MOCK: This will mimic a highlight
|
||||
InjectHighlights(doc, book, ptocBookmarks);
|
||||
|
||||
return PrepareFinalHtml(doc, body);
|
||||
}
|
||||
|
||||
|
@ -1215,7 +1288,7 @@ public class BookService : IBookService
|
|||
/// <param name="baseUrl">The API base for Kavita, to rewrite urls to so we load though our endpoint</param>
|
||||
/// <returns>Full epub HTML Page, scoped to Kavita's reader</returns>
|
||||
/// <exception cref="KavitaException">All exceptions throw this</exception>
|
||||
public async Task<string> GetBookPage(int page, int chapterId, string cachedEpubPath, string baseUrl)
|
||||
public async Task<string> GetBookPage(int page, int chapterId, string cachedEpubPath, string baseUrl, List<PersonalToCDto> ptocBookmarks)
|
||||
{
|
||||
using var book = await EpubReader.OpenBookAsync(cachedEpubPath, LenientBookReaderOptions);
|
||||
var mappings = await CreateKeyToPageMappingAsync(book);
|
||||
|
@ -1257,7 +1330,7 @@ public class BookService : IBookService
|
|||
body = doc.DocumentNode.SelectSingleNode("/html/body");
|
||||
}
|
||||
|
||||
return await ScopePage(doc, book, apiBase, body, mappings, page);
|
||||
return await ScopePage(doc, book, apiBase, body!, mappings, page, ptocBookmarks);
|
||||
}
|
||||
} catch (Exception ex)
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue