pull/1282/head
gedoor 3 years ago
parent 0a2d903899
commit 7c08da40ba
  1. 10
      app/src/main/java/io/legado/app/api/controller/BookController.kt
  2. 60
      app/src/main/java/io/legado/app/model/Debug.kt
  3. 14
      app/src/main/java/io/legado/app/model/ReadBook.kt
  4. 9
      app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt
  5. 7
      app/src/main/java/io/legado/app/model/webBook/BookContent.kt
  6. 7
      app/src/main/java/io/legado/app/model/webBook/BookInfo.kt
  7. 9
      app/src/main/java/io/legado/app/model/webBook/BookList.kt
  8. 5
      app/src/main/java/io/legado/app/model/webBook/PreciseSearch.kt
  9. 3
      app/src/main/java/io/legado/app/model/webBook/SearchBookModel.kt
  10. 100
      app/src/main/java/io/legado/app/model/webBook/WebBook.kt
  11. 7
      app/src/main/java/io/legado/app/service/AudioPlayService.kt
  12. 27
      app/src/main/java/io/legado/app/service/CacheBookService.kt
  13. 15
      app/src/main/java/io/legado/app/service/CheckSourceService.kt
  14. 6
      app/src/main/java/io/legado/app/service/help/AudioPlay.kt
  15. 5
      app/src/main/java/io/legado/app/service/help/CacheBook.kt
  16. 42
      app/src/main/java/io/legado/app/ui/book/audio/AudioPlayViewModel.kt
  17. 4
      app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverViewModel.kt
  18. 17
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceViewModel.kt
  19. 2
      app/src/main/java/io/legado/app/ui/book/explore/ExploreShowViewModel.kt
  20. 4
      app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt
  21. 6
      app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
  22. 52
      app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt
  23. 2
      app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt
  24. 9
      app/src/main/java/io/legado/app/ui/book/source/debug/BookSourceDebugModel.kt
  25. 12
      app/src/main/java/io/legado/app/ui/main/MainViewModel.kt
  26. 2
      app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt
  27. 3
      app/src/main/java/io/legado/app/web/SourceDebugWebSocket.kt

