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:
arkon 2023-03-26 13:12:32 -04:00
parent 1de4bc9586
commit 1a61130f0b
6 changed files with 10 additions and 72 deletions

View file

@ -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,
)

View file

@ -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,
)

View file

@ -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 = {

View file

@ -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.
*

View file

@ -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)

View file

@ -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()