@@ -134,6 +134,13 @@ data class BookSource(
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun hasGroup(group: String): Boolean {
|
||||||
|
bookSourceGroup?.splitNotBlank(AppPattern.splitGroupRegex)?.toHashSet()?.let {
|
||||||
|
return if (it.indexOf(group) != -1) true else false
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
fun equal(source: BookSource) =
|
fun equal(source: BookSource) =
|
||||||
equal(bookSourceName, source.bookSourceName)
|
equal(bookSourceName, source.bookSourceName)
|
||||||
&& equal(bookSourceUrl, source.bookSourceUrl)
|
&& equal(bookSourceUrl, source.bookSourceUrl)
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class ContentProcessor private constructor(
|
|||||||
|
|
||||||
fun getContent(
|
fun getContent(
|
||||||
book: Book,
|
book: Book,
|
||||||
chapter: BookChapter, //已经经过简繁转换
|
chapter: BookChapter,
|
||||||
content: String,
|
content: String,
|
||||||
includeTitle: Boolean = true,
|
includeTitle: Boolean = true,
|
||||||
useReplace: Boolean = true,
|
useReplace: Boolean = true,
|
||||||
@@ -83,20 +83,7 @@ class ContentProcessor private constructor(
|
|||||||
}
|
}
|
||||||
if (useReplace && book.getUseReplaceRule()) {
|
if (useReplace && book.getUseReplaceRule()) {
|
||||||
//替换
|
//替换
|
||||||
getReplaceRules().forEach { item ->
|
mContent = replaceContent(mContent)
|
||||||
if (item.pattern.isNotEmpty()) {
|
|
||||||
try {
|
|
||||||
mContent = if (item.isRegex) {
|
|
||||||
mContent.replace(item.pattern.toRegex(), item.replacement)
|
|
||||||
} else {
|
|
||||||
mContent.replace(item.pattern, item.replacement)
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
AppLog.put("${item.name}替换出错\n${e.localizedMessage}")
|
|
||||||
appCtx.toastOnUi("${item.name}替换出错")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (chineseConvert) {
|
if (chineseConvert) {
|
||||||
//简繁转换
|
//简繁转换
|
||||||
@@ -125,4 +112,23 @@ class ContentProcessor private constructor(
|
|||||||
return contents
|
return contents
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun replaceContent(content: String): String {
|
||||||
|
var mContent = content
|
||||||
|
getReplaceRules().forEach { item ->
|
||||||
|
if (item.pattern.isNotEmpty()) {
|
||||||
|
try {
|
||||||
|
mContent = if (item.isRegex) {
|
||||||
|
mContent.replace(item.pattern.toRegex(), item.replacement)
|
||||||
|
} else {
|
||||||
|
mContent.replace(item.pattern, item.replacement)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
AppLog.put("${item.name}替换出错\n${e.localizedMessage}")
|
||||||
|
appCtx.toastOnUi("${item.name}替换出错")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mContent
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -124,6 +124,7 @@ class TextFile(private val book: Book) {
|
|||||||
val chapterStart = matcher.start()
|
val chapterStart = matcher.start()
|
||||||
//获取章节内容
|
//获取章节内容
|
||||||
val chapterContent = blockContent.substring(seekPos, chapterStart)
|
val chapterContent = blockContent.substring(seekPos, chapterStart)
|
||||||
|
var mChapterContent = chapterContent
|
||||||
val chapterLength = chapterContent.toByteArray(charset).size
|
val chapterLength = chapterContent.toByteArray(charset).size
|
||||||
val lastStart = toc.lastOrNull()?.start ?: curOffset
|
val lastStart = toc.lastOrNull()?.start ?: curOffset
|
||||||
if (curOffset + chapterLength - lastStart > 50000) {
|
if (curOffset + chapterLength - lastStart > 50000) {
|
||||||
@@ -150,6 +151,7 @@ class TextFile(private val book: Book) {
|
|||||||
} else { //否则就block分割之后,上一个章节的剩余内容
|
} else { //否则就block分割之后,上一个章节的剩余内容
|
||||||
//获取上一章节
|
//获取上一章节
|
||||||
val lastChapter = toc.last()
|
val lastChapter = toc.last()
|
||||||
|
toc.last().isVolume = mChapterContent.substringAfter(lastChapter.title).isBlank()
|
||||||
//将当前段落添加上一章去
|
//将当前段落添加上一章去
|
||||||
lastChapter.end =
|
lastChapter.end =
|
||||||
lastChapter.end!! + chapterLength.toLong()
|
lastChapter.end!! + chapterLength.toLong()
|
||||||
@@ -163,6 +165,7 @@ class TextFile(private val book: Book) {
|
|||||||
if (toc.isNotEmpty()) { //获取章节内容
|
if (toc.isNotEmpty()) { //获取章节内容
|
||||||
//获取上一章节
|
//获取上一章节
|
||||||
val lastChapter = toc.last()
|
val lastChapter = toc.last()
|
||||||
|
toc.last().isVolume = mChapterContent.substringAfter(lastChapter.title).isBlank()
|
||||||
lastChapter.end =
|
lastChapter.end =
|
||||||
lastChapter.start!! + chapterContent.toByteArray(charset).size.toLong()
|
lastChapter.start!! + chapterContent.toByteArray(charset).size.toLong()
|
||||||
//创建当前章节
|
//创建当前章节
|
||||||
|
|||||||
@@ -121,8 +121,8 @@ class CheckSourceService : BaseService() {
|
|||||||
searchWord = it
|
searchWord = it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//校验搜索
|
//校验搜索 用户设置校验搜索 并且 搜索链接不为空
|
||||||
if (CheckSource.checkSearch) {
|
if (CheckSource.checkSearch && !source.searchUrl.isNullOrBlank()) {
|
||||||
books = WebBook.searchBookAwait(this, source, searchWord)
|
books = WebBook.searchBookAwait(this, source, searchWord)
|
||||||
if (books.isEmpty()) source.addGroup("搜索失效") else source.removeGroup("搜索失效")
|
if (books.isEmpty()) source.addGroup("搜索失效") else source.removeGroup("搜索失效")
|
||||||
}
|
}
|
||||||
@@ -187,7 +187,11 @@ class CheckSourceService : BaseService() {
|
|||||||
?.filterNot {
|
?.filterNot {
|
||||||
it.startsWith("Error: ")
|
it.startsWith("Error: ")
|
||||||
}?.joinToString("\n")
|
}?.joinToString("\n")
|
||||||
|
if (source.hasGroup("搜索失效")) {
|
||||||
|
Debug.updateFinalMessage(source.bookSourceUrl, "失败")
|
||||||
|
} else {
|
||||||
Debug.updateFinalMessage(source.bookSourceUrl, "成功")
|
Debug.updateFinalMessage(source.bookSourceUrl, "成功")
|
||||||
|
}
|
||||||
}.onFinally(searchCoroutine) {
|
}.onFinally(searchCoroutine) {
|
||||||
source.respondTime = Debug.getRespondTime(source.bookSourceUrl)
|
source.respondTime = Debug.getRespondTime(source.bookSourceUrl)
|
||||||
appDb.bookSourceDao.update(source)
|
appDb.bookSourceDao.update(source)
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
|
|||||||
.getContent(
|
.getContent(
|
||||||
book,
|
book,
|
||||||
chapter,
|
chapter,
|
||||||
content ?: "null",
|
content ?: "",
|
||||||
includeTitle = !AppConfig.exportNoChapterName,
|
includeTitle = !AppConfig.exportNoChapterName,
|
||||||
useReplace = useReplace,
|
useReplace = useReplace,
|
||||||
chineseConvert = false,
|
chineseConvert = false,
|
||||||
@@ -419,7 +419,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
|
|||||||
upAdapterLiveData.postValue(book.bookUrl)
|
upAdapterLiveData.postValue(book.bookUrl)
|
||||||
exportProgress[book.bookUrl] = index
|
exportProgress[book.bookUrl] = index
|
||||||
BookHelp.getContent(book, chapter).let { content ->
|
BookHelp.getContent(book, chapter).let { content ->
|
||||||
var content1 = fixPic(epubBook, book, content ?: "null", chapter)
|
var content1 = fixPic(epubBook, book, content ?: "", chapter)
|
||||||
content1 = contentProcessor
|
content1 = contentProcessor
|
||||||
.getContent(
|
.getContent(
|
||||||
book,
|
book,
|
||||||
|
|||||||
@@ -129,12 +129,13 @@ object ChapterProvider {
|
|||||||
matcher.appendTail(sb)
|
matcher.appendTail(sb)
|
||||||
text = sb.toString()
|
text = sb.toString()
|
||||||
val isTitle = index == 0
|
val isTitle = index == 0
|
||||||
val isVolumeTitle = bookChapter.isVolume && isTitle && contents.size == 1
|
val isTitleWithNoContent = isTitle && contents.size == 1
|
||||||
|
val isVolumeTitle = isTitle && bookChapter.isVolume
|
||||||
val textPaint = if (isTitle) titlePaint else contentPaint
|
val textPaint = if (isTitle) titlePaint else contentPaint
|
||||||
if (!(isTitle && ReadBookConfig.titleMode == 2)) {
|
if (!(isTitle && ReadBookConfig.titleMode == 2)) {
|
||||||
setTypeText(
|
setTypeText(
|
||||||
absStartX, durY, text, textPages, stringBuilder,
|
absStartX, durY, text, textPages, stringBuilder,
|
||||||
isTitle, isVolumeTitle, textPaint, srcList
|
isTitle, isTitleWithNoContent, isVolumeTitle, textPaint, srcList
|
||||||
).let {
|
).let {
|
||||||
absStartX = it.first
|
absStartX = it.first
|
||||||
durY = it.second
|
durY = it.second
|
||||||
@@ -147,12 +148,13 @@ object ChapterProvider {
|
|||||||
val text = content.substring(start, matcher.start())
|
val text = content.substring(start, matcher.start())
|
||||||
if (text.isNotBlank()) {
|
if (text.isNotBlank()) {
|
||||||
val isTitle = index == 0
|
val isTitle = index == 0
|
||||||
val isVolumeTitle = bookChapter.isVolume && isTitle && contents.size == 1
|
val isTitleWithNoContent = isTitle && contents.size == 1
|
||||||
|
val isVolumeTitle = isTitle && bookChapter.isVolume
|
||||||
val textPaint = if (isTitle) titlePaint else contentPaint
|
val textPaint = if (isTitle) titlePaint else contentPaint
|
||||||
if (!(isTitle && ReadBookConfig.titleMode == 2)) {
|
if (!(isTitle && ReadBookConfig.titleMode == 2)) {
|
||||||
setTypeText(
|
setTypeText(
|
||||||
absStartX, durY, text, textPages,
|
absStartX, durY, text, textPages, stringBuilder,
|
||||||
stringBuilder, isTitle, isVolumeTitle, textPaint
|
isTitle, isTitleWithNoContent, isVolumeTitle, textPaint
|
||||||
).let {
|
).let {
|
||||||
absStartX = it.first
|
absStartX = it.first
|
||||||
durY = it.second
|
durY = it.second
|
||||||
@@ -169,12 +171,13 @@ object ChapterProvider {
|
|||||||
val text = content.substring(start, content.length)
|
val text = content.substring(start, content.length)
|
||||||
if (text.isNotBlank()) {
|
if (text.isNotBlank()) {
|
||||||
val isTitle = index == 0
|
val isTitle = index == 0
|
||||||
val isVolumeTitle = bookChapter.isVolume && isTitle && contents.size == 1
|
val isTitleWithNoContent = isTitle && contents.size == 1
|
||||||
|
val isVolumeTitle = isTitle && bookChapter.isVolume
|
||||||
val textPaint = if (isTitle) titlePaint else contentPaint
|
val textPaint = if (isTitle) titlePaint else contentPaint
|
||||||
if (!(isTitle && ReadBookConfig.titleMode == 2)) {
|
if (!(isTitle && ReadBookConfig.titleMode == 2)) {
|
||||||
setTypeText(
|
setTypeText(
|
||||||
absStartX, durY, text, textPages,
|
absStartX, durY, text, textPages, stringBuilder,
|
||||||
stringBuilder, isTitle, isVolumeTitle, textPaint
|
isTitle, isTitleWithNoContent, isVolumeTitle, textPaint
|
||||||
).let {
|
).let {
|
||||||
absStartX = it.first
|
absStartX = it.first
|
||||||
durY = it.second
|
durY = it.second
|
||||||
@@ -277,6 +280,7 @@ object ChapterProvider {
|
|||||||
textPages: ArrayList<TextPage>,
|
textPages: ArrayList<TextPage>,
|
||||||
stringBuilder: StringBuilder,
|
stringBuilder: StringBuilder,
|
||||||
isTitle: Boolean,
|
isTitle: Boolean,
|
||||||
|
isTitleWithNoContent: Boolean,
|
||||||
isVolumeTitle: Boolean,
|
isVolumeTitle: Boolean,
|
||||||
textPaint: TextPaint,
|
textPaint: TextPaint,
|
||||||
srcList: LinkedList<String>? = null
|
srcList: LinkedList<String>? = null
|
||||||
@@ -288,7 +292,8 @@ object ChapterProvider {
|
|||||||
text, textPaint, visibleWidth, Layout.Alignment.ALIGN_NORMAL, 0f, 0f, true
|
text, textPaint, visibleWidth, Layout.Alignment.ALIGN_NORMAL, 0f, 0f, true
|
||||||
)
|
)
|
||||||
var durY = when {
|
var durY = when {
|
||||||
isVolumeTitle -> ((visibleHeight - layout.lineCount * textPaint.textHeight) / 2).toFloat()
|
//标题y轴居中
|
||||||
|
isTitleWithNoContent -> ((visibleHeight - layout.lineCount * textPaint.textHeight) / 2).toFloat()
|
||||||
isTitle -> y + titleTopSpacing
|
isTitle -> y + titleTopSpacing
|
||||||
else -> y
|
else -> y
|
||||||
}
|
}
|
||||||
@@ -334,8 +339,8 @@ object ChapterProvider {
|
|||||||
//最后一行
|
//最后一行
|
||||||
textLine.text = "$words\n"
|
textLine.text = "$words\n"
|
||||||
isLastLine = true
|
isLastLine = true
|
||||||
//标题居中
|
//标题x轴居中
|
||||||
val startX = if (isTitle && ReadBookConfig.titleMode == 1 || isVolumeTitle)
|
val startX = if (isTitle && ReadBookConfig.titleMode == 1 || isTitleWithNoContent || isVolumeTitle)
|
||||||
(visibleWidth - layout.getLineWidth(lineIndex)) / 2
|
(visibleWidth - layout.getLineWidth(lineIndex)) / 2
|
||||||
else 0f
|
else 0f
|
||||||
addCharsToLineLast(
|
addCharsToLineLast(
|
||||||
@@ -368,7 +373,7 @@ object ChapterProvider {
|
|||||||
durY += textPaint.textHeight * lineSpacingExtra
|
durY += textPaint.textHeight * lineSpacingExtra
|
||||||
textPages.last().height = durY
|
textPages.last().height = durY
|
||||||
}
|
}
|
||||||
if (isTitle && !isVolumeTitle) durY += titleBottomSpacing
|
if (isTitle && !isTitleWithNoContent) durY += titleBottomSpacing
|
||||||
durY += textPaint.textHeight * paragraphSpacing / 10f
|
durY += textPaint.textHeight * paragraphSpacing / 10f
|
||||||
return Pair(absStartX, durY)
|
return Pair(absStartX, durY)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ class SearchContentActivity :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
binding.refreshProgressBar.isAutoLoading = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class SearchContentViewModel(application: Application) : BaseViewModel(applicati
|
|||||||
var searchResultCounts = 0
|
var searchResultCounts = 0
|
||||||
val cacheChapterNames = hashSetOf<String>()
|
val cacheChapterNames = hashSetOf<String>()
|
||||||
val searchResultList: MutableList<SearchResult> = mutableListOf()
|
val searchResultList: MutableList<SearchResult> = mutableListOf()
|
||||||
|
var mContent: String = ""
|
||||||
|
|
||||||
fun initBook(bookUrl: String, success: () -> Unit) {
|
fun initBook(bookUrl: String, success: () -> Unit) {
|
||||||
this.bookUrl = bookUrl
|
this.bookUrl = bookUrl
|
||||||
@@ -40,21 +41,20 @@ class SearchContentViewModel(application: Application) : BaseViewModel(applicati
|
|||||||
book?.let { book ->
|
book?.let { book ->
|
||||||
val chapterContent = BookHelp.getContent(book, chapter)
|
val chapterContent = BookHelp.getContent(book, chapter)
|
||||||
if (chapterContent != null) {
|
if (chapterContent != null) {
|
||||||
//搜索替换后的正文
|
//先搜索没有启用净化的正文
|
||||||
val replaceContent: String
|
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
chapter.title = when (AppConfig.chineseConverterType) {
|
chapter.title = when (AppConfig.chineseConverterType) {
|
||||||
1 -> ChineseUtils.t2s(chapter.title)
|
1 -> ChineseUtils.t2s(chapter.title)
|
||||||
2 -> ChineseUtils.s2t(chapter.title)
|
2 -> ChineseUtils.s2t(chapter.title)
|
||||||
else -> chapter.title
|
else -> chapter.title
|
||||||
}
|
}
|
||||||
replaceContent = contentProcessor!!.getContent(
|
mContent = contentProcessor!!.getContent(
|
||||||
book, chapter, chapterContent, chineseConvert = false, reSegment = false
|
book, chapter, chapterContent, chineseConvert = false, reSegment = false, useReplace = false
|
||||||
).joinToString("")
|
).joinToString("")
|
||||||
}
|
}
|
||||||
val positions = searchPosition(replaceContent, query)
|
val positions = searchPosition(query)
|
||||||
positions.forEachIndexed { index, position ->
|
positions.forEachIndexed { index, position ->
|
||||||
val construct = getResultAndQueryIndex(replaceContent, position, query)
|
val construct = getResultAndQueryIndex(mContent, position, query)
|
||||||
val result = SearchResult(
|
val result = SearchResult(
|
||||||
resultCountWithinChapter = index,
|
resultCountWithinChapter = index,
|
||||||
resultText = construct.second,
|
resultText = construct.second,
|
||||||
@@ -73,12 +73,16 @@ class SearchContentViewModel(application: Application) : BaseViewModel(applicati
|
|||||||
return searchResultsWithinChapter
|
return searchResultsWithinChapter
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun searchPosition(chapterContent: String, pattern: String): List<Int> {
|
private fun searchPosition(pattern: String): List<Int> {
|
||||||
val position: MutableList<Int> = mutableListOf()
|
val position: MutableList<Int> = mutableListOf()
|
||||||
var index = chapterContent.indexOf(pattern)
|
if (mContent.indexOf(pattern) >= 0) {
|
||||||
|
//搜索到内容允许净化
|
||||||
|
if (book?.getUseReplaceRule() ?: false) mContent = contentProcessor!!.replaceContent(mContent)
|
||||||
|
var index = mContent.indexOf(pattern)
|
||||||
while (index >= 0) {
|
while (index >= 0) {
|
||||||
position.add(index)
|
position.add(index)
|
||||||
index = chapterContent.indexOf(pattern, index + 1)
|
index = mContent.indexOf(pattern, index + 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return position
|
return position
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user