Bladeren bron

fix: 一些朗读问题

HapeLee 3 maanden geleden
bovenliggende
commit
603cdb5b13

+ 20 - 4
app/src/main/java/io/legado/app/service/BaseReadAloudService.kt

@@ -313,12 +313,20 @@ abstract class BaseReadAloudService : BaseService(),
                     pos = tmp
                     pos = tmp
                 }
                 }
             }
             }
-            var preparedNowSpeak = preparedChapter.getParagraphNum(
+            val usePreparedPlaybackQueue = useSpeechPlaybackQueue && !preparedPlaybackQueue.isEmpty
+            var preparedNowSpeak = preparedChapter.getParagraphNumAtOrAfter(
                 preparedReadAloudNumber + 1,
                 preparedReadAloudNumber + 1,
                 preparedReadAloudByPage,
                 preparedReadAloudByPage,
             ) - 1
             ) - 1
+            if (!usePreparedPlaybackQueue && preparedNowSpeak !in preparedContentList.indices) {
+                AppLog.put(
+                    "启动朗读失败:无法定位朗读段落 position=$preparedReadAloudNumber " +
+                        "pageIndex=$pageIndex startPos=$startPos"
+                )
+                return@execute
+            }
             val moveToLast = toLast
             val moveToLast = toLast
-            if (!preparedReadAloudByPage && startPos == 0 && !moveToLast) {
+            if (!usePreparedPlaybackQueue && !preparedReadAloudByPage && startPos == 0 && !moveToLast) {
                 pos = page.chapterPosition -
                 pos = page.chapterPosition -
                         preparedChapter.paragraphs[preparedNowSpeak].chapterPosition
                         preparedChapter.paragraphs[preparedNowSpeak].chapterPosition
             }
             }
@@ -331,7 +339,7 @@ abstract class BaseReadAloudService : BaseService(),
                 }
                 }
             }
             }
             var preparedParagraphStartPos = pos
             var preparedParagraphStartPos = pos
