mirror of
https://github.com/gedoor/legado.git
synced 2024-07-06 23:47:49 +08:00
优化
This commit is contained in:
parent
d5caabf2cd
commit
5d407f3753
@ -302,6 +302,9 @@ object BookHelp {
|
||||
* 读取章节内容
|
||||
*/
|
||||
fun getContent(book: Book, bookChapter: BookChapter): String? {
|
||||
if (book.isLocalTxt) {
|
||||
return LocalBook.getContent(book, bookChapter)
|
||||
}
|
||||
val file = downloadDir.getFile(
|
||||
cacheFolderName,
|
||||
book.getFolderName(),
|
||||
|
@ -25,6 +25,7 @@ class ContentProcessor private constructor(
|
||||
|
||||
companion object {
|
||||
private val processors = hashMapOf<String, WeakReference<ContentProcessor>>()
|
||||
var enableRemoveSameTitle = true
|
||||
|
||||
fun get(bookName: String, bookOrigin: String): ContentProcessor {
|
||||
val processorWr = processors[bookName + bookOrigin]
|
||||
@ -84,7 +85,7 @@ class ContentProcessor private constructor(
|
||||
var sameTitleRemoved = false
|
||||
if (content != "null") {
|
||||
//去除重复标题
|
||||
if (BookHelp.removeSameTitle(book, chapter)) try {
|
||||
if (enableRemoveSameTitle && BookHelp.removeSameTitle(book, chapter)) try {
|
||||
val name = Pattern.quote(book.name)
|
||||
var title = Pattern.quote(chapter.title)
|
||||
var matcher = Pattern.compile("^(\\s|\\p{P}|${name})*${title}(\\s)*")
|
||||
|
@ -10,6 +10,7 @@ import io.legado.app.help.book.isLocal
|
||||
import io.legado.app.help.config.AppConfig
|
||||
import io.legado.app.help.config.ReadBookConfig
|
||||
import io.legado.app.help.coroutine.Coroutine
|
||||
import io.legado.app.model.localBook.TextFile
|
||||
import io.legado.app.model.webBook.WebBook
|
||||
import io.legado.app.service.BaseReadAloudService
|
||||
import io.legado.app.ui.book.read.page.entities.TextChapter
|
||||
@ -77,6 +78,7 @@ object ReadBook : CoroutineScope by MainScope() {
|
||||
upWebBook(book)
|
||||
lastBookPress = null
|
||||
webBookProgress = null
|
||||
TextFile.txtBuffer = null
|
||||
synchronized(this) {
|
||||
loadingChapters.clear()
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ object LocalBook {
|
||||
}
|
||||
|
||||
fun getContent(book: Book, chapter: BookChapter): String? {
|
||||
val content = try {
|
||||
var content = try {
|
||||
when {
|
||||
book.isEpub -> {
|
||||
EpubFile.getContent(book, chapter)
|
||||
@ -114,13 +114,16 @@ object LocalBook {
|
||||
e.printOnDebug()
|
||||
AppLog.put("获取本地书籍内容失败\n${e.localizedMessage}", e)
|
||||
"获取本地书籍内容失败\n${e.localizedMessage}"
|
||||
}?.replace("<img", "< img", true)
|
||||
content ?: return null
|
||||
return kotlin.runCatching {
|
||||
Entities.unescape(content)
|
||||
}.onFailure {
|
||||
AppLog.put("HTML实体解码失败\n${it.localizedMessage}", it)
|
||||
}.getOrElse { content }
|
||||
}
|
||||
if (book.isEpub) {
|
||||
content = content?.replace("<img", "< img", true) ?: return null
|
||||
return kotlin.runCatching {
|
||||
Entities.unescape(content)
|
||||
}.onFailure {
|
||||
AppLog.put("HTML实体解码失败\n${it.localizedMessage}", it)
|
||||
}.getOrDefault(content)
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
fun getCoverPath(book: Book): String {
|
||||
|
@ -18,23 +18,60 @@ import kotlin.math.min
|
||||
class TextFile(private val book: Book) {
|
||||
|
||||
companion object {
|
||||
private val padRegex = "^[\\n\\s]+".toRegex()
|
||||
private const val bufferSize = 8 * 1024 * 1024
|
||||
var txtBuffer: ByteArray? = null
|
||||
var bufferStart = -1
|
||||
var bufferEnd = -1
|
||||
var bookUrl = ""
|
||||
|
||||
@Throws(FileNotFoundException::class)
|
||||
fun getChapterList(book: Book): ArrayList<BookChapter> {
|
||||
return TextFile(book).getChapterList()
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
@Throws(FileNotFoundException::class)
|
||||
fun getContent(book: Book, bookChapter: BookChapter): String {
|
||||
if (txtBuffer == null
|
||||
|| bookUrl != book.bookUrl
|
||||
|| bookChapter.start!! > bufferEnd
|
||||
|| bookChapter.end!! < bufferStart
|
||||
) {
|
||||
bookUrl = book.bookUrl
|
||||
LocalBook.getBookInputStream(book).use { bis ->
|
||||
bufferStart = bufferSize * (bookChapter.start!! / bufferSize).toInt()
|
||||
txtBuffer = ByteArray(min(bufferSize, bis.available() - bufferStart))
|
||||
bufferEnd = bufferStart + txtBuffer!!.size
|
||||
bis.skip(bufferStart.toLong())
|
||||
bis.read(txtBuffer)
|
||||
}
|
||||
}
|
||||
|
||||
val count = (bookChapter.end!! - bookChapter.start!!).toInt()
|
||||
val buffer = ByteArray(count)
|
||||
LocalBook.getBookInputStream(book).use { bis ->
|
||||
bis.skip(bookChapter.start!!)
|
||||
bis.read(buffer)
|
||||
|
||||
if (bookChapter.start!! < bufferEnd && bookChapter.end!! > bufferEnd
|
||||
|| bookChapter.start!! < bufferStart && bookChapter.end!! > bufferStart
|
||||
) {
|
||||
/** 章节内容在缓冲区交界处 */
|
||||
LocalBook.getBookInputStream(book).use { bis ->
|
||||
bis.skip(bookChapter.start!!)
|
||||
bis.read(buffer)
|
||||
}
|
||||
} else {
|
||||
/** 章节内容在缓冲区内 */
|
||||
txtBuffer!!.copyInto(
|
||||
buffer,
|
||||
0,
|
||||
(bookChapter.start!! - bufferStart).toInt(),
|
||||
(bookChapter.end!! - bufferStart).toInt()
|
||||
)
|
||||
}
|
||||
|
||||
return String(buffer, book.fileCharset())
|
||||
.substringAfter(bookChapter.title)
|
||||
.replace("^[\\n\\s]+".toRegex(), " ")
|
||||
.replace(padRegex, " ")
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -121,6 +121,7 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
val searchResult = IntentData.get<SearchResult>("searchResult$key")
|
||||
val searchResultList = IntentData.get<List<SearchResult>>("searchResultList$key")
|
||||
if (searchResult != null && searchResultList != null) {
|
||||
ContentProcessor.enableRemoveSameTitle = false
|
||||
viewModel.searchContentQuery = searchResult.query
|
||||
binding.searchMenu.upSearchResultList(searchResultList)
|
||||
isShowingSearchResult = true
|
||||
@ -1005,6 +1006,7 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
override fun exitSearchMenu() {
|
||||
if (isShowingSearchResult) {
|
||||
isShowingSearchResult = false
|
||||
ContentProcessor.enableRemoveSameTitle = true
|
||||
binding.searchMenu.invalidate()
|
||||
binding.searchMenu.invisible()
|
||||
binding.readView.isTextSelected = false
|
||||
|
@ -15,6 +15,7 @@ import io.legado.app.data.entities.BookChapter
|
||||
import io.legado.app.databinding.ActivitySearchContentBinding
|
||||
import io.legado.app.help.IntentData
|
||||
import io.legado.app.help.book.BookHelp
|
||||
import io.legado.app.help.book.ContentProcessor
|
||||
import io.legado.app.help.book.isLocal
|
||||
import io.legado.app.lib.theme.bottomBackground
|
||||
import io.legado.app.lib.theme.getPrimaryTextColor
|
||||
@ -144,43 +145,48 @@ class SearchContentActivity :
|
||||
@SuppressLint("SetTextI18n")
|
||||
fun startContentSearch(query: String) {
|
||||
// 按章节搜索内容
|
||||
if (query.isNotBlank()) {
|
||||
searchJob?.cancel()
|
||||
adapter.clearItems()
|
||||
viewModel.searchResultList.clear()
|
||||
viewModel.searchResultCounts = 0
|
||||
viewModel.lastQuery = query
|
||||
searchJob = launch {
|
||||
kotlin.runCatching {
|
||||
binding.refreshProgressBar.isAutoLoading = true
|
||||
binding.fbStop.visible()
|
||||
withContext(IO) {
|
||||
appDb.bookChapterDao.getChapterList(viewModel.bookUrl)
|
||||
}.forEach { bookChapter ->
|
||||
ensureActive()
|
||||
val searchResults = withContext(IO) {
|
||||
if (isLocalBook || viewModel.cacheChapterNames.contains(bookChapter.getFileName())) {
|
||||
viewModel.searchChapter(query, bookChapter)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
binding.tvCurrentSearchInfo.text =
|
||||
this@SearchContentActivity.getString(R.string.search_content_size) + ": ${viewModel.searchResultCounts}"
|
||||
ensureActive()
|
||||
if (searchResults != null && searchResults.isNotEmpty()) {
|
||||
viewModel.searchResultList.addAll(searchResults)
|
||||
if (query.isBlank()) return
|
||||
searchJob?.cancel()
|
||||
adapter.clearItems()
|
||||
viewModel.searchResultList.clear()
|
||||
viewModel.searchResultCounts = 0
|
||||
viewModel.lastQuery = query
|
||||
binding.refreshProgressBar.isAutoLoading = true
|
||||
binding.fbStop.visible()
|
||||
ContentProcessor.enableRemoveSameTitle = false
|
||||
searchJob = launch(IO) {
|
||||
kotlin.runCatching {
|
||||
appDb.bookChapterDao.getChapterList(viewModel.bookUrl).forEach { bookChapter ->
|
||||
ensureActive()
|
||||
val searchResults = if (isLocalBook
|
||||
|| viewModel.cacheChapterNames.contains(bookChapter.getFileName())
|
||||
) {
|
||||
viewModel.searchChapter(query, bookChapter)
|
||||
} else {
|
||||
return@forEach
|
||||
}
|
||||
ensureActive()
|
||||
if (searchResults.isNotEmpty()) {
|
||||
viewModel.searchResultList.addAll(searchResults)
|
||||
binding.tvCurrentSearchInfo.post {
|
||||
binding.tvCurrentSearchInfo.text =
|
||||
this@SearchContentActivity.getString(R.string.search_content_size) + ": ${viewModel.searchResultCounts}"
|
||||
adapter.addItems(searchResults)
|
||||
}
|
||||
}
|
||||
if (viewModel.searchResultCounts == 0) {
|
||||
val noSearchResult =
|
||||
SearchResult(resultText = getString(R.string.search_content_empty))
|
||||
}
|
||||
if (viewModel.searchResultCounts == 0) {
|
||||
val noSearchResult =
|
||||
SearchResult(resultText = getString(R.string.search_content_empty))
|
||||
binding.tvCurrentSearchInfo.post {
|
||||
adapter.addItem(noSearchResult)
|
||||
}
|
||||
}.onFailure {
|
||||
AppLog.put("全文搜索出错\n${it.localizedMessage}", it)
|
||||
}
|
||||
}.onFailure {
|
||||
AppLog.put("全文搜索出错\n${it.localizedMessage}", it)
|
||||
}
|
||||
ContentProcessor.enableRemoveSameTitle = true
|
||||
binding.tvCurrentSearchInfo.post {
|
||||
binding.fbStop.invisible()
|
||||
binding.refreshProgressBar.isAutoLoading = false
|
||||
}
|
||||
|
@ -41,42 +41,35 @@ class SearchContentViewModel(application: Application) : BaseViewModel(applicati
|
||||
chapter: BookChapter?
|
||||
): List<SearchResult> {
|
||||
val searchResultsWithinChapter: MutableList<SearchResult> = mutableListOf()
|
||||
if (chapter != null) {
|
||||
book?.let { book ->
|
||||
val chapterContent = BookHelp.getContent(book, chapter)
|
||||
val mContent: String
|
||||
coroutineContext.ensureActive()
|
||||
if (chapterContent != null) {
|
||||
withContext(Dispatchers.IO) {
|
||||
chapter.title = when (AppConfig.chineseConverterType) {
|
||||
1 -> ChineseUtils.t2s(chapter.title)
|
||||
2 -> ChineseUtils.s2t(chapter.title)
|
||||
else -> chapter.title
|
||||
}
|
||||
coroutineContext.ensureActive()
|
||||
mContent = contentProcessor!!.getContent(
|
||||
book, chapter, chapterContent
|
||||
).toString()
|
||||
}
|
||||
val positions = searchPosition(mContent, query)
|
||||
positions.forEachIndexed { index, position ->
|
||||
coroutineContext.ensureActive()
|
||||
val construct = getResultAndQueryIndex(mContent, position, query)
|
||||
val result = SearchResult(
|
||||
resultCountWithinChapter = index,
|
||||
resultText = construct.second,
|
||||
chapterTitle = chapter.title,
|
||||
query = query,
|
||||
chapterIndex = chapter.index,
|
||||
queryIndexInResult = construct.first,
|
||||
queryIndexInChapter = position
|
||||
)
|
||||
searchResultsWithinChapter.add(result)
|
||||
}
|
||||
searchResultCounts += searchResultsWithinChapter.size
|
||||
}
|
||||
}
|
||||
chapter ?: return searchResultsWithinChapter
|
||||
val book = book ?: return searchResultsWithinChapter
|
||||
val chapterContent = BookHelp.getContent(book, chapter) ?: return searchResultsWithinChapter
|
||||
coroutineContext.ensureActive()
|
||||
chapter.title = when (AppConfig.chineseConverterType) {
|
||||
1 -> ChineseUtils.t2s(chapter.title)
|
||||
2 -> ChineseUtils.s2t(chapter.title)
|
||||
else -> chapter.title
|
||||
}
|
||||
coroutineContext.ensureActive()
|
||||
val mContent = contentProcessor!!.getContent(
|
||||
book, chapter, chapterContent
|
||||
).toString()
|
||||
val positions = searchPosition(mContent, query)
|
||||
positions.forEachIndexed { index, position ->
|
||||
coroutineContext.ensureActive()
|
||||
val construct = getResultAndQueryIndex(mContent, position, query)
|
||||
val result = SearchResult(
|
||||
resultCountWithinChapter = index,
|
||||
resultText = construct.second,
|
||||
chapterTitle = chapter.title,
|
||||
query = query,
|
||||
chapterIndex = chapter.index,
|
||||
queryIndexInResult = construct.first,
|
||||
queryIndexInChapter = position
|
||||
)
|
||||
searchResultsWithinChapter.add(result)
|
||||
}
|
||||
searchResultCounts += searchResultsWithinChapter.size
|
||||
return searchResultsWithinChapter
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user