Merge pull request #467 from gedoor/master

修复换源不获取详情页最新章节的bug
pull/481/head
Antecer 4 years ago committed by GitHub
commit 413011e2ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      app/src/main/assets/updateLog.md
  2. 3
      app/src/main/java/io/legado/app/help/AppConfig.kt
  3. 20
      app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt
  4. 2
      app/src/main/java/io/legado/app/model/webBook/BookInfo.kt
  5. 12
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceDialog.kt
  6. 119
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceViewModel.kt
  7. 4
      app/src/main/java/io/legado/app/ui/rss/article/RssArticlesFragment.kt
  8. 1
      app/src/main/java/io/legado/app/ui/rss/article/RssArticlesViewModel.kt
  9. 2
      app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditActivity.kt
  10. 26
      app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditViewModel.kt
  11. 8
      app/src/main/res/menu/change_source.xml

@ -3,7 +3,7 @@
* 关注合作公众号 **[小说拾遗]()** 获取好看的小说。
* 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。
**2020/11/06**
**2020/11/07**
* 详情页菜单添加拷贝URL
* 解决一些书名太长缓存报错的bug
* 添加备份搜索记录

@ -131,5 +131,8 @@ object AppConfig {
App.INSTANCE.getPrefBoolean(PreferKey.replaceEnableDefault, true)
}
val changeSourceLoadInfo get() = App.INSTANCE.getPrefBoolean(PreferKey.changeSourceLoadToc)
val changeSourceLoadToc get() = App.INSTANCE.getPrefBoolean(PreferKey.changeSourceLoadToc)
}

