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. (cherry picked from commit 1a61130f0b46addef036687b6c98f930e13147f8) # Conflicts: # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt
This commit is contained in:
parent
57d9ebb0b3
commit
016ca790e0
@ -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,
|
||||
)
|
||||
|
@ -7,9 +7,7 @@ import androidx.compose.ui.util.fastAny
|
||||
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.presentation.browse.FeedItemUI
|
||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||
@ -30,16 +28,12 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
import logcat.LogPriority
|
||||
import tachiyomi.core.util.lang.awaitSingle
|
||||
import tachiyomi.core.util.lang.launchIO
|
||||
import tachiyomi.core.util.lang.launchNonCancellable
|
||||
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.toMangaUpdate
|
||||
import tachiyomi.domain.source.interactor.CountFeedSavedSearchGlobal
|
||||
import tachiyomi.domain.source.interactor.DeleteFeedSavedSearchById
|
||||
import tachiyomi.domain.source.interactor.GetFeedSavedSearchGlobal
|
||||
@ -291,34 +285,10 @@ open class FeedScreenModel(
|
||||
getManga.subscribe(initialManga.url, initialManga.source)
|
||||
.collectLatest { manga ->
|
||||
if (manga == null) return@collectLatest
|
||||
withIOContext {
|
||||
initializeManga(source, manga)
|
||||
}
|
||||
value = manga
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a manga.
|
||||
*
|
||||
* @param manga to initialize.
|
||||
*/
|
||||
private suspend fun initializeManga(source: CatalogueSource?, manga: DomainManga) {
|
||||
if (source == null || 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDispose() {
|
||||
super.onDispose()
|
||||
coroutineDispatcher.close()
|
||||
|
@ -22,9 +22,7 @@ class MigrateSearchScreen(private val mangaId: Long, private val validSources: L
|
||||
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 = {
|
||||
|
@ -15,9 +15,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.interactor.GetExhSavedSearch
|
||||
import eu.kanade.domain.source.service.SourcePreferences
|
||||
import eu.kanade.domain.track.model.toDomainTrack
|
||||
@ -43,11 +41,9 @@ import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
@ -62,7 +58,6 @@ import tachiyomi.core.preference.mapAsCheckboxState
|
||||
import tachiyomi.core.util.lang.launchIO
|
||||
import tachiyomi.core.util.lang.launchNonCancellable
|
||||
import tachiyomi.core.util.lang.withIOContext
|
||||
import tachiyomi.core.util.lang.withNonCancellableContext
|
||||
import tachiyomi.core.util.lang.withUIContext
|
||||
import tachiyomi.core.util.system.logcat
|
||||
import tachiyomi.domain.UnsortedPreferences
|
||||
@ -211,7 +206,6 @@ open class BrowseSourceScreenModel(
|
||||
.filter { localManga ->
|
||||
!sourcePreferences.hideInLibraryItems().get() || !localManga.favorite
|
||||
}
|
||||
.onEach(::initializeManga)
|
||||
// SY -->
|
||||
.combineMetadata(metadata)
|
||||
// SY <--
|
||||
@ -335,26 +329,6 @@ open 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.
|
||||
*
|
||||
|
@ -9,9 +9,7 @@ import cafe.adriel.voyager.core.model.StateScreenModel
|
||||
import cafe.adriel.voyager.core.model.coroutineScope
|
||||
import eu.kanade.core.preference.asState
|
||||
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.interactor.GetExhSavedSearch
|
||||
import eu.kanade.domain.ui.UiPreferences
|
||||
import eu.kanade.presentation.browse.SourceFeedUI
|
||||
@ -34,17 +32,13 @@ import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import logcat.LogPriority
|
||||
import tachiyomi.core.util.lang.awaitSingle
|
||||
import tachiyomi.core.util.lang.launchIO
|
||||
import tachiyomi.core.util.lang.launchNonCancellable
|
||||
import tachiyomi.core.util.lang.withIOContext
|
||||
import tachiyomi.core.util.lang.withNonCancellableContext
|
||||
import tachiyomi.core.util.lang.withUIContext
|
||||
import tachiyomi.core.util.system.logcat
|
||||
import tachiyomi.domain.manga.interactor.GetManga
|
||||
import tachiyomi.domain.manga.interactor.NetworkToLocalManga
|
||||
import tachiyomi.domain.manga.model.toMangaUpdate
|
||||
import tachiyomi.domain.source.interactor.CountFeedSavedSearchBySourceId
|
||||
import tachiyomi.domain.source.interactor.DeleteFeedSavedSearchById
|
||||
import tachiyomi.domain.source.interactor.GetFeedSavedSearchBySourceId
|
||||
@ -205,34 +199,10 @@ open class SourceFeedScreenModel(
|
||||
getManga.subscribe(initialManga.url, initialManga.source)
|
||||
.collectLatest { manga ->
|
||||
if (manga == null) return@collectLatest
|
||||
withIOContext {
|
||||
initializeManga(manga)
|
||||
}
|
||||
value = manga
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a manga.
|
||||
*
|
||||
* @param manga to initialize.
|
||||
*/
|
||||
private suspend fun initializeManga(manga: DomainManga) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun loadSearches() =
|
||||
getExhSavedSearch.await(source.id, source::getFilterList)
|
||||
.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, EXHSavedSearch::name))
|
||||
|
@ -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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user