-            if (useSpeechPlaybackQueue && !preparedPlaybackQueue.isEmpty) {
+            if (usePreparedPlaybackQueue) {
                 preparedPlaybackQueue.cursorAt(preparedReadAloudNumber)?.let { cursor ->
                 preparedPlaybackQueue.cursorAt(preparedReadAloudNumber)?.let { cursor ->
                     preparedPlaybackCursor = cursor
                     preparedPlaybackCursor = cursor
                     preparedContentList = preparedPlaybackQueue.cues.map { it.text }
                     preparedContentList = preparedPlaybackQueue.cues.map { it.text }
@@ -526,11 +534,19 @@ abstract class BaseReadAloudService : BaseService(),
         }
         }
         if (nowSpeak > 0) {
         if (nowSpeak > 0) {
             playStop()
             playStop()
+            var foundPreviousReadableParagraph = false
             do {
             do {
                 nowSpeak--
                 nowSpeak--
                 readAloudNumber -= contentList[nowSpeak].length + 1 + paragraphStartPos
                 readAloudNumber -= contentList[nowSpeak].length + 1 + paragraphStartPos
                 paragraphStartPos = 0
                 paragraphStartPos = 0
-            } while (contentList[nowSpeak].matches(AppPattern.notReadAloudRegex))
+                foundPreviousReadableParagraph =
+                    !contentList[nowSpeak].matches(AppPattern.notReadAloudRegex)
+            } while (!foundPreviousReadableParagraph && nowSpeak > 0)
+            if (!foundPreviousReadableParagraph) {
+                toLast = true
+                ReadBook.moveToPrevChapter(true)
+                return
+            }
             textChapter?.let {
             textChapter?.let {
                 if (readAloudByPage) {
                 if (readAloudByPage) {
                     val paragraphs = it.getParagraphs(true)
                     val paragraphs = it.getParagraphs(true)

+ 23 - 6
app/src/main/java/io/legado/app/ui/book/read/page/entities/TextChapter.kt

@@ -215,14 +215,20 @@ data class TextChapter(
     fun getParagraphNum(
     fun getParagraphNum(
         position: Int,
         position: Int,
         pageSplit: Boolean,
         pageSplit: Boolean,
+    ): Int {
+        return findReadAloudParagraphNum(getParagraphs(pageSplit), position) ?: -1
+    }
+
+    /**
+     * Resolves a position between paragraphs to the next readable paragraph.
+     * Returns -1 only when the position is beyond all laid-out paragraphs.
+     */
+    fun getParagraphNumAtOrAfter(
+        position: Int,
+        pageSplit: Boolean,
     ): Int {
     ): Int {
         val paragraphs = getParagraphs(pageSplit)
         val paragraphs = getParagraphs(pageSplit)
-        paragraphs.forEach { paragraph ->
-            if (position in paragraph.chapterIndices) {
-                return paragraph.num
-            }
-        }
-        return -1
+        return findReadAloudParagraphNumAtOrAfter(paragraphs, position) ?: -1
     }
     }
 
 
     fun getParagraphs(pageSplit: Boolean): List<TextParagraph> {
     fun getParagraphs(pageSplit: Boolean): List<TextParagraph> {
@@ -352,3 +358,14 @@ data class TextChapter(
     }
     }
 
 
 }
 }
+
+internal fun findReadAloudParagraphNum(
+    paragraphs: List<TextParagraph>,
+    position: Int,
+): Int? = paragraphs.firstOrNull { position in it.chapterIndices }?.num
+
+internal fun findReadAloudParagraphNumAtOrAfter(
+    paragraphs: List<TextParagraph>,
+    position: Int,
+): Int? = findReadAloudParagraphNum(paragraphs, position)
+    ?: paragraphs.firstOrNull { it.chapterIndices.last >= position }?.num

+ 1 - 0
app/src/main/java/io/legado/app/ui/book/readaloud/player/ReadAloudPlayerCoordinator.kt

@@ -141,6 +141,7 @@ class ReadAloudPlayerCoordinator(
         val chapter = ReadBook.curTextChapter ?: return
         val chapter = ReadBook.curTextChapter ?: return
         val position = chapterPosition.coerceIn(0, chapterLength)
         val position = chapterPosition.coerceIn(0, chapterLength)
         val pageIndex = chapter.getPageIndexByCharIndex(position)
         val pageIndex = chapter.getPageIndexByCharIndex(position)
+        if (pageIndex < 0) return
         val startPos = position - chapter.getReadLength(pageIndex)
         val startPos = position - chapter.getReadLength(pageIndex)
         ReadAloud.play(application, play = true, pageIndex = pageIndex, startPos = startPos)
         ReadAloud.play(application, play = true, pageIndex = pageIndex, startPos = startPos)
     }
     }

+ 33 - 0
app/src/test/java/io/legado/app/ui/book/read/page/entities/TextChapterReadAloudPositionTest.kt

@@ -0,0 +1,33 @@
+package io.legado.app.ui.book.read.page.entities
+
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Test
+
+class TextChapterReadAloudPositionTest {
+
+    private val paragraphs = listOf(
+        paragraph(num = 1, position = 10, text = "第一段"),
+        paragraph(num = 2, position = 20, text = "第二段"),
+    )
+
+    @Test
+    fun `resolves a position within a paragraph`() {
+        assertEquals(1, findReadAloudParagraphNumAtOrAfter(paragraphs, 12))
+    }
+
+    @Test
+    fun `resolves a gap to the next paragraph`() {
+        assertEquals(2, findReadAloudParagraphNumAtOrAfter(paragraphs, 18))
+    }
+
+    @Test
+    fun `does not resolve a position after the final paragraph`() {
+        assertNull(findReadAloudParagraphNumAtOrAfter(paragraphs, 30))
+    }
+
+    private fun paragraph(num: Int, position: Int, text: String) = TextParagraph(
+        num = num,
+        textLines = arrayListOf(TextLine(text = text, chapterPosition = position)),
+    )
+}