@ -17,8 +17,7 @@ import javax.script.SimpleBindings
import kotlin.collections.HashMap
/**
* Created by REFGD.
* 统一解析接口
* 解析规则获取结果
*/
@Keep
@Suppress("unused", "RegExpRedundantEscape")
@ -38,11 +37,11 @@ class AnalyzeRule(var book: BaseBook? = null) : JsExtensions {
private var objectChangedJS = false
private var objectChangedJP = false
@Throws(Exception::class)
fun setContent(content: Any?): AnalyzeRule {
fun setContent(content: Any?, baseUrl: String? = null): AnalyzeRule {
if (content == null) throw AssertionError("Content cannot be null")
isJSON = content.toString().isJson()
this.content = content
setBaseUrl(baseUrl)
isJSON = content.toString().isJson()
objectChangedXP = true
objectChangedJS = true
objectChangedJP = true
@ -50,8 +49,8 @@ class AnalyzeRule(var book: BaseBook? = null) : JsExtensions {
}
fun setBaseUrl(baseUrl: String?): AnalyzeRule {
this.baseUrl = baseUrl
baseUrl?.let {
this.baseUrl = baseUrl
try {
baseURL = URL(baseUrl.substringBefore(","))
} catch (e: Exception) {
@ -118,7 +117,6 @@ class AnalyzeRule(var book: BaseBook? = null) : JsExtensions {
/**
* 获取文本列表
*/
@Throws(Exception::class)
@JvmOverloads
fun getStringList(rule: String?, isUrl: Boolean = false): List<String>? {
if (rule.isNullOrEmpty()) return null
@ -126,7 +124,6 @@ class AnalyzeRule(var book: BaseBook? = null) : JsExtensions {
return getStringList(ruleList, isUrl)
}
@Throws(Exception::class)
fun getStringList(ruleList: List<SourceRule>, isUrl: Boolean = false): List<String>? {
var result: Any? = null
val content = this.content
@ -184,14 +181,12 @@ class AnalyzeRule(var book: BaseBook? = null) : JsExtensions {
/**
* 获取文本
*/
@Throws(Exception::class)
fun getString(ruleStr: String?, isUrl: Boolean = false): String {
if (TextUtils.isEmpty(ruleStr)) return ""
val ruleList = splitSourceRule(ruleStr)
return getString(ruleList, isUrl)
}
@Throws(Exception::class)
@JvmOverloads
fun getString(ruleList: List<SourceRule>, isUrl: Boolean = false): String {
var result: Any? = null
@ -244,7 +239,6 @@ class AnalyzeRule(var book: BaseBook? = null) : JsExtensions {
/**
* 获取Element
*/
@Throws(Exception::class)
fun getElement(ruleStr: String): Any? {
if (TextUtils.isEmpty(ruleStr)) return null
var result: Any? = null
@ -278,7 +272,6 @@ class AnalyzeRule(var book: BaseBook? = null) : JsExtensions {
* 获取列表
*/
@Suppress("UNCHECKED_CAST")
@Throws(Exception::class)
fun getElements(ruleStr: String): List<Any> {
var result: Any? = null
val ruleList = splitSourceRule(ruleStr)
@ -313,7 +306,6 @@ class AnalyzeRule(var book: BaseBook? = null) : JsExtensions {
/**
* 保存变量
*/
@Throws(Exception::class)
private fun putRule(map: Map<String, String>) {
for ((key, value) in map) {
put(key, getString(value))
@ -323,7 +315,6 @@ class AnalyzeRule(var book: BaseBook? = null) : JsExtensions {
/**
* 分离put规则
*/
@Throws(Exception::class)
private fun splitPutRule(ruleStr: String, putMap: HashMap<String, String>): String {
var vRuleStr = ruleStr
val putMatcher = putPattern.matcher(vRuleStr)
@ -359,7 +350,6 @@ class AnalyzeRule(var book: BaseBook? = null) : JsExtensions {
/**
* 分解规则生成规则列表
*/
@Throws(Exception::class)
fun splitSourceRule(ruleStr: String?, mode: Mode = Mode.Default): List<SourceRule> {
var vRuleStr = ruleStr
val ruleList = ArrayList<SourceRule>()

@ -29,7 +29,7 @@ object BookInfo {
val analyzeRule = AnalyzeRule(book)
analyzeRule.setContent(body).setBaseUrl(baseUrl)
infoRule.init?.let {
if (it.isNotEmpty()) {
if (it.isNotBlank()) {
Debug.log(bookSource.bookSourceUrl, "≡执行详情页初始化规则")
analyzeRule.setContent(analyzeRule.getElement(it))
}

@ -16,9 +16,13 @@ import io.legado.app.base.BaseDialogFragment
import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.SearchBook
import io.legado.app.help.AppConfig
import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.*
import io.legado.app.utils.applyTint
import io.legado.app.utils.getSize
import io.legado.app.utils.getViewModel
import io.legado.app.utils.putPrefBoolean
import kotlinx.android.synthetic.main.dialog_change_source.*
@ -81,10 +85,8 @@ class ChangeSourceDialog : BaseDialogFragment(),
tool_bar.inflateMenu(R.menu.change_source)
tool_bar.menu.applyTint(requireContext())
tool_bar.setOnMenuItemClickListener(this)
tool_bar.menu.findItem(R.id.menu_load_toc)?.isChecked =
getPrefBoolean(PreferKey.changeSourceLoadToc)
tool_bar.menu.findItem(R.id.menu_load_info)?.isChecked =
getPrefBoolean(PreferKey.changeSourceLoadInfo)
tool_bar.menu.findItem(R.id.menu_load_info)?.isChecked = AppConfig.changeSourceLoadInfo
tool_bar.menu.findItem(R.id.menu_load_toc)?.isChecked = AppConfig.changeSourceLoadToc
}
private fun initRecyclerView() {

@ -23,7 +23,6 @@ import kotlinx.coroutines.asCoroutineDispatcher
import org.jetbrains.anko.debug
import java.util.concurrent.CopyOnWriteArraySet
import java.util.concurrent.Executors
import kotlin.math.min
class ChangeSourceViewModel(application: Application) : BaseViewModel(application) {
private val threadCount = AppConfig.threadCount
@ -39,6 +38,7 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
private val searchBooks = CopyOnWriteArraySet<SearchBook>()
private var postTime = 0L
private val sendRunnable = Runnable { upAdapter() }
@Volatile
private var searchIndex = -1
@ -113,80 +113,73 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
return
}
searchIndex++
val source = bookSourceList[searchIndex]
val variableBook = SearchBook()
val task = WebBook(source)
.searchBook(name, variableBook = variableBook, scope = this, context = searchPool!!)
.timeout(60000L)
.onSuccess(IO) {
it.forEach { searchBook ->
if (searchBook.name == name && searchBook.author == author) {
if (searchBook.latestChapterTitle.isNullOrEmpty()) {
if (context.getPrefBoolean(PreferKey.changeSourceLoadInfo) || context.getPrefBoolean(PreferKey.changeSourceLoadToc)) {
loadBookInfo(searchBook.toBook())
} else {
searchFinish(searchBook)
}
}
val source = bookSourceList[searchIndex]
val variableBook = SearchBook()
val webBook = WebBook(source)
val task = webBook
.searchBook(name, variableBook = variableBook, scope = this, context = searchPool!!)
.timeout(60000L)
.onSuccess(IO) {
it.forEach { searchBook ->
if (searchBook.name == name && searchBook.author == author) {
if (searchBook.latestChapterTitle.isNullOrEmpty()) {
if (AppConfig.changeSourceLoadInfo || AppConfig.changeSourceLoadToc) {
loadBookInfo(webBook, searchBook.toBook())
} else {
searchFinish(searchBook)
}
return@forEach
} else {
searchFinish(searchBook)
}
return@onSuccess
}
}
.onFinally {
synchronized(this) {
if (searchIndex < bookSourceList.lastIndex) {
search()
} else {
searchIndex++
}
if (searchIndex >= bookSourceList.lastIndex + min(bookSourceList.size,
threadCount)
) {
searchStateData.postValue(false)
}
}
.onFinally {
synchronized(this) {
if (searchIndex < bookSourceList.lastIndex) {
search()
} else {
searchIndex++
}
if (searchIndex >= bookSourceList.lastIndex + bookSourceList.size
|| searchIndex >= bookSourceList.lastIndex + threadCount
) {
searchStateData.postValue(false)
}
}
tasks.add(task)
}
}
tasks.add(task)
}
private fun loadBookInfo(book: Book) {
execute {
App.db.bookSourceDao().getBookSource(book.origin)?.let { bookSource ->
WebBook(bookSource).getBookInfo(book, this)
.onSuccess {
if (context.getPrefBoolean(PreferKey.changeSourceLoadToc)) {
loadChapter(it)
} else {
//从详情页里获取最新章节
book.latestChapterTitle = it.latestChapterTitle
val searchBook = book.toSearchBook()
searchFinish(searchBook)
}
}.onError {
debug { context.getString(R.string.error_get_book_info) }
}
} ?: debug { context.getString(R.string.error_no_source) }
}
private fun loadBookInfo(webBook: WebBook, book: Book) {
webBook.getBookInfo(book, this)
.onSuccess {
if (context.getPrefBoolean(PreferKey.changeSourceLoadToc)) {
loadChapter(webBook, book)
} else {
//从详情页里获取最新章节
book.latestChapterTitle = it.latestChapterTitle
val searchBook = book.toSearchBook()
searchFinish(searchBook)
}
}.onError {
debug { context.getString(R.string.error_get_book_info) }
}
}
private fun loadChapter(book: Book) {
execute {
App.db.bookSourceDao().getBookSource(book.origin)?.let { bookSource ->
WebBook(bookSource).getChapterList(book, this)
.onSuccess(IO) { chapters ->
if (chapters.isNotEmpty()) {
book.latestChapterTitle = chapters.last().title
val searchBook: SearchBook = book.toSearchBook()
searchFinish(searchBook)
}
}.onError {
debug { context.getString(R.string.error_get_chapter_list) }
}
} ?: debug { R.string.error_no_source }
}
private fun loadChapter(webBook: WebBook, book: Book) {
webBook.getChapterList(book, this)
.onSuccess(IO) { chapters ->
if (chapters.isNotEmpty()) {
book.latestChapterTitle = chapters.last().title
val searchBook: SearchBook = book.toSearchBook()
searchFinish(searchBook)
}
}.onError {
debug { context.getString(R.string.error_get_chapter_list) }
}
}
/**

@ -103,14 +103,14 @@ class RssArticlesFragment : VMBaseFragment<RssArticlesViewModel>(R.layout.fragme
}
override fun observeLiveBus() {
viewModel.loadFinally.observe(viewLifecycleOwner, {
viewModel.loadFinally.observe(viewLifecycleOwner) {
refresh_recycler_view.stopLoading()
if (it) {
loadMoreView.startLoad()
} else {
loadMoreView.noMore()
}
})
}
}
override fun readRss(rssArticle: RssArticle) {

@ -49,6 +49,7 @@ class RssArticlesViewModel(application: Application) : BaseViewModel(application
isLoading = false
}
}.onError {
loadFinally.postValue(false)
it.printStackTrace()
toast(it.localizedMessage)
}

@ -136,7 +136,7 @@ class RssSourceEditActivity :
}
private fun getRssSource(): RssSource {
val source = viewModel.rssSource?.copy() ?: RssSource()
val source = viewModel.rssSource
source.enabled = cb_is_enable.isChecked
source.enableJs = cb_enable_js.isChecked
source.loadWithBaseUrl = cb_enable_base_url.isChecked

@ -8,24 +8,23 @@ import io.legado.app.data.entities.RssSource
import io.legado.app.utils.GSON
import io.legado.app.utils.fromJsonObject
import io.legado.app.utils.getClipText
import io.legado.app.utils.msg
import kotlinx.coroutines.Dispatchers
class RssSourceEditViewModel(application: Application) : BaseViewModel(application) {
var rssSource: RssSource? = null
private var oldSourceUrl: String? = null
var rssSource: RssSource = RssSource()
private var oldSourceUrl: String = ""
fun initData(intent: Intent, onFinally: () -> Unit) {
execute {
val key = intent.getStringExtra("data")
var source: RssSource? = null
if (key != null) {
source = App.db.rssSourceDao().getByKey(key)
}
source?.let {
oldSourceUrl = it.sourceUrl
rssSource = it
App.db.rssSourceDao().getByKey(key)?.let {
rssSource = it
}
}
oldSourceUrl = rssSource.sourceUrl
}.onFinally {
onFinally()
}
@ -33,14 +32,11 @@ class RssSourceEditViewModel(application: Application) : BaseViewModel(applicati
fun save(source: RssSource, success: (() -> Unit)) {
execute {
oldSourceUrl?.let {
if (oldSourceUrl != source.sourceUrl) {
App.db.rssSourceDao().delete(it)
}
if (oldSourceUrl != source.sourceUrl) {
App.db.rssSourceDao().delete(oldSourceUrl)
oldSourceUrl = source.sourceUrl
}
oldSourceUrl = source.sourceUrl
App.db.rssSourceDao().insert(source)
rssSource = source
}.onSuccess {
success()
}.onError {
@ -74,7 +70,7 @@ class RssSourceEditViewModel(application: Application) : BaseViewModel(applicati
finally.invoke(it)
}
}.onError {
toast(it.localizedMessage ?: "Error")
toast(it.msg)
}
}

@ -19,14 +19,14 @@
tools:ignore="AlwaysShowAction" />
<item
android:id="@+id/menu_load_toc"
android:title="@string/load_toc"
android:id="@+id/menu_load_info"
android:title="@string/load_info"
android:checkable="true"
app:showAsAction="never" />
<item
android:id="@+id/menu_load_info"
android:title="@string/load_info"
android:id="@+id/menu_load_toc"
android:title="@string/load_toc"
android:checkable="true"
app:showAsAction="never" />

Loading…
Cancel
Save