Don't attempt to initialize manga details from BrowseSource or Search screens
This was effectively DDoSing sources as it does a request for every entry to get the details (primarily a cover image). The expectation now is that users have to open individual entries to load the details/cover if needed. This isn't necessary for most sources, which are able to provide covers as part of the listing normally.
This commit is contained in:
parent
1de4bc9586
commit
1a61130f0b
6 changed files with 10 additions and 72 deletions
|
@ -29,7 +29,7 @@ fun GlobalSearchScreen(
|
|||
navigateUp: () -> Unit,
|
||||
onChangeSearchQuery: (String?) -> Unit,
|
||||
onSearch: (String) -> Unit,
|
||||
getManga: @Composable (CatalogueSource, Manga) -> State<Manga>,
|
||||
getManga: @Composable (Manga) -> State<Manga>,
|
||||
onClickSource: (CatalogueSource) -> Unit,
|
||||
onClickItem: (Manga) -> Unit,
|
||||
onLongClickItem: (Manga) -> Unit,
|
||||
|
@ -62,7 +62,7 @@ fun GlobalSearchScreen(
|
|||
private fun GlobalSearchContent(
|
||||
items: Map<CatalogueSource, SearchItemResult>,
|
||||
contentPadding: PaddingValues,
|
||||
getManga: @Composable (CatalogueSource, Manga) -> State<Manga>,
|
||||
getManga: @Composable (Manga) -> State<Manga>,
|
||||
onClickSource: (CatalogueSource) -> Unit,
|
||||
onClickItem: (Manga) -> Unit,
|
||||
onLongClickItem: (Manga) -> Unit,
|
||||
|
@ -96,7 +96,7 @@ private fun GlobalSearchContent(
|
|||
|
||||
GlobalSearchCardRow(
|
||||
titles = result.result,
|
||||
getManga = { getManga(source, it) },
|
||||
getManga = { getManga(it) },
|
||||
onClick = onClickItem,
|
||||
onLongClick = onLongClickItem,
|
||||
)
|
||||
|
|
|
@ -21,7 +21,7 @@ import tachiyomi.presentation.core.components.material.Scaffold
|
|||
fun MigrateSearchScreen(
|
||||
navigateUp: () -> Unit,
|
||||
state: MigrateSearchState,
|
||||
getManga: @Composable (CatalogueSource, Manga) -> State<Manga>,
|
||||
getManga: @Composable (Manga) -> State<Manga>,
|
||||
onChangeSearchQuery: (String?) -> Unit,
|
||||
onSearch: (String) -> Unit,
|
||||
onClickSource: (CatalogueSource) -> Unit,
|
||||
|
@ -58,7 +58,7 @@ private fun MigrateSearchContent(
|
|||
sourceId: Long,
|
||||
items: Map<CatalogueSource, SearchItemResult>,
|
||||
contentPadding: PaddingValues,
|
||||
getManga: @Composable (CatalogueSource, Manga) -> State<Manga>,
|
||||
getManga: @Composable (Manga) -> State<Manga>,
|
||||
onClickSource: (CatalogueSource) -> Unit,
|
||||
onClickItem: (Manga) -> Unit,
|
||||
onLongClickItem: (Manga) -> Unit,
|
||||
|
@ -85,7 +85,7 @@ private fun MigrateSearchContent(
|
|||
|
||||
GlobalSearchCardRow(
|
||||
titles = result.result,
|
||||
getManga = { getManga(source, it) },
|
||||
getManga = { getManga(it) },
|
||||
onClick = onClickItem,
|
||||
onLongClick = onLongClickItem,
|
||||
)
|
||||
|
|
|
@ -22,9 +22,7 @@ class MigrateSearchScreen(private val mangaId: Long) : Screen() {
|
|||
MigrateSearchScreen(
|
||||
navigateUp = navigator::pop,
|
||||
state = state,
|
||||
getManga = { source, manga ->
|
||||
screenModel.getManga(source = source, initialManga = manga)
|
||||
},
|
||||
getManga = { screenModel.getManga(it) },
|
||||
onChangeSearchQuery = screenModel::updateSearchQuery,
|
||||
onSearch = screenModel::search,
|
||||
onClickSource = {
|
||||
|
|
|
@ -16,9 +16,7 @@ import cafe.adriel.voyager.core.model.coroutineScope
|
|||
import eu.kanade.core.preference.asState
|
||||
import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay
|
||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||
import eu.kanade.domain.manga.model.copyFrom
|
||||
import eu.kanade.domain.manga.model.toDomainManga
|
||||
import eu.kanade.domain.manga.model.toSManga
|
||||
import eu.kanade.domain.source.service.SourcePreferences
|
||||
import eu.kanade.domain.track.model.toDomainTrack
|
||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||
|
@ -36,7 +34,6 @@ import kotlinx.coroutines.flow.filterNotNull
|
|||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -45,7 +42,6 @@ import tachiyomi.core.preference.CheckboxState
|
|||
import tachiyomi.core.preference.mapAsCheckboxState
|
||||
import tachiyomi.core.util.lang.launchIO
|
||||
import tachiyomi.core.util.lang.withIOContext
|
||||
import tachiyomi.core.util.lang.withNonCancellableContext
|
||||
import tachiyomi.core.util.system.logcat
|
||||
import tachiyomi.domain.category.interactor.GetCategories
|
||||
import tachiyomi.domain.category.interactor.SetMangaCategories
|
||||
|
@ -133,7 +129,6 @@ class BrowseSourceScreenModel(
|
|||
.filter { localManga ->
|
||||
!sourcePreferences.hideInLibraryItems().get() || !localManga.favorite
|
||||
}
|
||||
.onEach(::initializeManga)
|
||||
.stateIn(coroutineScope)
|
||||
}
|
||||
}
|
||||
|
@ -234,26 +229,6 @@ class BrowseSourceScreenModel(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a manga.
|
||||
*
|
||||
* @param manga to initialize.
|
||||
*/
|
||||
private suspend fun initializeManga(manga: Manga) {
|
||||
if (manga.thumbnailUrl != null || manga.initialized) return
|
||||
withNonCancellableContext {
|
||||
try {
|
||||
val networkManga = source.getMangaDetails(manga.toSManga())
|
||||
val updatedManga = manga.copyFrom(networkManga)
|
||||
.copy(initialized = true)
|
||||
|
||||
updateManga.await(updatedManga.toMangaUpdate())
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.ERROR, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds or removes a manga from the library.
|
||||
*
|
||||
|
|
|
@ -33,12 +33,7 @@ class GlobalSearchScreen(
|
|||
navigateUp = navigator::pop,
|
||||
onChangeSearchQuery = screenModel::updateSearchQuery,
|
||||
onSearch = screenModel::search,
|
||||
getManga = { source, manga ->
|
||||
screenModel.getManga(
|
||||
source = source,
|
||||
initialManga = manga,
|
||||
)
|
||||
},
|
||||
getManga = { screenModel.getManga(it) },
|
||||
onClickSource = {
|
||||
if (!screenModel.incognitoMode.get()) {
|
||||
screenModel.lastUsedSourceId.set(it.id)
|
||||
|
|
|
@ -6,9 +6,7 @@ import androidx.compose.runtime.produceState
|
|||
import cafe.adriel.voyager.core.model.StateScreenModel
|
||||
import cafe.adriel.voyager.core.model.coroutineScope
|
||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||
import eu.kanade.domain.manga.model.copyFrom
|
||||
import eu.kanade.domain.manga.model.toDomainManga
|
||||
import eu.kanade.domain.manga.model.toSManga
|
||||
import eu.kanade.domain.source.service.SourcePreferences
|
||||
import eu.kanade.tachiyomi.extension.ExtensionManager
|
||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||
|
@ -18,15 +16,11 @@ import kotlinx.coroutines.awaitAll
|
|||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import logcat.LogPriority
|
||||
import tachiyomi.core.util.lang.awaitSingle
|
||||
import tachiyomi.core.util.lang.withIOContext
|
||||
import tachiyomi.core.util.lang.withNonCancellableContext
|
||||
import tachiyomi.core.util.system.logcat
|
||||
import tachiyomi.domain.manga.interactor.GetManga
|
||||
import tachiyomi.domain.manga.interactor.NetworkToLocalManga
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.domain.manga.model.toMangaUpdate
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.util.concurrent.Executors
|
||||
|
@ -57,43 +51,19 @@ abstract class SearchScreenModel<T>(
|
|||
}
|
||||
|
||||
@Composable
|
||||
fun getManga(source: CatalogueSource, initialManga: Manga): State<Manga> {
|
||||
fun getManga(initialManga: Manga): State<Manga> {
|
||||
return produceState(initialValue = initialManga) {
|
||||
getManga.subscribe(initialManga.url, initialManga.source)
|
||||
.collectLatest { manga ->
|
||||
if (manga == null) return@collectLatest
|
||||
withIOContext {
|
||||
initializeManga(source, manga)
|
||||
}
|
||||
value = manga
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a manga.
|
||||
*
|
||||
* @param source to interact with
|
||||
* @param manga to initialize.
|
||||
*/
|
||||
private suspend fun initializeManga(source: CatalogueSource, manga: Manga) {
|
||||
if (manga.thumbnailUrl != null || manga.initialized) return
|
||||
withNonCancellableContext {
|
||||
try {
|
||||
val networkManga = source.getMangaDetails(manga.toSManga())
|
||||
val updatedManga = manga.copyFrom(networkManga)
|
||||
.copy(initialized = true)
|
||||
|
||||
updateManga.await(updatedManga.toMangaUpdate())
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.ERROR, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun getEnabledSources(): List<CatalogueSource>
|
||||
|
||||
fun getSelectedSources(): List<CatalogueSource> {
|
||||
private fun getSelectedSources(): List<CatalogueSource> {
|
||||
val filter = extensionFilter
|
||||
|
||||
val enabledSources = getEnabledSources()
|
||||
|
|
Reference in a new issue