导出支持替换净化

pull/478/head
gedoor 4 years ago
parent cbda1e7f6c
commit 2aae76a8b4
  1. 9
      app/src/main/java/io/legado/app/data/dao/ReplaceRuleDao.kt
  2. 91
      app/src/main/java/io/legado/app/help/BookHelp.kt
  3. 81
      app/src/main/java/io/legado/app/help/ContentProcessor.kt
  4. 11
      app/src/main/java/io/legado/app/service/help/ReadBook.kt
  5. 88
      app/src/main/java/io/legado/app/ui/book/cache/CacheViewModel.kt
  6. 2
      app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt
  7. 28
      app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentActivity.kt
  8. 5
      app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentViewModel.kt
  9. 4
      app/src/main/java/io/legado/app/ui/replace/ReplaceRuleActivity.kt

@ -32,15 +32,6 @@ interface ReplaceRuleDao {
@Query("SELECT * FROM replace_rules WHERE id in (:ids)")
fun findByIds(vararg ids: Long): List<ReplaceRule>
@Query(
"""
SELECT * FROM replace_rules WHERE isEnabled = 1
AND (scope LIKE '%' || :scope || '%' or scope is null or scope = '')
order by sortOrder
"""
)
fun findEnabledByScope(scope: String): List<ReplaceRule>
@Query(
"""
SELECT * FROM replace_rules WHERE isEnabled = 1

@ -1,22 +1,17 @@
package io.legado.app.help
import com.hankcs.hanlp.HanLP
import io.legado.app.App
import io.legado.app.constant.AppPattern
import io.legado.app.constant.EventBus
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter
import io.legado.app.data.entities.ReplaceRule
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.model.analyzeRule.AnalyzeUrl
import io.legado.app.model.localBook.LocalBook
import io.legado.app.utils.*
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import net.ricecode.similarity.JaroWinklerStrategy
import net.ricecode.similarity.StringSimilarityServiceImpl
import org.jetbrains.anko.toast
import java.io.File
import java.util.concurrent.CopyOnWriteArraySet
import java.util.regex.Matcher
@ -317,90 +312,4 @@ object BookHelp {
.replace(regexOther, "")
}
private var bookName: String? = null
private var bookOrigin: String? = null
private var replaceRules: List<ReplaceRule> = arrayListOf()
@Synchronized
fun upReplaceRules() {
val o = bookOrigin
bookName?.let {
replaceRules = if (o.isNullOrEmpty()) {
App.db.replaceRuleDao().findEnabledByScope(it)
} else {
App.db.replaceRuleDao().findEnabledByScope(it, o)
}
}
}
suspend fun disposeContent(
book: Book,
title: String,
content: String,
): List<String> {
var title1 = title
var content1 = content
if (book.getUseReplaceRule()) {
synchronized(this) {
if (bookName != book.name || bookOrigin != book.origin) {
bookName = book.name
bookOrigin = book.origin
replaceRules = if (bookOrigin.isNullOrEmpty()) {
App.db.replaceRuleDao().findEnabledByScope(bookName!!)
} else {
App.db.replaceRuleDao().findEnabledByScope(bookName!!, bookOrigin!!)
}
}
}
replaceRules.forEach { item ->
item.pattern.let {
if (it.isNotEmpty()) {
try {
content1 = if (item.isRegex) {
content1.replace(it.toRegex(), item.replacement)
} else {
content1.replace(it, item.replacement)
}
} catch (e: Exception) {
withContext(Main) {
App.INSTANCE.toast("${item.name}替换出错")
}
}
}
}
}
}
if (book.getReSegment()) {
content1 = ContentHelp.reSegment(content1, title1)
}
try {
when (AppConfig.chineseConverterType) {
1 -> {
title1 = HanLP.convertToSimplifiedChinese(title1)
content1 = HanLP.convertToSimplifiedChinese(content1)
}
2 -> {
title1 = HanLP.convertToTraditionalChinese(title1)
content1 = HanLP.convertToTraditionalChinese(content1)
}
}
} catch (e: Exception) {
withContext(Main) {
App.INSTANCE.toast("简繁转换出错")
}
}
val contents = arrayListOf<String>()
content1.split("\n").forEach {
val str = it.replace("^[\\n\\s\\r]+".toRegex(), "")
if (contents.isEmpty()) {
contents.add(title1)
if (str != title1 && str.isNotEmpty()) {
contents.add("${ReadBookConfig.paragraphIndent}$str")
}
} else if (str.isNotEmpty()) {
contents.add("${ReadBookConfig.paragraphIndent}$str")
}
}
return contents
}
}

@ -0,0 +1,81 @@
package io.legado.app.help
import com.hankcs.hanlp.HanLP
import io.legado.app.App
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.ReplaceRule
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.jetbrains.anko.toast
class ContentProcessor(private val bookName: String, private val bookOrigin: String) {
private var replaceRules = arrayListOf<ReplaceRule>()
init {
upReplaceRules()
}
@Synchronized
fun upReplaceRules() {
replaceRules.clear()
replaceRules.addAll(App.db.replaceRuleDao().findEnabledByScope(bookName, bookOrigin))
}
suspend fun getContent(
book: Book,
title: String, //已经经过简繁转换
content: String,
isRead: Boolean = true
): List<String> {
var content1 = content
if (book.getUseReplaceRule()) {
replaceRules.forEach { item ->
item.pattern.let {
if (it.isNotEmpty()) {
try {
content1 = if (item.isRegex) {
content1.replace(it.toRegex(), item.replacement)
} else {
content1.replace(it, item.replacement)
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
App.INSTANCE.toast("${item.name}替换出错")
}
}
}
}
}
}
if (isRead) {
if (book.getReSegment()) {
content1 = ContentHelp.reSegment(content1, title)
}
try {
when (AppConfig.chineseConverterType) {
1 -> content1 = HanLP.convertToSimplifiedChinese(content1)
2 -> content1 = HanLP.convertToTraditionalChinese(content1)
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
App.INSTANCE.toast("简繁转换出错")
}
}
}
val contents = arrayListOf<String>()
content1.split("\n").forEach {
val str = it.replace("^[\\n\\s\\r]+".toRegex(), "")
if (contents.isEmpty()) {
contents.add(title)
if (str != title && str.isNotEmpty()) {
contents.add("${ReadBookConfig.paragraphIndent}$str")
}
} else if (str.isNotEmpty()) {
contents.add("${ReadBookConfig.paragraphIndent}$str")
}
}
return contents
}
}

@ -8,10 +8,7 @@ import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter
import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.ReadRecord
import io.legado.app.help.AppConfig
import io.legado.app.help.BookHelp
import io.legado.app.help.IntentDataHelp
import io.legado.app.help.ReadBookConfig
import io.legado.app.help.*
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.model.webBook.WebBook
import io.legado.app.service.BaseReadAloudService
@ -30,6 +27,7 @@ import org.jetbrains.anko.toast
object ReadBook {
var titleDate = MutableLiveData<String>()
var book: Book? = null
var contentProcessor: ContentProcessor? = null
var inBookshelf = false
var chapterSize = 0
var durChapterIndex = 0
@ -48,6 +46,7 @@ object ReadBook {
fun resetData(book: Book) {
this.book = book
contentProcessor = ContentProcessor(book.name, book.origin)
readRecord.bookName = book.name
readRecord.readTime = App.db.readRecordDao().getReadTime(book.name) ?: 0
durChapterIndex = book.durChapterIndex
@ -372,13 +371,14 @@ object ReadBook {
resetPageOffset: Boolean
) {
Coroutine.async {
ImageProvider.clearOut(durChapterIndex)
if (chapter.index in durChapterIndex - 1..durChapterIndex + 1) {
chapter.title = when (AppConfig.chineseConverterType) {
1 -> HanLP.convertToSimplifiedChinese(chapter.title)
2 -> HanLP.convertToTraditionalChinese(chapter.title)
else -> chapter.title
}
val contents = BookHelp.disposeContent(book, chapter.title, content)
val contents = contentProcessor!!.getContent(book, chapter.title, content)
when (chapter.index) {
durChapterIndex -> {
curTextChapter =
@ -393,7 +393,6 @@ object ReadBook {
callBack?.upView()
curPageChanged()
callBack?.contentLoadFinish()
ImageProvider.clearOut(durChapterIndex)
}
durChapterIndex - 1 -> {
prevTextChapter =

@ -10,6 +10,7 @@ import io.legado.app.constant.AppPattern
import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.Book
import io.legado.app.help.BookHelp
import io.legado.app.help.ContentProcessor
import io.legado.app.help.storage.WebDavHelp
import io.legado.app.utils.*
import java.io.File
@ -35,12 +36,12 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
}
}
private fun export(doc: DocumentFile, book: Book) {
private suspend fun export(doc: DocumentFile, book: Book) {
val filename = "${book.name} by ${book.author}.txt"
val content = getAllContents(book)
DocumentUtils.createFileIfNotExist(doc, filename)
?.writeText(context, content)
if(App.INSTANCE.getPrefBoolean(PreferKey.webDavCacheBackup,false)) {
if (App.INSTANCE.getPrefBoolean(PreferKey.webDavCacheBackup, false)) {
FileUtils.createFileIfNotExist(
File(FileUtils.getCachePath()),
filename
@ -50,74 +51,75 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
// 上传完删除cache文件
FileUtils.deleteFile("${FileUtils.getCachePath()}${File.separator}${filename}")
}
App.db.bookChapterDao().getChapterList(book.bookUrl).forEach { chapter ->
BookHelp.getContent(book, chapter).let { content ->
content?.split("\n")?.forEachIndexed { index, text ->
val matcher = AppPattern.imgPattern.matcher(text)
if (matcher.find()) {
var src = matcher.group(1)
src = NetworkUtils.getAbsoluteURL(chapter.url, src)
src?.let {
val vFile = BookHelp.getImage(book, src)
getSrcList(book).forEach {
val vFile = BookHelp.getImage(book, it.third)
if (vFile.exists()) {
DocumentUtils.createFileIfNotExist(doc,
"${index}-${MD5Utils.md5Encode16(src)}.jpg",
subDirs = arrayOf("${book.name}_${book.author}",
"images",
chapter.title))
?.writeBytes(context, vFile.readBytes())
}
}
}
}
DocumentUtils.createFileIfNotExist(
doc,
"${it.second}-${MD5Utils.md5Encode16(it.third)}.jpg",
subDirs = arrayOf("${book.name}_${book.author}", "images", it.first)
)?.writeBytes(context, vFile.readBytes())
}
}
}
private fun export(file: File, book: Book) {
private suspend fun export(file: File, book: Book) {
val filename = "${book.name} by ${book.author}.txt"
FileUtils.createFileIfNotExist(file, filename)
.writeText(getAllContents(book))
if(App.INSTANCE.getPrefBoolean(PreferKey.webDavCacheBackup,false)) {
if (App.INSTANCE.getPrefBoolean(PreferKey.webDavCacheBackup, false)) {
WebDavHelp.exportWebDav(file.absolutePath, filename) // 导出到webdav
}
App.db.bookChapterDao().getChapterList(book.bookUrl).forEach { chapter ->
BookHelp.getContent(book, chapter).let { content ->
content?.split("\n")?.forEachIndexed { index, text ->
val matcher = AppPattern.imgPattern.matcher(text)
if (matcher.find()) {
var src = matcher.group(1)
src = NetworkUtils.getAbsoluteURL(chapter.url, src)
src?.let {
val vFile = BookHelp.getImage(book, src)
getSrcList(book).forEach {
val vFile = BookHelp.getImage(book, it.third)
if (vFile.exists()) {
FileUtils.createFileIfNotExist(file,
FileUtils.createFileIfNotExist(
file,
"${book.name}_${book.author}",
"images",
chapter.title,
"${index}-${MD5Utils.md5Encode16(src)}.jpg")
.writeBytes(vFile.readBytes())
}
}
}
}
it.first,
"${it.second}-${MD5Utils.md5Encode16(it.third)}.jpg"
).writeBytes(vFile.readBytes())
}
}
}
private fun getAllContents(book: Book): String {
private suspend fun getAllContents(book: Book): String {
val contentProcessor = ContentProcessor(book.name, book.origin)
val stringBuilder = StringBuilder()
stringBuilder.append(book.name)
.append("\n")
.append(context.getString(R.string.author_show, book.author))
App.db.bookChapterDao().getChapterList(book.bookUrl).forEach { chapter ->
BookHelp.getContent(book, chapter).let {
BookHelp.getContent(book, chapter).let { content ->
val content1 = content?.let {
contentProcessor.getContent(book, chapter.title, it, false)
}
stringBuilder.append("\n\n")
.append(chapter.title)
.append("\n")
.append(it)
.append(content1)
}
}
return stringBuilder.toString()
}
private fun getSrcList(book: Book): ArrayList<Triple<String, Int, String>> {
val srcList = arrayListOf<Triple<String, Int, String>>()
App.db.bookChapterDao().getChapterList(book.bookUrl).forEach { chapter ->
BookHelp.getContent(book, chapter)?.let { content ->
content.split("\n").forEachIndexed { index, text ->
val matcher = AppPattern.imgPattern.matcher(text)
if (matcher.find()) {
var src = matcher.group(1)
src = NetworkUtils.getAbsoluteURL(chapter.url, src)
src?.let {
srcList.add(Triple(chapter.title, index, src))
}
}
}
}
}
return srcList
}
}

@ -276,7 +276,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
*/
fun replaceRuleChanged() {
execute {
BookHelp.upReplaceRules()
ReadBook.contentProcessor?.upReplaceRules()
ReadBook.loadContent(resetPageOffset = false)
}
}

@ -24,7 +24,9 @@ import io.legado.app.utils.getViewModel
import io.legado.app.utils.observeEvent
import kotlinx.android.synthetic.main.activity_search_content.*
import kotlinx.android.synthetic.main.view_search.*
import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.jetbrains.anko.sdk27.listeners.onClick
@ -34,7 +36,6 @@ class SearchContentActivity :
override val viewModel: SearchContentViewModel
get() = getViewModel(SearchContentViewModel::class.java)
lateinit var adapter: SearchContentAdapter
private lateinit var mLayoutManager: UpLinearLayoutManager
private var searchResultCounts = 0
@ -97,7 +98,6 @@ class SearchContentActivity :
@SuppressLint("SetTextI18n")
private fun initBook() {
launch {
tv_current_search_info.text = "搜索结果:$searchResultCounts"
viewModel.book?.let {
initCacheFileNames(it)
@ -107,7 +107,6 @@ class SearchContentActivity :
}
}
}
}
private fun initCacheFileNames(book: Book) {
launch(Dispatchers.IO) {
@ -141,14 +140,13 @@ class SearchContentActivity :
var searchResults = listOf<SearchResult>()
launch(Dispatchers.Main) {
App.db.bookChapterDao().getChapterList(viewModel.bookUrl).map { chapter ->
val job = async(Dispatchers.IO) {
withContext(Dispatchers.IO) {
if (isLocalBook
|| adapter.cacheFileNames.contains(chapter.getFileName())
) {
searchResults = searchChapter(newText, chapter)
}
}
job.await()
if (searchResults.isNotEmpty()) {
searchResultList.addAll(searchResults)
refresh_progress_bar.isAutoLoading = false
@ -164,26 +162,27 @@ class SearchContentActivity :
private suspend fun searchChapter(query: String, chapter: BookChapter?): List<SearchResult> {
val searchResults: MutableList<SearchResult> = mutableListOf()
var positions: List<Int>
var replaceContents: List<String>? = null
var replaceContents: List<String>?
var totalContents: String
if (chapter != null) {
viewModel.book?.let { book ->
val bookContent = BookHelp.getContent(book, chapter)
if (bookContent != null) {
//搜索替换后的正文
val job = async(Dispatchers.IO) {
withContext(Dispatchers.IO) {
chapter.title = when (AppConfig.chineseConverterType) {
1 -> HanLP.convertToSimplifiedChinese(chapter.title)
2 -> HanLP.convertToTraditionalChinese(chapter.title)
else -> chapter.title
}
replaceContents = BookHelp.disposeContent(book, chapter.title, bookContent)
}
job.await()
while (replaceContents == null) {
delay(100L)
replaceContents =
viewModel.contentProcessor!!.getContent(
book,
chapter.title,
bookContent
)
}
totalContents = replaceContents!!.joinToString("")
totalContents = replaceContents?.joinToString("") ?: bookContent
positions = searchPosition(totalContents, query)
var count = 1
positions.map {
@ -241,7 +240,6 @@ class SearchContentActivity :
get() = viewModel.book?.isLocalBook() == true
override fun openSearchResult(searchResult: SearchResult) {
val searchData = Intent()
searchData.putExtra("index", searchResult.chapterIndex)
searchData.putExtra("contentPosition", searchResult.contentPosition)

@ -5,16 +5,21 @@ import android.app.Application
import io.legado.app.App
import io.legado.app.base.BaseViewModel
import io.legado.app.data.entities.Book
import io.legado.app.help.ContentProcessor
class SearchContentViewModel(application: Application) : BaseViewModel(application) {
var bookUrl: String = ""
var book: Book? = null
var contentProcessor: ContentProcessor? = null
var lastQuery: String = ""
fun initBook(bookUrl: String, success: () -> Unit) {
this.bookUrl = bookUrl
execute {
book = App.db.bookDao().getBook(bookUrl)
book?.let {
contentProcessor = ContentProcessor(it.name, it.origin)
}
}.onSuccess {
success.invoke()
}

@ -19,12 +19,12 @@ import io.legado.app.R
import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.AppPattern
import io.legado.app.data.entities.ReplaceRule
import io.legado.app.help.BookHelp
import io.legado.app.help.IntentDataHelp
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.theme.ATH
import io.legado.app.lib.theme.primaryTextColor
import io.legado.app.service.help.ReadBook
import io.legado.app.ui.association.ImportReplaceRuleActivity
import io.legado.app.ui.filepicker.FilePicker
import io.legado.app.ui.filepicker.FilePickerDialog
@ -277,7 +277,7 @@ class ReplaceRuleActivity :
override fun onDestroy() {
super.onDestroy()
Coroutine.async { BookHelp.upReplaceRules() }
Coroutine.async { ReadBook.contentProcessor?.upReplaceRules() }
}
override fun upCountView() {

Loading…
Cancel
Save