@ -81,12 +81,11 @@ object BookController {
} else {
val bookSource = appDb.bookSourceDao.getBookSource(book.origin)
?: return returnData.setErrorMsg("未找到对应书源,请换源")
val webBook = WebBook(bookSource)
val toc = runBlocking {
if (book.tocUrl.isBlank()) {
webBook.getBookInfoAwait(this, book)
WebBook.getBookInfoAwait(this, bookSource, book)
}
webBook.getChapterListAwait(this, book)
WebBook.getChapterListAwait(this, bookSource, book)
}
appDb.bookChapterDao.delByBook(book.bookUrl)
appDb.bookChapterDao.insert(*toc.toTypedArray())
@ -149,13 +148,12 @@ object BookController {
?: return returnData.setErrorMsg("未找到书源")
try {
content = runBlocking {
WebBook(bookSource).getContentAwait(this, book, chapter)
WebBook.getContentAwait(this, bookSource, book, chapter)
}
val contentProcessor = ContentProcessor.get(book.name, book.origin)
saveBookReadIndex(book, index)
returnData.setData(
contentProcessor.getContent(book, chapter.title, content)
.joinToString("\n")
contentProcessor.getContent(book, chapter.title, content).joinToString("\n")
)
} catch (e: Exception) {
returnData.setErrorMsg(e.msg)

@ -143,56 +143,56 @@ object Debug {
}
}
fun startDebug(scope: CoroutineScope, webBook: WebBook, key: String) {
fun startDebug(scope: CoroutineScope, bookSource: BookSource, key: String) {
cancelDebug()
debugSource = webBook.sourceUrl
debugSource = bookSource.bookSourceUrl
startTime = System.currentTimeMillis()
when {
key.isAbsUrl() -> {
val book = Book()
book.origin = webBook.sourceUrl
book.origin = bookSource.bookSourceUrl
book.bookUrl = key
log(webBook.sourceUrl, "⇒开始访问详情页:$key")
infoDebug(scope, webBook, book)
log(bookSource.bookSourceUrl, "⇒开始访问详情页:$key")
infoDebug(scope, bookSource, book)
}
key.contains("::") -> {
val url = key.substringAfter("::")
log(webBook.sourceUrl, "⇒开始访问发现页:$url")
exploreDebug(scope, webBook, url)
log(bookSource.bookSourceUrl, "⇒开始访问发现页:$url")
exploreDebug(scope, bookSource, url)
}
key.startsWith("++") -> {
val url = key.substring(2)
val book = Book()
book.origin = webBook.sourceUrl
book.origin = bookSource.bookSourceUrl
book.tocUrl = url
log(webBook.sourceUrl, "⇒开始访目录页:$url")
tocDebug(scope, webBook, book)
log(bookSource.bookSourceUrl, "⇒开始访目录页:$url")
tocDebug(scope, bookSource, book)
}
key.startsWith("--") -> {
val url = key.substring(2)
val book = Book()
book.origin = webBook.sourceUrl
log(webBook.sourceUrl, "⇒开始访正文页:$url")
book.origin = bookSource.bookSourceUrl
log(bookSource.bookSourceUrl, "⇒开始访正文页:$url")
val chapter = BookChapter()
chapter.title = "调试"
chapter.url = url
contentDebug(scope, webBook, book, chapter, null)
contentDebug(scope, bookSource, book, chapter, null)
}
else -> {
log(webBook.sourceUrl, "⇒开始搜索关键字:$key")
searchDebug(scope, webBook, key)
log(bookSource.bookSourceUrl, "⇒开始搜索关键字:$key")
searchDebug(scope, bookSource, key)
}
}
}
private fun exploreDebug(scope: CoroutineScope, webBook: WebBook, url: String) {
private fun exploreDebug(scope: CoroutineScope, bookSource: BookSource, url: String) {
log(debugSource, "︾开始解析发现页")
val explore = webBook.exploreBook(scope, url, 1)
val explore = WebBook.exploreBook(scope, bookSource, url, 1)
.onSuccess { exploreBooks ->
if (exploreBooks.isNotEmpty()) {
log(debugSource, "︽发现页解析完成")
log(debugSource, showTime = false)
infoDebug(scope, webBook, exploreBooks[0].toBook())
infoDebug(scope, bookSource, exploreBooks[0].toBook())
} else {
log(debugSource, "︽未获取到书籍", state = -1)
}
@ -203,14 +203,14 @@ object Debug {
tasks.add(explore)
}
private fun searchDebug(scope: CoroutineScope, webBook: WebBook, key: String) {
private fun searchDebug(scope: CoroutineScope, bookSource: BookSource, key: String) {
log(debugSource, "︾开始解析搜索页")
val search = webBook.searchBook(scope, key, 1)
val search = WebBook.searchBook(scope, bookSource, key, 1)
.onSuccess { searchBooks ->
if (searchBooks.isNotEmpty()) {
log(debugSource, "︽搜索页解析完成")
log(debugSource, showTime = false)
infoDebug(scope, webBook, searchBooks[0].toBook())
infoDebug(scope, bookSource, searchBooks[0].toBook())
} else {
log(debugSource, "︽未获取到书籍", state = -1)
}
@ -221,19 +221,19 @@ object Debug {
tasks.add(search)
}
private fun infoDebug(scope: CoroutineScope, webBook: WebBook, book: Book) {
private fun infoDebug(scope: CoroutineScope, bookSource: BookSource, book: Book) {
if (book.tocUrl.isNotBlank()) {
log(debugSource, "≡已获取目录链接,跳过详情页")
log(debugSource, showTime = false)
tocDebug(scope, webBook, book)
tocDebug(scope, bookSource, book)
return
}
log(debugSource, "︾开始解析详情页")
val info = webBook.getBookInfo(scope, book)
val info = WebBook.getBookInfo(scope, bookSource, book)
.onSuccess {
log(debugSource, "︽详情页解析完成")
log(debugSource, showTime = false)
tocDebug(scope, webBook, book)
tocDebug(scope, bookSource, book)
}
.onError {
log(debugSource, it.msg, state = -1)
@ -241,15 +241,15 @@ object Debug {
tasks.add(info)
}
private fun tocDebug(scope: CoroutineScope, webBook: WebBook, book: Book) {
private fun tocDebug(scope: CoroutineScope, bookSource: BookSource, book: Book) {
log(debugSource, "︾开始解析目录页")
val chapterList = webBook.getChapterList(scope, book)
val chapterList = WebBook.getChapterList(scope, bookSource, book)
.onSuccess {
if (it.isNotEmpty()) {
log(debugSource, "︽目录页解析完成")
log(debugSource, showTime = false)
val nextChapterUrl = it.getOrNull(1)?.url
contentDebug(scope, webBook, book, it[0], nextChapterUrl)
contentDebug(scope, bookSource, book, it[0], nextChapterUrl)
} else {
log(debugSource, "︽目录列表为空", state = -1)
}
@ -262,13 +262,13 @@ object Debug {
private fun contentDebug(
scope: CoroutineScope,
webBook: WebBook,
bookSource: BookSource,
book: Book,
bookChapter: BookChapter,
nextChapterUrl: String?
) {
log(debugSource, "︾开始解析正文页")
val content = webBook.getContent(scope, book, bookChapter, nextChapterUrl)
val content = WebBook.getContent(scope, bookSource, book, bookChapter, nextChapterUrl)
.onSuccess {
log(debugSource, "︽正文页解析完成", state = 1000)
}

@ -38,7 +38,6 @@ object ReadBook : CoroutineScope by MainScope() {
var curTextChapter: TextChapter? = null
var nextTextChapter: TextChapter? = null
var bookSource: BookSource? = null
var webBook: WebBook? = null
var msg: String? = null
private val loadingChapters = arrayListOf<Int>()
private val readRecord = ReadRecord()
@ -65,17 +64,14 @@ object ReadBook : CoroutineScope by MainScope() {
fun upWebBook(book: Book) {
if (book.origin == BookType.local) {
bookSource = null
webBook = null
} else {
appDb.bookSourceDao.getBookSource(book.origin)?.let {
bookSource = it
webBook = WebBook(it)
if (book.getImageStyle().isNullOrBlank()) {
book.setImageStyle(it.getContentRule().imageStyle)
}
} ?: let {
bookSource = null
webBook = null
}
}
}
@ -314,9 +310,9 @@ object ReadBook : CoroutineScope by MainScope() {
success: (() -> Unit)? = null
) {
val book = book
val webBook = webBook
if (book != null && webBook != null) {
CacheBook.download(scope, webBook, book, chapter)
val bookSource = bookSource
if (book != null && bookSource != null) {
CacheBook.download(scope, bookSource, book, chapter)
} else if (book != null) {
contentLoadFinish(
book, chapter, "没有书源", resetPageOffset = resetPageOffset
@ -394,11 +390,11 @@ object ReadBook : CoroutineScope by MainScope() {
@Synchronized
fun upToc() {
val webBook = webBook ?: return
val bookSource = bookSource ?: return
val book = book ?: return
if (System.currentTimeMillis() - book.lastCheckTime < 600000) return
book.lastCheckTime = System.currentTimeMillis()
webBook.getChapterList(this, book).onSuccess(IO) { cList ->
WebBook.getChapterList(this, bookSource, book).onSuccess(IO) { cList ->
if (book.bookUrl == ReadBook.book?.bookUrl
&& cList.size > chapterSize
) {

@ -6,7 +6,6 @@ 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.rule.TocRule
import io.legado.app.help.http.StrResponse
import io.legado.app.model.Debug
import io.legado.app.model.analyzeRule.AnalyzeRule
import io.legado.app.model.analyzeRule.AnalyzeUrl
@ -16,20 +15,18 @@ import kotlinx.coroutines.async
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.withContext
import splitties.init.appCtx
import java.net.URLDecoder
@Suppress("BlockingMethodInNonBlockingContext")
object BookChapterList {
suspend fun analyzeChapterList(
scope: CoroutineScope,
strResponse: StrResponse,
bookSource: BookSource,
book: Book,
redirectUrl: String
redirectUrl: String,
baseUrl: String,
body: String?
): List<BookChapter> {
val baseUrl = URLDecoder.decode(strResponse.url, "utf-8")
val body = strResponse.body
body ?: throw Exception(
appCtx.getString(R.string.error_get_web_content, baseUrl)
)

@ -7,7 +7,6 @@ import io.legado.app.data.entities.BookChapter
import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.rule.ContentRule
import io.legado.app.help.BookHelp
import io.legado.app.help.http.StrResponse
import io.legado.app.model.Debug
import io.legado.app.model.analyzeRule.AnalyzeRule
import io.legado.app.model.analyzeRule.AnalyzeUrl
@ -19,7 +18,6 @@ import kotlinx.coroutines.async
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.withContext
import splitties.init.appCtx
import java.net.URLDecoder
@Suppress("BlockingMethodInNonBlockingContext")
object BookContent {
@ -27,15 +25,14 @@ object BookContent {
@Throws(Exception::class)
suspend fun analyzeContent(
scope: CoroutineScope,
strResponse: StrResponse,
bookSource: BookSource,
book: Book,
bookChapter: BookChapter,
redirectUrl: String,
baseUrl: String,
body: String?,
nextChapterUrl: String? = null
): String {
val baseUrl = URLDecoder.decode(strResponse.url, "utf-8")
val body = strResponse.body
body ?: throw Exception(
appCtx.getString(R.string.error_get_web_content, baseUrl)
)

@ -4,7 +4,6 @@ import io.legado.app.R
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookSource
import io.legado.app.help.BookHelp
import io.legado.app.help.http.StrResponse
import io.legado.app.model.Debug
import io.legado.app.model.analyzeRule.AnalyzeRule
import io.legado.app.utils.HtmlFormatter
@ -13,21 +12,19 @@ import io.legado.app.utils.StringUtils.wordCountFormat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ensureActive
import splitties.init.appCtx
import java.net.URLDecoder
object BookInfo {
@Throws(Exception::class)
fun analyzeBookInfo(
scope: CoroutineScope,
strResponse: StrResponse,
bookSource: BookSource,
book: Book,
redirectUrl: String,
baseUrl: String,
body: String?,
canReName: Boolean,
) {
val baseUrl = URLDecoder.decode(strResponse.url, "utf-8")
val body = strResponse.body
body ?: throw Exception(
appCtx.getString(R.string.error_get_web_content, baseUrl)
)

@ -6,7 +6,6 @@ import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.SearchBook
import io.legado.app.data.entities.rule.BookListRule
import io.legado.app.help.BookHelp
import io.legado.app.help.http.StrResponse
import io.legado.app.model.Debug
import io.legado.app.model.analyzeRule.AnalyzeRule
import io.legado.app.model.analyzeRule.AnalyzeUrl
@ -16,21 +15,19 @@ import io.legado.app.utils.StringUtils.wordCountFormat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ensureActive
import splitties.init.appCtx
import java.net.URLDecoder
object BookList {
@Throws(Exception::class)
fun analyzeBookList(
scope: CoroutineScope,
strResponse: StrResponse,
bookSource: BookSource,
analyzeUrl: AnalyzeUrl,
variableBook: SearchBook,
analyzeUrl: AnalyzeUrl,
baseUrl: String,
body: String?,
isSearch: Boolean = true,
): ArrayList<SearchBook> {
val baseUrl = URLDecoder.decode(strResponse.url, "utf-8")
val body = strResponse.body
body ?: throw Exception(
appCtx.getString(
R.string.error_get_web_content,

@ -17,15 +17,14 @@ object PreciseSearch {
author: String
): Book? {
bookSources.forEach { bookSource ->
val webBook = WebBook(bookSource)
kotlin.runCatching {
if (!scope.isActive) return null
webBook.searchBookAwait(scope, name).firstOrNull {
WebBook.searchBookAwait(scope, bookSource, name).firstOrNull {
it.name == name && it.author == author
}?.let {
return if (it.tocUrl.isBlank()) {
if (!scope.isActive) return null
webBook.getBookInfoAwait(scope, it.toBook())
WebBook.getBookInfoAwait(scope, bookSource, it.toBook())
} else {
it.toBook()
}

@ -67,8 +67,9 @@ class SearchBookModel(private val scope: CoroutineScope, private val callBack: C
}
searchIndex++
val source = bookSourceList[searchIndex]
val task = WebBook(source).searchBook(
val task = WebBook.searchBook(
scope,
source,
searchKey,
searchPage,
context = searchPool!!

@ -13,27 +13,26 @@ import kotlinx.coroutines.Dispatchers
import kotlin.coroutines.CoroutineContext
@Suppress("MemberVisibilityCanBePrivate")
class WebBook(val bookSource: BookSource) {
val sourceUrl: String
get() = bookSource.bookSourceUrl
object WebBook {
/**
* 搜索
*/
fun searchBook(
scope: CoroutineScope,
bookSource: BookSource,
key: String,
page: Int? = 1,
context: CoroutineContext = Dispatchers.IO,
): Coroutine<ArrayList<SearchBook>> {
return Coroutine.async(scope, context) {
searchBookAwait(scope, key, page)
searchBookAwait(scope, bookSource, key, page)
}
}
suspend fun searchBookAwait(
scope: CoroutineScope,
bookSource: BookSource,
key: String,
page: Int? = 1,
): ArrayList<SearchBook> {
@ -43,7 +42,7 @@ class WebBook(val bookSource: BookSource) {
ruleUrl = searchUrl,
key = key,
page = page,
baseUrl = sourceUrl,
baseUrl = bookSource.bookSourceUrl,
headerMapF = bookSource.getHeaderMap(true),
book = variableBook,
source = bookSource
@ -55,7 +54,15 @@ class WebBook(val bookSource: BookSource) {
res = analyzeUrl.evalJS(checkJs) as StrResponse
}
}
return BookList.analyzeBookList(scope, res, bookSource, analyzeUrl, variableBook, true)
return BookList.analyzeBookList(
scope,
bookSource,
variableBook,
analyzeUrl,
res.url,
res.body,
true
)
}
return arrayListOf()
}
@ -65,17 +72,19 @@ class WebBook(val bookSource: BookSource) {
*/
fun exploreBook(
scope: CoroutineScope,
bookSource: BookSource,
url: String,
page: Int? = 1,
context: CoroutineContext = Dispatchers.IO,
): Coroutine<List<SearchBook>> {
return Coroutine.async(scope, context) {
exploreBookAwait(scope, url, page)
exploreBookAwait(scope, bookSource, url, page)
}
}
suspend fun exploreBookAwait(
scope: CoroutineScope,
bookSource: BookSource,
url: String,
page: Int? = 1,
): ArrayList<SearchBook> {
@ -83,7 +92,7 @@ class WebBook(val bookSource: BookSource) {
val analyzeUrl = AnalyzeUrl(
ruleUrl = url,
page = page,
baseUrl = sourceUrl,
baseUrl = bookSource.bookSourceUrl,
book = variableBook,
source = bookSource,
headerMapF = bookSource.getHeaderMap(true)
@ -95,7 +104,15 @@ class WebBook(val bookSource: BookSource) {
res = analyzeUrl.evalJS(checkJs) as StrResponse
}
}
return BookList.analyzeBookList(scope, res, bookSource, analyzeUrl, variableBook, false)
return BookList.analyzeBookList(
scope,
bookSource,
variableBook,
analyzeUrl,
res.url,
res.body,
false
)
}
/**
@ -103,28 +120,37 @@ class WebBook(val bookSource: BookSource) {
*/
fun getBookInfo(
scope: CoroutineScope,
bookSource: BookSource,
book: Book,
context: CoroutineContext = Dispatchers.IO,
canReName: Boolean = true,
): Coroutine<Book> {
return Coroutine.async(scope, context) {
getBookInfoAwait(scope, book, canReName)
getBookInfoAwait(scope, bookSource, book, canReName)
}
}
suspend fun getBookInfoAwait(
scope: CoroutineScope,
bookSource: BookSource,
book: Book,
canReName: Boolean = true,
): Book {
book.type = bookSource.bookSourceType
if (!book.infoHtml.isNullOrEmpty()) {
val strResponse = StrResponse(book.bookUrl, book.infoHtml)
BookInfo.analyzeBookInfo(scope, strResponse, bookSource, book, book.bookUrl, canReName)
BookInfo.analyzeBookInfo(
scope,
bookSource,
book,
book.bookUrl,
book.bookUrl,
book.infoHtml,
canReName
)
} else {
val analyzeUrl = AnalyzeUrl(
ruleUrl = book.bookUrl,
baseUrl = sourceUrl,
baseUrl = bookSource.bookSourceUrl,
book = book,
source = bookSource,
headerMapF = bookSource.getHeaderMap(true)
@ -136,7 +162,15 @@ class WebBook(val bookSource: BookSource) {
res = analyzeUrl.evalJS(checkJs) as StrResponse
}
}
BookInfo.analyzeBookInfo(scope, res, bookSource, book, book.bookUrl, canReName)
BookInfo.analyzeBookInfo(
scope,
bookSource,
book,
book.bookUrl,
res.url,
res.body,
canReName
)
}
return book
}
@ -146,22 +180,30 @@ class WebBook(val bookSource: BookSource) {
*/
fun getChapterList(
scope: CoroutineScope,
bookSource: BookSource,
book: Book,
context: CoroutineContext = Dispatchers.IO
): Coroutine<List<BookChapter>> {
return Coroutine.async(scope, context) {
getChapterListAwait(scope, book)
getChapterListAwait(scope, bookSource, book)
}
}
suspend fun getChapterListAwait(
scope: CoroutineScope,
bookSource: BookSource,
book: Book,
): List<BookChapter> {
book.type = bookSource.bookSourceType
return if (book.bookUrl == book.tocUrl && !book.tocHtml.isNullOrEmpty()) {
val strResponse = StrResponse(book.tocUrl, book.tocHtml)
BookChapterList.analyzeChapterList(scope, strResponse, bookSource, book, book.tocUrl)
BookChapterList.analyzeChapterList(
scope,
bookSource,
book,
book.tocUrl,
book.tocUrl,
book.tocHtml
)
} else {
val analyzeUrl = AnalyzeUrl(
ruleUrl = book.tocUrl,
@ -177,7 +219,14 @@ class WebBook(val bookSource: BookSource) {
res = analyzeUrl.evalJS(checkJs) as StrResponse
}
}
BookChapterList.analyzeChapterList(scope, res, bookSource, book, book.tocUrl)
BookChapterList.analyzeChapterList(
scope,
bookSource,
book,
book.tocUrl,
res.url,
res.body
)
}
}
@ -186,35 +235,37 @@ class WebBook(val bookSource: BookSource) {
*/
fun getContent(
scope: CoroutineScope,
bookSource: BookSource,
book: Book,
bookChapter: BookChapter,
nextChapterUrl: String? = null,
context: CoroutineContext = Dispatchers.IO
): Coroutine<String> {
return Coroutine.async(scope, context) {
getContentAwait(scope, book, bookChapter, nextChapterUrl)
getContentAwait(scope, bookSource, book, bookChapter, nextChapterUrl)
}
}
suspend fun getContentAwait(
scope: CoroutineScope,
bookSource: BookSource,
book: Book,
bookChapter: BookChapter,
nextChapterUrl: String? = null,
): String {
if (bookSource.getContentRule().content.isNullOrEmpty()) {
Debug.log(sourceUrl, "⇒正文规则为空,使用章节链接:${bookChapter.url}")
Debug.log(bookSource.bookSourceUrl, "⇒正文规则为空,使用章节链接:${bookChapter.url}")
return bookChapter.url
}
return if (bookChapter.url == book.bookUrl && !book.tocHtml.isNullOrEmpty()) {
val strResponse = StrResponse(bookChapter.getAbsoluteURL(), book.tocHtml)
BookContent.analyzeContent(
scope,
strResponse,
bookSource,
book,
bookChapter,
bookChapter.getAbsoluteURL(),
bookChapter.getAbsoluteURL(),
book.tocHtml,
nextChapterUrl
)
} else {
@ -239,11 +290,12 @@ class WebBook(val bookSource: BookSource) {
}
BookContent.analyzeContent(
scope,
res,
bookSource,
book,
bookChapter,
bookChapter.getAbsoluteURL(),
res.url,
res.body,
nextChapterUrl
)
}

@ -26,6 +26,7 @@ import io.legado.app.data.entities.BookChapter
import io.legado.app.help.IntentHelp
import io.legado.app.help.MediaHelp
import io.legado.app.model.analyzeRule.AnalyzeUrl
import io.legado.app.model.webBook.WebBook
import io.legado.app.receiver.MediaButtonReceiver
import io.legado.app.service.help.AudioPlay
import io.legado.app.service.help.ReadAloud
@ -288,9 +289,9 @@ class AudioPlayService : BaseService(),
durChapter?.let { chapter ->
if (addLoading(durChapterIndex)) {
val book = AudioPlay.book
val webBook = AudioPlay.webBook
if (book != null && webBook != null) {
webBook.getContent(this@AudioPlayService, book, chapter)
val bookSource = AudioPlay.bookSource
if (book != null && bookSource != null) {
WebBook.getContent(this@AudioPlayService, bookSource, book, chapter)
.onSuccess { content ->
if (content.isEmpty()) {
withContext(Main) {

@ -10,6 +10,7 @@ import io.legado.app.constant.IntentAction
import io.legado.app.data.appDb
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.help.AppConfig
import io.legado.app.help.BookHelp
import io.legado.app.help.IntentHelp
@ -35,7 +36,7 @@ class CacheBookService : BaseService() {
Executors.newFixedThreadPool(min(threadCount, 8)).asCoroutineDispatcher()
private var tasks = CompositeCoroutine()
private val bookMap = ConcurrentHashMap<String, Book>()
private val webBookMap = ConcurrentHashMap<String, WebBook>()
private val bookSourceMap = ConcurrentHashMap<String, BookSource>()
private val downloadMap = ConcurrentHashMap<String, CopyOnWriteArraySet<BookChapter>>()
private val downloadCount = ConcurrentHashMap<String, DownloadCount>()
private val finalMap = ConcurrentHashMap<String, CopyOnWriteArraySet<BookChapter>>()
@ -110,22 +111,20 @@ class CacheBookService : BaseService() {
return book
}
private fun getWebBook(bookUrl: String, origin: String): WebBook? {
var webBook = webBookMap[origin]
if (webBook == null) {
private fun getBookSource(bookUrl: String, origin: String): BookSource? {
var bookSource = bookSourceMap[origin]
if (bookSource == null) {
synchronized(this) {
webBook = webBookMap[origin]
if (webBook == null) {
appDb.bookSourceDao.getBookSource(origin)?.let {
webBook = WebBook(it)
}
if (webBook == null) {
bookSource = bookSourceMap[origin]
if (bookSource == null) {
bookSource = appDb.bookSourceDao.getBookSource(origin)
if (bookSource == null) {
removeDownload(bookUrl)
}
}
}
}
return webBook
return bookSource
}
private fun addDownloadData(bookUrl: String?, start: Int, end: Int) {
@ -183,13 +182,13 @@ class CacheBookService : BaseService() {
postDownloading(true)
return@async
}
val webBook = getWebBook(bookChapter.bookUrl, book.origin)
if (webBook == null) {
val bookSource = getBookSource(bookChapter.bookUrl, book.origin)
if (bookSource == null) {
postDownloading(true)
return@async
}
if (!BookHelp.hasImageContent(book, bookChapter)) {
webBook.getContent(this, book, bookChapter, context = cachePool)
WebBook.getContent(this, bookSource, book, bookChapter, context = cachePool)
.timeout(60000L)
.onError(cachePool) {
synchronized(this) {

@ -24,7 +24,8 @@ import kotlin.math.min
class CheckSourceService : BaseService() {
private var threadCount = AppConfig.threadCount
private var searchCoroutine = Executors.newFixedThreadPool(min(threadCount,8)).asCoroutineDispatcher()
private var searchCoroutine =
Executors.newFixedThreadPool(min(threadCount, 8)).asCoroutineDispatcher()
private var tasks = CompositeCoroutine()
private val allIds = ArrayList<String>()
private val checkedIds = ArrayList<String>()
@ -109,8 +110,7 @@ class CheckSourceService : BaseService() {
fun check(source: BookSource) {
execute(context = searchCoroutine) {
Debug.startChecking(source)
val webBook = WebBook(source)
var books = webBook.searchBookAwait(this, CheckSource.keyword)
var books = WebBook.searchBookAwait(this, source, CheckSource.keyword)
if (books.isEmpty()) {
val exs = source.exploreKinds
var url: String? = null
@ -123,11 +123,12 @@ class CheckSourceService : BaseService() {
if (url.isNullOrBlank()) {
throw Exception("搜索内容为空并且没有发现")
}
books = webBook.exploreBookAwait(this, url)
books = WebBook.exploreBookAwait(this, source, url)
}
val book = webBook.getBookInfoAwait(this, books.first().toBook())
val toc = webBook.getChapterListAwait(this, book)
val content = webBook.getContentAwait(this, book, toc.first(), toc.getOrNull(2)?.url)
val book = WebBook.getBookInfoAwait(this, source, books.first().toBook())
val toc = WebBook.getChapterListAwait(this, source, book)
val content =
WebBook.getContentAwait(this, source, book, toc.first(), toc.getOrNull(1)?.url)
if (content.isBlank()) {
throw Exception("正文内容为空")
}

@ -9,8 +9,8 @@ import io.legado.app.constant.Status
import io.legado.app.data.appDb
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.help.coroutine.Coroutine
import io.legado.app.model.webBook.WebBook
import io.legado.app.service.AudioPlayService
import io.legado.app.utils.postEvent
import io.legado.app.utils.startService
@ -24,11 +24,11 @@ object AudioPlay {
var inBookshelf = false
var durChapterIndex = 0
var durChapterPos = 0
var webBook: WebBook? = null
var bookSource: BookSource? = null
val loadingChapters = arrayListOf<Int>()
fun headers(): Map<String, String>? {
return webBook?.bookSource?.getHeaderMap()
return bookSource?.getHeaderMap()
}
fun play(context: Context) {

@ -5,6 +5,7 @@ import io.legado.app.R
import io.legado.app.constant.IntentAction
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.model.ReadBook
import io.legado.app.model.webBook.WebBook
import io.legado.app.service.CacheBookService
@ -61,7 +62,7 @@ object CacheBook {
fun download(
scope: CoroutineScope,
webBook: WebBook,
bookSource: BookSource,
book: Book,
chapter: BookChapter,
resetPageOffset: Boolean = false
@ -73,7 +74,7 @@ object CacheBook {
downloadMap[book.bookUrl] = CopyOnWriteArraySet()
}
downloadMap[book.bookUrl]?.add(chapter.index)
webBook.getContent(scope, book, chapter)
WebBook.getContent(scope, bookSource, book, chapter)
.onSuccess { content ->
if (ReadBook.book?.bookUrl == book.bookUrl) {
ReadBook.contentLoadFinish(

@ -31,9 +31,7 @@ class AudioPlayViewModel(application: Application) : BaseViewModel(application)
durChapterPos = book.durChapterPos
durChapter = appDb.bookChapterDao.getChapter(book.bookUrl, durChapterIndex)
upDurChapter(book)
appDb.bookSourceDao.getBookSource(book.origin)?.let {
webBook = WebBook(it)
}
bookSource = appDb.bookSourceDao.getBookSource(book.origin)
if (durChapter == null) {
if (book.tocUrl.isEmpty()) {
loadBookInfo(book)
@ -52,10 +50,12 @@ class AudioPlayViewModel(application: Application) : BaseViewModel(application)
changeDruChapterIndex: ((chapters: List<BookChapter>) -> Unit)? = null
) {
execute {
AudioPlay.webBook?.getBookInfo(this, book)
?.onSuccess {
loadChapterList(book, changeDruChapterIndex)
}
AudioPlay.bookSource?.let {
WebBook.getBookInfo(this, it, book)
.onSuccess {
loadChapterList(book, changeDruChapterIndex)
}
}
}
}
@ -64,21 +64,23 @@ class AudioPlayViewModel(application: Application) : BaseViewModel(application)
changeDruChapterIndex: ((chapters: List<BookChapter>) -> Unit)? = null
) {
execute {
AudioPlay.webBook?.getChapterList(this, book)
?.onSuccess(Dispatchers.IO) { cList ->
if (cList.isNotEmpty()) {
if (changeDruChapterIndex == null) {
appDb.bookChapterDao.insert(*cList.toTypedArray())
AudioPlay.bookSource?.let {
WebBook.getChapterList(this, it, book)
.onSuccess(Dispatchers.IO) { cList ->
if (cList.isNotEmpty()) {
if (changeDruChapterIndex == null) {
appDb.bookChapterDao.insert(*cList.toTypedArray())
} else {
changeDruChapterIndex(cList)
}
AudioPlay.upDurChapter(book)
} else {
changeDruChapterIndex(cList)
context.toastOnUi(R.string.error_load_toc)
}
AudioPlay.upDurChapter(book)
} else {
}.onError {
context.toastOnUi(R.string.error_load_toc)
}
}?.onError {
context.toastOnUi(R.string.error_load_toc)
}
}
}
}
@ -92,9 +94,7 @@ class AudioPlayViewModel(application: Application) : BaseViewModel(application)
}
appDb.bookDao.insert(book1)
AudioPlay.book = book1
appDb.bookSourceDao.getBookSource(book1.origin)?.let {
AudioPlay.webBook = WebBook(it)
}
AudioPlay.bookSource = appDb.bookSourceDao.getBookSource(book1.origin)
if (book1.tocUrl.isEmpty()) {
loadBookInfo(book1) { upChangeDurChapterIndex(book1, oldTocSize, it) }
} else {

@ -100,8 +100,8 @@ class ChangeCoverViewModel(application: Application) : BaseViewModel(application
searchNext()
return
}
val task = WebBook(source)
.searchBook(viewModelScope, name, context = searchPool!!)
val task = WebBook
.searchBook(viewModelScope, source, name, context = searchPool!!)
.timeout(60000L)
.onSuccess(searchPool) {
if (it.isNotEmpty()) {

@ -128,9 +128,8 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
searchIndex++
}
val source = bookSourceList[searchIndex]
val webBook = WebBook(source)
val task = webBook
.searchBook(viewModelScope, name, context = searchPool!!)
val task = WebBook
.searchBook(viewModelScope, source, name, context = searchPool!!)
.timeout(60000L)
.onSuccess(searchPool) {
it.forEach { searchBook ->
@ -140,7 +139,7 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
) {
if (searchBook.latestChapterTitle.isNullOrEmpty()) {
if (AppConfig.changeSourceLoadInfo || AppConfig.changeSourceLoadToc) {
loadBookInfo(webBook, searchBook.toBook())
loadBookInfo(source, searchBook.toBook())
} else {
searchFinish(searchBook)
}
@ -171,11 +170,11 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
}
private fun loadBookInfo(webBook: WebBook, book: Book) {
webBook.getBookInfo(viewModelScope, book)
private fun loadBookInfo(source: BookSource, book: Book) {
WebBook.getBookInfo(viewModelScope, source, book)
.onSuccess {
if (context.getPrefBoolean(PreferKey.changeSourceLoadToc)) {
loadBookToc(webBook, book)
loadBookToc(source, book)
} else {
//从详情页里获取最新章节
book.latestChapterTitle = it.latestChapterTitle
@ -187,8 +186,8 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
}
}
private fun loadBookToc(webBook: WebBook, book: Book) {
webBook.getChapterList(viewModelScope, book)
private fun loadBookToc(source: BookSource, book: Book) {
WebBook.getChapterList(viewModelScope, source, book)
.onSuccess(IO) { chapters ->
if (chapters.isNotEmpty()) {
book.latestChapterTitle = chapters.last().title

@ -35,7 +35,7 @@ class ExploreShowViewModel(application: Application) : BaseViewModel(application
val source = bookSource
val url = exploreUrl
if (source != null && url != null) {
WebBook(source).exploreBook(viewModelScope, url, page)
WebBook.exploreBook(viewModelScope, source, url, page)
.timeout(30000L)
.onSuccess(IO) { searchBooks ->
booksData.postValue(searchBooks)

@ -81,7 +81,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
loadChapter(book, changeDruChapterIndex)
} else {
bookSource?.let { bookSource ->
WebBook(bookSource).getBookInfo(this, book, canReName = canReName)
WebBook.getBookInfo(this, bookSource, book, canReName = canReName)
.onSuccess(IO) {
bookData.postValue(book)
if (inBookshelf) {
@ -112,7 +112,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
}
} else {
bookSource?.let { bookSource ->
WebBook(bookSource).getChapterList(this, book)
WebBook.getChapterList(this, bookSource, book)
.onSuccess(IO) {
if (it.isNotEmpty()) {
if (inBookshelf) {

@ -727,9 +727,9 @@ class ReadBookActivity : ReadBookBaseActivity(),
}
override fun openSourceEditActivity() {
ReadBook.webBook?.let {
ReadBook.bookSource?.let {
sourceEditActivity.launch(Intent(this, BookSourceEditActivity::class.java).apply {
putExtra("data", it.bookSource.bookSourceUrl)
putExtra("data", it.bookSourceUrl)
})
}
}
@ -785,7 +785,7 @@ class ReadBookActivity : ReadBookBaseActivity(),
}
override fun showLogin() {
ReadBook.webBook?.bookSource?.let {
ReadBook.bookSource?.let {
startActivity<SourceLoginActivity> {
putExtra("sourceUrl", it.bookSourceUrl)
}

@ -53,7 +53,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
if (ReadBook.book?.bookUrl != book.bookUrl) {
ReadBook.resetData(book)
isInitFinish = true
if (!book.isLocalBook() && ReadBook.webBook == null) {
if (!book.isLocalBook() && ReadBook.bookSource == null) {
autoChangeSource(book.name, book.author)
return
}
@ -81,7 +81,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
ReadBook.titleDate.postValue(book.name)
ReadBook.upWebBook(book)
isInitFinish = true
if (!book.isLocalBook() && ReadBook.webBook == null) {
if (!book.isLocalBook() && ReadBook.bookSource == null) {
autoChangeSource(book.name, book.author)
return
}
@ -112,10 +112,12 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
if (book.isLocalBook()) {
loadChapterList(book, changeDruChapterIndex)
} else {
ReadBook.webBook?.getBookInfo(viewModelScope, book, canReName = false)
?.onSuccess {
loadChapterList(book, changeDruChapterIndex)
}
ReadBook.bookSource?.let {
WebBook.getBookInfo(viewModelScope, it, book, canReName = false)
.onSuccess {
loadChapterList(book, changeDruChapterIndex)
}
}
}
}
@ -141,24 +143,26 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
ReadBook.upMsg("LoadTocError:${it.localizedMessage}")
}
} else {
ReadBook.webBook?.getChapterList(viewModelScope, book)
?.onSuccess(IO) { cList ->
if (cList.isNotEmpty()) {
if (changeDruChapterIndex == null) {
appDb.bookChapterDao.insert(*cList.toTypedArray())
appDb.bookDao.update(book)
ReadBook.chapterSize = cList.size
ReadBook.upMsg(null)
ReadBook.loadContent(resetPageOffset = true)
ReadBook.bookSource?.let {
WebBook.getChapterList(viewModelScope, it, book)
.onSuccess(IO) { cList ->
if (cList.isNotEmpty()) {
if (changeDruChapterIndex == null) {
appDb.bookChapterDao.insert(*cList.toTypedArray())
appDb.bookDao.update(book)
ReadBook.chapterSize = cList.size
ReadBook.upMsg(null)
ReadBook.loadContent(resetPageOffset = true)
} else {
changeDruChapterIndex(cList)
}
} else {
changeDruChapterIndex(cList)
ReadBook.upMsg(context.getString(R.string.error_load_toc))
}
} else {
}.onError {
ReadBook.upMsg(context.getString(R.string.error_load_toc))
}
}?.onError {
ReadBook.upMsg(context.getString(R.string.error_load_toc))
}
}
}
}
@ -192,9 +196,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
it.changeTo(newBook)
}
ReadBook.book = newBook
appDb.bookSourceDao.getBookSource(newBook.origin)?.let {
ReadBook.webBook = WebBook(it)
}
ReadBook.bookSource = appDb.bookSourceDao.getBookSource(newBook.origin)
ReadBook.prevTextChapter = null
ReadBook.curTextChapter = null
ReadBook.nextTextChapter = null
@ -277,9 +279,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
fun upBookSource(success: (() -> Unit)?) {
execute {
ReadBook.book?.let { book ->
appDb.bookSourceDao.getBookSource(book.origin)?.let {
ReadBook.webBook = WebBook(it)
}
ReadBook.bookSource = appDb.bookSourceDao.getBookSource(book.origin)
}
}.onSuccess {
success?.invoke()

@ -284,7 +284,7 @@ class ReadMenu @JvmOverloads constructor(
}
fun upBookView() {
binding.tvLogin.isGone = ReadBook.webBook?.bookSource?.loginUrl.isNullOrEmpty()
binding.tvLogin.isGone = ReadBook.bookSource?.loginUrl.isNullOrEmpty()
ReadBook.curTextChapter?.let {
binding.tvChapterName.text = it.title
binding.tvChapterName.visible()

@ -3,13 +3,13 @@ package io.legado.app.ui.book.source.debug
import android.app.Application
import io.legado.app.base.BaseViewModel
import io.legado.app.data.appDb
import io.legado.app.data.entities.BookSource
import io.legado.app.model.Debug
import io.legado.app.model.webBook.WebBook
class BookSourceDebugModel(application: Application) : BaseViewModel(application),
Debug.Callback {
private var webBook: WebBook? = null
private var bookSource: BookSource? = null
private var callback: ((Int, String) -> Unit)? = null
var searchSrc: String? = null
var bookSrc: String? = null
@ -20,8 +20,7 @@ class BookSourceDebugModel(application: Application) : BaseViewModel(application
sourceUrl?.let {
//优先使用这个,不会抛出异常
execute {
val bookSource = appDb.bookSourceDao.getBookSource(sourceUrl)
bookSource?.let { webBook = WebBook(it) }
bookSource = appDb.bookSourceDao.getBookSource(sourceUrl)
}
}
}
@ -33,7 +32,7 @@ class BookSourceDebugModel(application: Application) : BaseViewModel(application
fun startDebug(key: String, start: (() -> Unit)? = null, error: (() -> Unit)? = null) {
execute {
Debug.callback = this@BookSourceDebugModel
Debug.startDebug(this, webBook!!, key)
Debug.startDebug(this, bookSource!!, key)
}.onStart {
start?.invoke()
}.onError {

@ -6,6 +6,7 @@ import io.legado.app.constant.BookType
import io.legado.app.constant.EventBus
import io.legado.app.data.appDb
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookSource
import io.legado.app.help.AppConfig
import io.legado.app.help.BookHelp
import io.legado.app.help.DefaultData
@ -75,15 +76,14 @@ class MainViewModel(application: Application) : BaseViewModel(application) {
}
appDb.bookSourceDao.getBookSource(book.origin)?.let { bookSource ->
execute(context = upTocPool) {
val webBook = WebBook(bookSource)
if (book.tocUrl.isBlank()) {
webBook.getBookInfoAwait(this, book)
WebBook.getBookInfoAwait(this, bookSource, book)
}
val toc = webBook.getChapterListAwait(this, book)
val toc = WebBook.getChapterListAwait(this, bookSource, book)
appDb.bookDao.update(book)
appDb.bookChapterDao.delByBook(book.bookUrl)
appDb.bookChapterDao.insert(*toc.toTypedArray())
cacheBook(webBook, book)
cacheBook(bookSource, book)
}.onError(upTocPool) {
it.printStackTrace()
}.onFinally(upTocPool) {
@ -108,7 +108,7 @@ class MainViewModel(application: Application) : BaseViewModel(application) {
}
}
private fun cacheBook(webBook: WebBook, book: Book) {
private fun cacheBook(bookSource: BookSource, book: Book) {
execute {
if (book.totalChapterNum > book.durChapterIndex) {
val downloadToIndex =
@ -119,7 +119,7 @@ class MainViewModel(application: Application) : BaseViewModel(application) {
var addToCache = false
while (!addToCache) {
if (CacheBook.downloadCount() < 10) {
CacheBook.download(this, webBook, book, chapter)
CacheBook.download(this, bookSource, book, chapter)
addToCache = true
} else {
delay(100)

@ -46,7 +46,7 @@ class BookshelfViewModel(application: Application) : BaseViewModel(application)
origin = bookSource.bookSourceUrl,
originName = bookSource.bookSourceName
)
WebBook(bookSource).getBookInfo(this, book)
WebBook.getBookInfo(this, bookSource, book)
.onSuccess(IO) {
it.order = appDb.bookDao.maxOrder + 1
it.save()

@ -6,7 +6,6 @@ import fi.iki.elonen.NanoWSD
import io.legado.app.R
import io.legado.app.data.appDb
import io.legado.app.model.Debug
import io.legado.app.model.webBook.WebBook
import io.legado.app.utils.GSON
import io.legado.app.utils.fromJsonObject
import io.legado.app.utils.isJson
@ -63,7 +62,7 @@ class SourceDebugWebSocket(handshakeRequest: NanoHTTPD.IHTTPSession) :
}
appDb.bookSourceDao.getBookSource(tag)?.let {
Debug.callback = this@SourceDebugWebSocket
Debug.startDebug(this, WebBook(it), key)
Debug.startDebug(this, it, key)
}
}
}

Loading…
Cancel
Save