General fixes (#102)
* Improved PDF embedded annotation hit detection logic * Updated EPUB and PDF reader logic to improve position tracking and TTS stability. Key changes: - Refined CFI and locator handling in `EpubReaderScreen` with improved initialization. - Updated `BookPaginator` to more accurately calculate text offsets in chunks and improve CFI matching logic. - Modified `TtsPlaybackManager` to insert media items at the correct index, ensuring proper playback order. - Simplified `EpubReaderTts` page scrolling by removing conditional checks for backward jumps. - Disabled OCR fallback in `PdfViewerScreen` during text extraction for TTS. * Updated extractTextWithCfiFromTop to use getBoundingClientRect for identifying the starting block
This commit is contained in:
parent
ddcd253c7b
commit
fbe8e7aa56
9 changed files with 95 additions and 184 deletions
|
|
@ -352,15 +352,23 @@ class BookPaginator(
|
|||
if (blockText.isNotBlank()) {
|
||||
val textChunksInBlock = splitTextIntoChunks(blockText)
|
||||
|
||||
var currentOffsetInBlock = 0
|
||||
var currentSearchIndex = 0
|
||||
textChunksInBlock.forEach { chunkText ->
|
||||
val firstWord = chunkText.trim().substringBefore(' ')
|
||||
val relativeOffset = if (firstWord.isNotEmpty()) {
|
||||
val idx = blockText.indexOf(firstWord, currentSearchIndex)
|
||||
if (idx != -1) idx else currentSearchIndex
|
||||
} else {
|
||||
currentSearchIndex
|
||||
}
|
||||
|
||||
val chunk = TtsChunk(
|
||||
text = chunkText,
|
||||
sourceCfi = block.cfi!!,
|
||||
startOffsetInSource = block.startCharOffsetInSource + currentOffsetInBlock
|
||||
startOffsetInSource = block.startCharOffsetInSource + relativeOffset
|
||||
)
|
||||
allTtsChunks.add(chunk)
|
||||
currentOffsetInBlock += chunkText.length
|
||||
currentSearchIndex = relativeOffset + chunkText.length
|
||||
}
|
||||
} else {
|
||||
Timber.d("PAGINATOR: Skipping blank text block. CFI: ${block.cfi}, startOffset: ${block.startCharOffsetInSource}")
|
||||
|
|
@ -1036,8 +1044,10 @@ class BookPaginator(
|
|||
return null
|
||||
}
|
||||
|
||||
val targetPath = CfiUtils.getPath(cfi)
|
||||
val foundRange = index.find { range ->
|
||||
val cfiMatches = cfi.startsWith(range.cfi)
|
||||
val rangePath = CfiUtils.getPath(range.cfi)
|
||||
val cfiMatches = targetPath == rangePath || targetPath.startsWith(rangePath) || rangePath.startsWith(targetPath)
|
||||
val offsetMatches = charOffset >= range.startOffset && charOffset < range.endOffset
|
||||
cfiMatches && offsetMatches
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,39 +51,25 @@ class LocatorConverter(
|
|||
private val context: Context
|
||||
) {
|
||||
private suspend fun processAndCacheChapter(book: EpubBook, chapterIndex: Int): List<SemanticBlock>? = withContext(Dispatchers.IO) {
|
||||
Timber.tag("PosSaveDiag").d("processAndCacheChapter: STARTED for book='${book.title}', chapterIndex=$chapterIndex")
|
||||
Timber.tag("POS_DIAG").d("processAndCacheChapter: Processing for bookId='${book.title}' index=$chapterIndex")
|
||||
try {
|
||||
val chapter = book.chapters.getOrNull(chapterIndex)
|
||||
if (chapter == null) {
|
||||
Timber.tag("PosSaveDiag").e("processAndCacheChapter: FAILED. Chapter is null for index $chapterIndex")
|
||||
return@withContext null
|
||||
}
|
||||
val chapter = book.chapters.getOrNull(chapterIndex) ?: return@withContext null
|
||||
|
||||
Timber.tag("PosSaveDiag").d("processAndCacheChapter: Checking HTML content. RAM content length: ${chapter.htmlContent.length}")
|
||||
|
||||
val htmlToParse = if (chapter.htmlContent.isNotBlank()) {
|
||||
Timber.tag("PosSaveDiag").d("processAndCacheChapter: Using HTML from RAM.")
|
||||
chapter.htmlContent
|
||||
} else {
|
||||
Timber.tag("PosSaveDiag").d("processAndCacheChapter: RAM HTML is blank. Falling back to disk. Path: ${book.extractionBasePath} / ${chapter.htmlFilePath}")
|
||||
val htmlToParse = chapter.htmlContent.ifBlank {
|
||||
try {
|
||||
val file = File(book.extractionBasePath, chapter.htmlFilePath)
|
||||
if (file.exists()) {
|
||||
val content = file.readText()
|
||||
Timber.tag("PosSaveDiag").d("processAndCacheChapter: Read file from disk SUCCESS. Content length: ${content.length}")
|
||||
content
|
||||
} else {
|
||||
Timber.tag("PosSaveDiag").e("processAndCacheChapter: File DOES NOT EXIST at ${file.absolutePath}")
|
||||
""
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.tag("PosSaveDiag").e(e, "processAndCacheChapter: Exception reading chapter file from disk")
|
||||
} catch (_: Exception) {
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
if (htmlToParse.isBlank()) {
|
||||
Timber.tag("PosSaveDiag").w("processAndCacheChapter: Final HTML to parse is blank. Aborting semantic block generation.")
|
||||
return@withContext null
|
||||
}
|
||||
|
||||
|
|
@ -129,7 +115,6 @@ class LocatorConverter(
|
|||
otherComplex = mergedOtherComplex
|
||||
)
|
||||
|
||||
Timber.tag("PosSaveDiag").d("processAndCacheChapter: Calling htmlToSemanticBlocks...")
|
||||
val semanticBlocks = htmlToSemanticBlocks(
|
||||
html = htmlToParse,
|
||||
cssRules = parsingCssRules,
|
||||
|
|
@ -140,10 +125,8 @@ class LocatorConverter(
|
|||
fontFamilyMap = emptyMap(),
|
||||
constraints = constraints
|
||||
)
|
||||
Timber.tag("PosSaveDiag").d("processAndCacheChapter: htmlToSemanticBlocks returned ${semanticBlocks.size} blocks.")
|
||||
|
||||
val protoBytes = proto.encodeToByteArray(semanticBlocks)
|
||||
Timber.tag("PosSaveDiag").d("processAndCacheChapter: Encoded blocks to protoBytes (size: ${protoBytes.size} bytes).")
|
||||
|
||||
val newCacheEntry = ProcessedChapter(
|
||||
bookId = book.title,
|
||||
|
|
@ -152,10 +135,8 @@ class LocatorConverter(
|
|||
estimatedPageCount = 0
|
||||
)
|
||||
bookCacheDao.insertProcessedChapters(listOf(newCacheEntry))
|
||||
Timber.tag("PosSaveDiag").i("processAndCacheChapter: On-demand processing and DB caching SUCCESS for chapter $chapterIndex.")
|
||||
semanticBlocks
|
||||
} catch (e: Exception) {
|
||||
Timber.tag("PosSaveDiag").e(e, "processAndCacheChapter: FAILED for chapter $chapterIndex")
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
|
@ -172,12 +153,10 @@ class LocatorConverter(
|
|||
}
|
||||
|
||||
if (allBlocks.isNullOrEmpty()) {
|
||||
Timber.tag("PosSaveDiag").w("getLocatorFromCfi: Cache missing or empty for chapter $chapterIndex. Triggering on-demand processing.")
|
||||
allBlocks = processAndCacheChapter(book, chapterIndex)
|
||||
}
|
||||
|
||||
if (allBlocks.isNullOrEmpty()) {
|
||||
Timber.tag("PosSaveDiag").e("getLocatorFromCfi: FAILED. Could not get or process semantic blocks.")
|
||||
return@withContext null
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue