Significantly improve browsing speed (near instantaneous) (#1946)

(cherry picked from commit c8ffabc84a096207c1997ab69fc86176f3b53f00)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/domain/manga/model/Manga.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt
#	data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt
#	data/src/main/java/tachiyomi/data/source/SourcePagingSource.kt
#	data/src/main/sqldelight/tachiyomi/data/mangas.sq
#	domain/src/main/java/tachiyomi/domain/manga/interactor/NetworkToLocalManga.kt
#	domain/src/main/java/tachiyomi/domain/manga/repository/MangaRepository.kt
#	domain/src/main/java/tachiyomi/domain/source/repository/SourceRepository.kt
This commit is contained in:
AntsyLich
2025-03-31 13:17:22 +06:00
committed by Jobobby04
parent 26674136e6
commit c8039739d5
29 changed files with 180 additions and 172 deletions
@@ -76,24 +76,6 @@ fun Manga.copyFrom(other: SManga): Manga {
)
}
fun SManga.toDomainManga(sourceId: Long): Manga {
return Manga.create().copy(
url = url,
// SY -->
ogTitle = title,
ogArtist = artist,
ogAuthor = author,
ogThumbnailUrl = thumbnail_url,
ogDescription = description,
ogGenre = getGenres(),
ogStatus = status.toLong(),
// SY <--
updateStrategy = update_strategy,
initialized = initialized,
source = sourceId,
)
}
fun Manga.hasCustomCover(coverCache: CoverCache = Injekt.get()): Boolean {
return coverCache.getCustomCoverFile(id).exists()
}
@@ -510,7 +510,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
private suspend fun updateCovers() {
val semaphore = Semaphore(5)
val progressCount = AtomicInteger(0)
val progressCount = AtomicInt(0)
val currentlyUpdatingManga = CopyOnWriteArrayList<Manga>()
coroutineScope {
@@ -585,7 +585,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
var dbManga = getManga.await(networkManga.url, mangaDex.id)
if (dbManga == null) {
dbManga = networkToLocalManga.await(
dbManga = networkToLocalManga(
Manga.create().copy(
url = networkManga.url,
ogTitle = networkManga.title,
@@ -170,7 +170,7 @@ class MergedSource : HttpSource() {
var manga = getManga.await(mangaUrl, mangaSourceId)
val source = sourceManager.getOrStub(manga?.source ?: mangaSourceId)
if (manga == null) {
val newManga = networkToLocalManga.await(
val newManga = networkToLocalManga(
Manga.create().copy(
source = mangaSourceId,
url = mangaUrl,
@@ -7,7 +7,6 @@ import androidx.compose.ui.util.fastAny
import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.screenModelScope
import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.manga.model.toDomainManga
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.presentation.browse.FeedItemUI
import eu.kanade.tachiyomi.source.CatalogueSource
@@ -31,6 +30,7 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json
import mihon.domain.manga.model.toDomainManga
import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.core.common.util.lang.launchNonCancellable
import tachiyomi.core.common.util.lang.withIOContext
@@ -251,9 +251,7 @@ open class FeedScreenModel(
val result = withIOContext {
itemUI.copy(
results = page.map {
networkToLocalManga.await(it.toDomainManga(itemUI.source!!.id))
},
results = networkToLocalManga(page.map { it.toDomainManga(itemUI.source!!.id) }),
)
}
@@ -226,7 +226,7 @@ class MigrationListScreenModel(
if (searchResult != null &&
!(searchResult.url == mangaObj.url && source.id == mangaObj.source)
) {
val localManga = networkToLocalManga.await(searchResult)
val localManga = networkToLocalManga(searchResult)
val chapters = if (source is EHentai) {
source.getChapterList(localManga.toSManga(), throttleManager::throttle)
@@ -264,7 +264,7 @@ class MigrationListScreenModel(
}
if (searchResult != null) {
val localManga = networkToLocalManga.await(searchResult)
val localManga = networkToLocalManga(searchResult)
val chapters = try {
if (source is EHentai) {
source.getChapterList(localManga.toSManga(), throttleManager::throttle)
@@ -455,7 +455,7 @@ class MigrationListScreenModel(
screenModelScope.launchIO {
val result = migratingManga.migrationScope.async {
val manga = getManga(newMangaId)!!
val localManga = networkToLocalManga.await(manga)
val localManga = networkToLocalManga(manga)
try {
val source = sourceManager.get(manga.source)!!
val chapters = source.getChapterList(localManga.toSManga())
@@ -16,7 +16,6 @@ import cafe.adriel.voyager.core.model.screenModelScope
import dev.icerock.moko.resources.StringResource
import eu.kanade.core.preference.asState
import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.manga.model.toDomainManga
import eu.kanade.domain.source.interactor.GetExhSavedSearch
import eu.kanade.domain.source.interactor.GetIncognitoState
import eu.kanade.domain.source.service.SourcePreferences
@@ -39,7 +38,6 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
@@ -50,7 +48,6 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import tachiyomi.core.common.preference.CheckboxState
@@ -67,7 +64,6 @@ import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.domain.manga.interactor.GetDuplicateLibraryManga
import tachiyomi.domain.manga.interactor.GetFlatMetadataById
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 tachiyomi.domain.source.interactor.DeleteSavedSearchById
@@ -75,7 +71,7 @@ import tachiyomi.domain.source.interactor.GetRemoteManga
import tachiyomi.domain.source.interactor.InsertSavedSearch
import tachiyomi.domain.source.model.EXHSavedSearch
import tachiyomi.domain.source.model.SavedSearch
import tachiyomi.domain.source.repository.SourcePagingSourceType
import tachiyomi.domain.source.repository.SourcePagingSource
import tachiyomi.domain.source.service.SourceManager
import tachiyomi.i18n.sy.SYMR
import uy.kohesive.injekt.Injekt
@@ -101,7 +97,6 @@ open class BrowseSourceScreenModel(
private val setMangaCategories: SetMangaCategories = Injekt.get(),
private val setMangaDefaultChapterFlags: SetMangaDefaultChapterFlags = Injekt.get(),
private val getManga: GetManga = Injekt.get(),
private val networkToLocalManga: NetworkToLocalManga = Injekt.get(),
private val updateManga: UpdateManga = Injekt.get(),
private val addTracks: AddTracks = Injekt.get(),
private val getIncognitoState: GetIncognitoState = Injekt.get(),
@@ -193,10 +188,9 @@ open class BrowseSourceScreenModel(
createSourcePagingSource(listing.query ?: "", listing.filters)
// SY <--
}.flow.map { pagingData ->
pagingData.map { (it, metadata) ->
networkToLocalManga.await(it.toDomainManga(sourceId))
.let { localManga -> getManga.subscribe(localManga.url, localManga.source) }
.filterNotNull()
pagingData.map { (manga, metadata) ->
getManga.subscribe(manga.url, manga.source)
.map { it ?: manga }
// SY -->
.combineMetadata(metadata)
// SY <--
@@ -382,8 +376,8 @@ open class BrowseSourceScreenModel(
}
// SY -->
open fun createSourcePagingSource(query: String, filters: FilterList): SourcePagingSourceType {
return getRemoteManga.subscribe(sourceId, query, filters)
open fun createSourcePagingSource(query: String, filters: FilterList): SourcePagingSource {
return getRemoteManga(sourceId, query, filters)
}
// SY <--
@@ -10,7 +10,6 @@ import cafe.adriel.voyager.core.model.screenModelScope
import dev.icerock.moko.resources.StringResource
import eu.kanade.core.preference.asState
import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.manga.model.toDomainManga
import eu.kanade.domain.source.interactor.GetExhSavedSearch
import eu.kanade.domain.ui.UiPreferences
import eu.kanade.presentation.browse.SourceFeedUI
@@ -32,8 +31,8 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import mihon.domain.manga.model.toDomainManga
import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.core.common.util.lang.launchNonCancellable
import tachiyomi.core.common.util.lang.withIOContext
@@ -173,9 +172,7 @@ open class SourceFeedScreenModel(
}
val titles = withIOContext {
page.map {
networkToLocalManga.await(it.toDomainManga(source.id))
}
networkToLocalManga(page.map { it.toDomainManga(source.id) })
}
mutableState.update { state ->
@@ -5,7 +5,6 @@ import androidx.compose.runtime.Immutable
import androidx.compose.runtime.produceState
import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.screenModelScope
import eu.kanade.domain.manga.model.toDomainManga
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.presentation.util.ioCoroutineScope
import eu.kanade.tachiyomi.extension.ExtensionManager
@@ -25,6 +24,7 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import mihon.domain.manga.model.toDomainManga
import tachiyomi.core.common.preference.toggle
import tachiyomi.domain.manga.interactor.GetManga
import tachiyomi.domain.manga.interactor.NetworkToLocalManga
@@ -169,9 +169,8 @@ abstract class SearchScreenModel(
source.getSearchManga(1, query, source.getFilterList())
}
val titles = page.mangas.map {
networkToLocalManga.await(it.toDomainManga(source.id))
}
val titles = page.mangas.map { it.toDomainManga(source.id) }
.let { networkToLocalManga(it) }
if (isActive) {
updateItem(source, SearchItemResult.Success(titles))
@@ -4,18 +4,16 @@ import androidx.compose.runtime.Immutable
import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.screenModelScope
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
import eu.kanade.domain.manga.model.toDomainManga
import eu.kanade.domain.manga.model.toSManga
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ResolvableSource
import eu.kanade.tachiyomi.source.online.UriType
import kotlinx.coroutines.flow.update
import mihon.domain.manga.model.toDomainManga
import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.domain.chapter.interactor.GetChapterByUrlAndMangaId
import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.manga.interactor.GetMangaByUrlAndSourceId
import tachiyomi.domain.manga.interactor.NetworkToLocalManga
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.source.service.SourceManager
@@ -27,7 +25,6 @@ class DeepLinkScreenModel(
private val sourceManager: SourceManager = Injekt.get(),
private val networkToLocalManga: NetworkToLocalManga = Injekt.get(),
private val getChapterByUrlAndMangaId: GetChapterByUrlAndMangaId = Injekt.get(),
private val getMangaByUrlAndSourceId: GetMangaByUrlAndSourceId = Injekt.get(),
private val syncChaptersWithSource: SyncChaptersWithSource = Injekt.get(),
) : StateScreenModel<DeepLinkScreenModel.State>(State.Loading) {
@@ -38,7 +35,7 @@ class DeepLinkScreenModel(
.firstOrNull { it.getUriType(query) != UriType.Unknown }
val manga = source?.getManga(query)?.let {
getMangaFromSManga(it, source.id)
networkToLocalManga(it.toDomainManga(source.id))
}
val chapter = if (source?.getUriType(query) == UriType.Chapter && manga != null) {
@@ -73,11 +70,6 @@ class DeepLinkScreenModel(
}
}
private suspend fun getMangaFromSManga(sManga: SManga, sourceId: Long): Manga {
return getMangaByUrlAndSourceId.await(sManga.url, sourceId)
?: networkToLocalManga.await(sManga.toDomainManga(sourceId))
}
sealed interface State {
@Immutable
data object Loading : State
@@ -658,7 +658,7 @@ class MangaScreenModel(
existingManga = getManga.await(mergedManga.url, mergedManga.source)
}
mergedManga = networkToLocalManga.await(mergedManga)
mergedManga = networkToLocalManga(mergedManga)
getCategories.await(originalMangaId)
.let {