修复发现页书源互相污染的问题

This commit is contained in:
HapeLee
2026-05-24 01:16:08 +08:00
parent d2853244be
commit 5b59b5a743
5 changed files with 36 additions and 24 deletions
@@ -5,8 +5,10 @@ import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.BookSourcePart import io.legado.app.data.entities.BookSourcePart
import io.legado.app.data.entities.SearchBook import io.legado.app.data.entities.SearchBook
import io.legado.app.data.entities.rule.ExploreKind import io.legado.app.data.entities.rule.ExploreKind
import io.legado.app.domain.gateway.ExploreBooksGateway
import io.legado.app.help.source.SourceHelp import io.legado.app.help.source.SourceHelp
import io.legado.app.help.source.exploreKinds import io.legado.app.help.source.exploreKinds
import io.legado.app.model.webBook.WebBook
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
@@ -16,7 +18,7 @@ interface ExploreRepository {
fun getBookshelfItems(): Flow<List<SearchBook>> fun getBookshelfItems(): Flow<List<SearchBook>>
fun getExploreGroups(): Flow<List<String>> fun getExploreGroups(): Flow<List<String>>
fun getExploreSources(query: String, selectedGroup: String): Flow<List<BookSourcePart>> fun getExploreSources(query: String, selectedGroup: String): Flow<List<BookSourcePart>>
suspend fun getBookSource(url: String): BookSource? suspend fun getBookSource(sourceUrl: String): BookSource?
suspend fun saveSearchBooks(books: List<SearchBook>) suspend fun saveSearchBooks(books: List<SearchBook>)
suspend fun getSourceExploreKinds(sourceUrl: String): List<ExploreKind> suspend fun getSourceExploreKinds(sourceUrl: String): List<ExploreKind>
suspend fun topSource(bookSource: BookSourcePart) suspend fun topSource(bookSource: BookSourcePart)
@@ -25,7 +27,7 @@ interface ExploreRepository {
class ExploreRepositoryImpl( class ExploreRepositoryImpl(
private val appDb: AppDatabase private val appDb: AppDatabase
) : ExploreRepository { ) : ExploreRepository, ExploreBooksGateway {
override fun getBookshelfItems(): Flow<List<SearchBook>> { override fun getBookshelfItems(): Flow<List<SearchBook>> {
return appDb.bookDao.flowBookShelf().map { books -> return appDb.bookDao.flowBookShelf().map { books ->
@@ -71,8 +73,16 @@ class ExploreRepositoryImpl(
} }
} }
override suspend fun getBookSource(url: String): BookSource? { override suspend fun getBookSource(sourceUrl: String): BookSource? {
return appDb.bookSourceDao.getBookSource(url) return appDb.bookSourceDao.getBookSource(sourceUrl)
}
override suspend fun exploreBooks(
bookSource: BookSource,
url: String,
page: Int
): List<SearchBook> {
return WebBook.exploreBookSuspend(bookSource, url, page)
} }
override suspend fun getSourceExploreKinds(sourceUrl: String): List<ExploreKind> = withContext(IO) { override suspend fun getSourceExploreKinds(sourceUrl: String): List<ExploreKind> = withContext(IO) {
@@ -39,6 +39,7 @@ import io.legado.app.domain.gateway.BookCacheDownloadGateway
import io.legado.app.domain.gateway.BookSearchGateway import io.legado.app.domain.gateway.BookSearchGateway
import io.legado.app.domain.gateway.BookSourceCallbackGateway import io.legado.app.domain.gateway.BookSourceCallbackGateway
import io.legado.app.domain.gateway.DatabaseMaintenanceGateway import io.legado.app.domain.gateway.DatabaseMaintenanceGateway
import io.legado.app.domain.gateway.ExploreBooksGateway
import io.legado.app.domain.gateway.HomepageModulesGateway import io.legado.app.domain.gateway.HomepageModulesGateway
import io.legado.app.domain.gateway.LocalBookGateway import io.legado.app.domain.gateway.LocalBookGateway
import io.legado.app.domain.gateway.ReadingProgressGateway import io.legado.app.domain.gateway.ReadingProgressGateway
@@ -173,7 +174,9 @@ val appModule = module {
single<ReadingProgressGateway> { WebDavReadingProgressRepository() } single<ReadingProgressGateway> { WebDavReadingProgressRepository() }
single<HomepageModulesGateway> { HomepageModulesRepository(get(), get()) } single<HomepageModulesGateway> { HomepageModulesRepository(get(), get()) }
single<BookDomainRepository> { BookDomainRepositoryImpl(get(), get()) } single<BookDomainRepository> { BookDomainRepositoryImpl(get(), get()) }
single<ExploreRepository> { ExploreRepositoryImpl(get()) } single { ExploreRepositoryImpl(get()) }
single<ExploreRepository> { get<ExploreRepositoryImpl>() }
single<ExploreBooksGateway> { get<ExploreRepositoryImpl>() }
singleOf(::RssRepository) singleOf(::RssRepository)
single { single {
SearchRepositoryImpl(get()) SearchRepositoryImpl(get())
@@ -0,0 +1,9 @@
package io.legado.app.domain.gateway
import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.SearchBook
interface ExploreBooksGateway {
suspend fun getBookSource(sourceUrl: String): BookSource?
suspend fun exploreBooks(bookSource: BookSource, url: String, page: Int): List<SearchBook>
}
@@ -1,13 +1,12 @@
package io.legado.app.domain.usecase package io.legado.app.domain.usecase
import io.legado.app.data.entities.SearchBook import io.legado.app.data.entities.SearchBook
import io.legado.app.data.repository.BookSourceRepository import io.legado.app.domain.gateway.ExploreBooksGateway
import io.legado.app.model.webBook.WebBook
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
class ExploreBooksUseCase( class ExploreBooksUseCase(
private val bookSourceRepository: BookSourceRepository, private val gateway: ExploreBooksGateway,
) { ) {
companion object { companion object {
/** 排名类模块自动加载的最大书本数 */ /** 排名类模块自动加载的最大书本数 */
@@ -23,18 +22,12 @@ class ExploreBooksUseCase(
args: String?, args: String?,
page: Int = 1 page: Int = 1
): ExploreResult = withContext(Dispatchers.IO) { ): ExploreResult = withContext(Dispatchers.IO) {
val base = bookSourceRepository.getBookSource(sourceUrl) val base = gateway.getBookSource(sourceUrl)
?: throw SourceNotFound(sourceUrl) ?: throw SourceNotFound(sourceUrl)
val source = args?.let { base.copy().also { s -> s.setVariable(it) } } ?: base val source = args?.let { base.copy().also { s -> s.setVariable(it) } } ?: base
val resolvedUrl = moduleUrl ?: source.exploreUrl val resolvedUrl = moduleUrl ?: source.exploreUrl
?: throw NoExploreUrl(sourceUrl) ?: throw NoExploreUrl(sourceUrl)
if (!resolvedUrl.startsWith("http", ignoreCase = true) val books = gateway.exploreBooks(source, resolvedUrl, page)
&& !resolvedUrl.startsWith("data:", ignoreCase = true)
&& !resolvedUrl.startsWith("{{")
) {
throw InvalidUrl(resolvedUrl)
}
val books = WebBook.exploreBookSuspend(source, resolvedUrl, page)
ExploreResult(resolvedUrl, books) ExploreResult(resolvedUrl, books)
} }
@@ -49,13 +42,10 @@ class ExploreBooksUseCase(
while (books.size < MAX_RANKING_BOOKS && page < MAX_RANKING_PAGES) { while (books.size < MAX_RANKING_BOOKS && page < MAX_RANKING_PAGES) {
page++ page++
val next = try { val next = try {
WebBook.exploreBookSuspend( val source = gateway.getBookSource(sourceUrl)
bookSourceRepository.getBookSource(sourceUrl)
?.let { s -> args?.let { s.copy().also { x -> x.setVariable(it) } } ?: s } ?.let { s -> args?.let { s.copy().also { x -> x.setVariable(it) } } ?: s }
?: return@withContext books.take(MAX_RANKING_BOOKS), ?: return@withContext books.take(MAX_RANKING_BOOKS)
result.resolvedUrl, gateway.exploreBooks(source, result.resolvedUrl, page)
page,
)
} catch (_: Exception) { } catch (_: Exception) {
emptyList() emptyList()
} }
@@ -69,5 +59,4 @@ class ExploreBooksUseCase(
class SourceNotFound(url: String) : Exception("Source not found: ${url.take(60)}") class SourceNotFound(url: String) : Exception("Source not found: ${url.take(60)}")
class NoExploreUrl(url: String) : Exception("No explore URL for source: ${url.take(60)}") class NoExploreUrl(url: String) : Exception("No explore URL for source: ${url.take(60)}")
class InvalidUrl(url: String) : Exception("Invalid explore URL: ${url.take(80)}")
} }
@@ -136,6 +136,7 @@ class ExploreShowViewModel(
sourceUrl = incomingSourceUrl sourceUrl = incomingSourceUrl
exploreUrl = incomingExploreUrl exploreUrl = incomingExploreUrl
page = 1 page = 1
bookSource = null
_rawBooks.value = emptyList() _rawBooks.value = emptyList()
_isEndStateFlow.value = false _isEndStateFlow.value = false
_errorMsg.value = null _errorMsg.value = null