Significantly improve browsing speed (near instantaneous) (mihonapp/mihon#1946)
(cherry picked from commit c8ffabc84a096207c1997ab69fc86176f3b53f00)
This commit is contained in:
parent
d23130b317
commit
712a1f2bdb
30 changed files with 183 additions and 166 deletions
|
|
@ -18,6 +18,9 @@ The format is a modified version of [Keep a Changelog](https://keepachangelog.co
|
|||
- Add markdown support for manga descriptions ([@Secozzi](https://github.com/Secozzi)) ([#1948](https://github.com/mihonapp/mihon/pull/1948))
|
||||
- Add Nord Theme ([@Riztard](https://github.com/Riztard)) ([#1951](https://github.com/mihonapp/mihon/pull/1951))
|
||||
|
||||
### Improved
|
||||
- Significantly improve browsing speed (near instantaneous) ([@AntsyLich](https://github.com/AntsyLich)) ([#1946](https://github.com/mihonapp/mihon/pull/1946))
|
||||
|
||||
### Fixes
|
||||
- Fix Bangumi search results including novels ([@MajorTanya](https://github.com/MajorTanya)) ([#1885](https://github.com/mihonapp/mihon/pull/1885))
|
||||
- Fix next chapter button occasionally jumping to the last page of the current chapter ([@perokhe](https://github.com/perokhe)) ([#1920](https://github.com/mihonapp/mihon/pull/1920))
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ class SmartSearchMerge(
|
|||
existingManga = getManga.await(mergedManga.url, mergedManga.source)
|
||||
}
|
||||
|
||||
mergedManga = networkToLocalManga.await(mergedManga)
|
||||
mergedManga = networkToLocalManga(mergedManga)
|
||||
|
||||
getCategories.await(originalMangaId)
|
||||
.let { categories ->
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -568,7 +568,7 @@ class LibraryUpdateJob(private val context: Context, private val workerParams: W
|
|||
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,
|
||||
|
|
|
|||
|
|
@ -166,7 +166,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,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import androidx.compose.runtime.produceState
|
|||
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.model.toDomainManga
|
||||
import eu.kanade.domain.source.service.SourcePreferences
|
||||
import eu.kanade.presentation.browse.FeedItemUI
|
||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||
|
|
@ -32,6 +31,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
|
||||
|
|
@ -302,7 +302,7 @@ open class FeedScreenModel(
|
|||
val result = withIOContext {
|
||||
itemUI.copy(
|
||||
results = page.map {
|
||||
networkToLocalManga.await(it.toDomainManga(itemUI.source!!.id))
|
||||
networkToLocalManga(it.toDomainManga(itemUI.source!!.id))
|
||||
}
|
||||
// KMK -->
|
||||
.filter { !hideInLibraryFeedItems.get() || !it.favorite },
|
||||
|
|
|
|||
|
|
@ -235,7 +235,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)
|
||||
|
|
@ -273,7 +273,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)
|
||||
|
|
@ -514,7 +514,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
|
||||
|
|
@ -66,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
|
||||
|
|
@ -74,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
|
||||
|
|
@ -100,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(),
|
||||
|
|
@ -207,10 +203,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 <--
|
||||
|
|
@ -422,8 +417,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 <--
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import cafe.adriel.voyager.core.model.StateScreenModel
|
|||
import cafe.adriel.voyager.core.model.screenModelScope
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import eu.kanade.core.preference.asState
|
||||
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
|
||||
|
|
@ -34,6 +33,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
|
||||
|
|
@ -212,7 +212,7 @@ open class SourceFeedScreenModel(
|
|||
|
||||
val titles = withIOContext {
|
||||
page.map {
|
||||
networkToLocalManga.await(it.toDomainManga(source.id))
|
||||
networkToLocalManga(it.toDomainManga(source.id))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -186,9 +186,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
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ import eu.kanade.domain.manga.interactor.UpdateManga
|
|||
import eu.kanade.domain.manga.model.PagePreview
|
||||
import eu.kanade.domain.manga.model.chaptersFiltered
|
||||
import eu.kanade.domain.manga.model.downloadedFilter
|
||||
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.interactor.AddTracks
|
||||
|
|
@ -105,6 +104,7 @@ import kotlinx.coroutines.isActive
|
|||
import kotlinx.coroutines.launch
|
||||
import logcat.LogPriority
|
||||
import mihon.domain.chapter.interactor.FilterChaptersForDownload
|
||||
import mihon.domain.manga.model.toDomainManga
|
||||
import tachiyomi.core.common.i18n.stringResource
|
||||
import tachiyomi.core.common.preference.CheckboxState
|
||||
import tachiyomi.core.common.preference.TriState
|
||||
|
|
@ -1152,7 +1152,7 @@ class MangaScreenModel(
|
|||
/* Push found related mangas into collection */
|
||||
val relatedManga = RelatedManga.Success.fromPair(pair) { mangaList ->
|
||||
mangaList.map {
|
||||
networkToLocalManga.await(it.toDomainManga(state.source.id))
|
||||
networkToLocalManga(it.toDomainManga(state.source.id))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ class GalleryAdder(
|
|||
|
||||
// Use manga in DB if possible, otherwise, make a new manga
|
||||
var manga = getManga.await(cleanedMangaUrl, source.id)
|
||||
?: networkToLocalManga.await(
|
||||
?: networkToLocalManga(
|
||||
Manga.create().copy(
|
||||
source = source.id,
|
||||
url = cleanedMangaUrl,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package exh.favorites
|
||||
|
||||
import eu.kanade.domain.manga.model.toDomainManga
|
||||
import eu.kanade.tachiyomi.source.online.all.EHentai
|
||||
import exh.metadata.metadata.EHentaiSearchMetadata
|
||||
import exh.source.EXH_SOURCE_ID
|
||||
|
|
@ -11,6 +10,7 @@ import kotlinx.coroutines.flow.filter
|
|||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import mihon.domain.manga.model.toDomainManga
|
||||
import tachiyomi.domain.category.interactor.GetCategories
|
||||
import tachiyomi.domain.category.model.Category
|
||||
import tachiyomi.domain.manga.interactor.DeleteFavoriteEntries
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@ package exh.md.follows
|
|||
|
||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||
import eu.kanade.tachiyomi.source.online.all.MangaDex
|
||||
import tachiyomi.data.source.SourcePagingSource
|
||||
import tachiyomi.data.source.BaseSourcePagingSource
|
||||
|
||||
/**
|
||||
* LatestUpdatesPager inherited from the general Pager.
|
||||
*/
|
||||
class MangaDexFollowsPagingSource(val mangadex: MangaDex) : SourcePagingSource(mangadex) {
|
||||
class MangaDexFollowsPagingSource(val mangadex: MangaDex) : BaseSourcePagingSource(mangadex) {
|
||||
|
||||
override suspend fun requestNextPage(currentPage: Int): MangasPage {
|
||||
return mangadex.fetchFollows(currentPage)
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@ import kotlinx.coroutines.flow.Flow
|
|||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.update
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.domain.source.repository.SourcePagingSourceType
|
||||
import tachiyomi.domain.source.repository.SourcePagingSource
|
||||
|
||||
class MangaDexFollowsScreenModel(sourceId: Long) : BrowseSourceScreenModel(sourceId, null) {
|
||||
|
||||
override fun createSourcePagingSource(query: String, filters: FilterList): SourcePagingSourceType {
|
||||
override fun createSourcePagingSource(query: String, filters: FilterList): SourcePagingSource {
|
||||
return MangaDexFollowsPagingSource(source.getMainSource() as MangaDex)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.produceState
|
||||
import cafe.adriel.voyager.core.model.StateScreenModel
|
||||
import eu.kanade.domain.manga.model.toDomainManga
|
||||
import eu.kanade.presentation.util.ioCoroutineScope
|
||||
import exh.recs.sources.RecommendationPagingSource
|
||||
import exh.recs.sources.SourceCatalogue
|
||||
|
|
@ -22,6 +21,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.domain.manga.interactor.GetManga
|
||||
import tachiyomi.domain.manga.interactor.NetworkToLocalManga
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
|
|
@ -81,7 +81,7 @@ open class RecommendsScreenModel(
|
|||
val recSourceId = recSource.associatedSourceId
|
||||
if (recSourceId != null) {
|
||||
// If the recommendation is associated with a source, resolve it
|
||||
networkToLocalManga.await(it.toDomainManga(recSourceId))
|
||||
networkToLocalManga(it.toDomainManga(recSourceId))
|
||||
} else {
|
||||
// Otherwise, skip this step. The user will be prompted to choose a source via SmartSearch
|
||||
it.toDomainManga(RECOMMENDS_SOURCE)
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ import exh.source.isMdBasedSource
|
|||
import kotlinx.serialization.json.Json
|
||||
import logcat.LogPriority
|
||||
import tachiyomi.core.common.util.system.logcat
|
||||
import tachiyomi.data.source.BaseSourcePagingSource
|
||||
import tachiyomi.data.source.NoResultsException
|
||||
import tachiyomi.data.source.SourcePagingSource
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.domain.source.service.SourceManager
|
||||
import tachiyomi.domain.track.interactor.GetTracks
|
||||
|
|
@ -29,7 +29,7 @@ import uy.kohesive.injekt.injectLazy
|
|||
abstract class RecommendationPagingSource(
|
||||
source: CatalogueSource?,
|
||||
protected val manga: Manga,
|
||||
) : SourcePagingSource(source) {
|
||||
) : BaseSourcePagingSource(source) {
|
||||
// Display name
|
||||
abstract val name: String
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package exh.smartsearch
|
||||
|
||||
import eu.kanade.domain.manga.model.toDomainManga
|
||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
|
|
@ -8,6 +7,7 @@ import info.debatty.java.stringsimilarity.NormalizedLevenshtein
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.supervisorScope
|
||||
import mihon.domain.manga.model.toDomainManga
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import java.util.Locale
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class SmartSearchScreenModel(
|
|||
val result = try {
|
||||
val resultManga = smartSearchEngine.smartSearch(source, config.origTitle)
|
||||
if (resultManga != null) {
|
||||
val localManga = networkToLocalManga.await(resultManga)
|
||||
val localManga = networkToLocalManga(resultManga)
|
||||
SearchResults.Found(localManga)
|
||||
} else {
|
||||
SearchResults.NotFound
|
||||
|
|
|
|||
|
|
@ -97,40 +97,6 @@ class MangaRepositoryImpl(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun insert(manga: Manga): Long? {
|
||||
return handler.awaitOneOrNullExecutable(inTransaction = true) {
|
||||
// SY -->
|
||||
if (mangasQueries.getIdByUrlAndSource(manga.url, manga.source).executeAsOneOrNull() != null) {
|
||||
return@awaitOneOrNullExecutable mangasQueries.getIdByUrlAndSource(manga.url, manga.source)
|
||||
}
|
||||
// SY <--
|
||||
mangasQueries.insert(
|
||||
source = manga.source,
|
||||
url = manga.url,
|
||||
artist = manga.artist,
|
||||
author = manga.author,
|
||||
description = manga.description,
|
||||
genre = manga.genre,
|
||||
title = manga.title,
|
||||
status = manga.status,
|
||||
thumbnailUrl = manga.thumbnailUrl,
|
||||
favorite = manga.favorite,
|
||||
lastUpdate = manga.lastUpdate,
|
||||
nextUpdate = manga.nextUpdate,
|
||||
calculateInterval = manga.fetchInterval.toLong(),
|
||||
initialized = manga.initialized,
|
||||
viewerFlags = manga.viewerFlags,
|
||||
chapterFlags = manga.chapterFlags,
|
||||
coverLastModified = manga.coverLastModified,
|
||||
dateAdded = manga.dateAdded,
|
||||
updateStrategy = manga.updateStrategy,
|
||||
version = manga.version,
|
||||
notes = manga.notes,
|
||||
)
|
||||
mangasQueries.selectLastInsertedRowId()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun update(update: MangaUpdate): Boolean {
|
||||
return try {
|
||||
partialUpdate(update)
|
||||
|
|
@ -151,6 +117,37 @@ class MangaRepositoryImpl(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun insertNetworkManga(manga: List<Manga>): List<Manga> {
|
||||
return handler.await(inTransaction = true) {
|
||||
manga.map {
|
||||
mangasQueries.insertNetworkManga(
|
||||
source = it.source,
|
||||
url = it.url,
|
||||
artist = it.artist,
|
||||
author = it.author,
|
||||
description = it.description,
|
||||
genre = it.genre,
|
||||
title = it.title,
|
||||
status = it.status,
|
||||
thumbnailUrl = it.thumbnailUrl,
|
||||
favorite = it.favorite,
|
||||
lastUpdate = it.lastUpdate,
|
||||
nextUpdate = it.nextUpdate,
|
||||
calculateInterval = it.fetchInterval.toLong(),
|
||||
initialized = it.initialized,
|
||||
viewerFlags = it.viewerFlags,
|
||||
chapterFlags = it.chapterFlags,
|
||||
coverLastModified = it.coverLastModified,
|
||||
dateAdded = it.dateAdded,
|
||||
updateStrategy = it.updateStrategy,
|
||||
version = it.version,
|
||||
mapper = MangaMapper::mapManga,
|
||||
)
|
||||
.executeAsOne()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun partialUpdate(vararg mangaUpdates: MangaUpdate) {
|
||||
handler.await(inTransaction = true) {
|
||||
mangaUpdates.forEach { value ->
|
||||
|
|
|
|||
|
|
@ -4,21 +4,27 @@ import eu.kanade.tachiyomi.source.CatalogueSource
|
|||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||
import eu.kanade.tachiyomi.source.model.MetadataMangasPage
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import exh.metadata.metadata.RaisedSearchMetadata
|
||||
import mihon.domain.manga.model.toDomainManga
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
|
||||
abstract class EHentaiPagingSource(override val source: CatalogueSource) : SourcePagingSource(source) {
|
||||
abstract class EHentaiPagingSource(override val source: CatalogueSource) : BaseSourcePagingSource(source) {
|
||||
|
||||
override fun getPageLoadResult(
|
||||
override suspend fun getPageLoadResult(
|
||||
params: LoadParams<Long>,
|
||||
mangasPage: MangasPage,
|
||||
): LoadResult.Page<Long, Pair<SManga, RaisedSearchMetadata?>> {
|
||||
): LoadResult.Page<Long, Pair<Manga, RaisedSearchMetadata?>> {
|
||||
mangasPage as MetadataMangasPage
|
||||
val metadata = mangasPage.mangasMetadata
|
||||
|
||||
val manga = mangasPage.mangas.map { it.toDomainManga(source.id) }
|
||||
.let { networkToLocalManga(it) }
|
||||
// SY -->
|
||||
.mapIndexed { index, manga -> manga to metadata.getOrNull(index) }
|
||||
// SY <--
|
||||
|
||||
return LoadResult.Page(
|
||||
data = mangasPage.mangas
|
||||
.mapIndexed { index, sManga -> sManga to metadata.getOrNull(index) },
|
||||
data = manga,
|
||||
prevKey = null,
|
||||
nextKey = mangasPage.nextKey,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,13 +5,20 @@ import eu.kanade.tachiyomi.source.CatalogueSource
|
|||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||
import eu.kanade.tachiyomi.source.model.MetadataMangasPage
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import exh.metadata.metadata.RaisedSearchMetadata
|
||||
import mihon.domain.manga.model.toDomainManga
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
import tachiyomi.domain.source.repository.SourcePagingSourceType
|
||||
import tachiyomi.domain.manga.interactor.NetworkToLocalManga
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.domain.source.repository.SourcePagingSource
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class SourceSearchPagingSource(source: CatalogueSource, val query: String, val filters: FilterList) :
|
||||
SourcePagingSource(source) {
|
||||
class SourceSearchPagingSource(
|
||||
source: CatalogueSource,
|
||||
private val query: String,
|
||||
private val filters: FilterList,
|
||||
) : BaseSourcePagingSource(source) {
|
||||
override suspend fun requestNextPage(currentPage: Int): MangasPage {
|
||||
return source?.getSearchManga(currentPage, query, filters)
|
||||
// KMK -->
|
||||
|
|
@ -20,7 +27,7 @@ class SourceSearchPagingSource(source: CatalogueSource, val query: String, val f
|
|||
}
|
||||
}
|
||||
|
||||
class SourcePopularPagingSource(source: CatalogueSource) : SourcePagingSource(source) {
|
||||
class SourcePopularPagingSource(source: CatalogueSource) : BaseSourcePagingSource(source) {
|
||||
override suspend fun requestNextPage(currentPage: Int): MangasPage {
|
||||
return source?.getPopularManga(currentPage)
|
||||
// KMK -->
|
||||
|
|
@ -29,7 +36,7 @@ class SourcePopularPagingSource(source: CatalogueSource) : SourcePagingSource(so
|
|||
}
|
||||
}
|
||||
|
||||
class SourceLatestPagingSource(source: CatalogueSource) : SourcePagingSource(source) {
|
||||
class SourceLatestPagingSource(source: CatalogueSource) : BaseSourcePagingSource(source) {
|
||||
override suspend fun requestNextPage(currentPage: Int): MangasPage {
|
||||
return source?.getLatestUpdates(currentPage)
|
||||
// KMK -->
|
||||
|
|
@ -38,37 +45,38 @@ class SourceLatestPagingSource(source: CatalogueSource) : SourcePagingSource(sou
|
|||
}
|
||||
}
|
||||
|
||||
abstract class SourcePagingSource(
|
||||
abstract class BaseSourcePagingSource(
|
||||
protected open val source: CatalogueSource?,
|
||||
) : SourcePagingSourceType() {
|
||||
protected val networkToLocalManga: NetworkToLocalManga = Injekt.get(),
|
||||
) : SourcePagingSource() {
|
||||
|
||||
abstract suspend fun requestNextPage(currentPage: Int): MangasPage
|
||||
|
||||
override suspend fun load(
|
||||
params: LoadParams<Long>,
|
||||
): LoadResult<Long, /*SY --> */ Pair<SManga, RaisedSearchMetadata?>/*SY <-- */> {
|
||||
): LoadResult<Long, /*SY --> */ Pair<Manga, RaisedSearchMetadata?>/*SY <-- */> {
|
||||
val page = params.key ?: 1
|
||||
|
||||
val mangasPage = try {
|
||||
withIOContext {
|
||||
return try {
|
||||
val mangasPage = withIOContext {
|
||||
requestNextPage(page.toInt())
|
||||
.takeIf { it.mangas.isNotEmpty() }
|
||||
?: throw NoResultsException()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
return LoadResult.Error(e)
|
||||
}
|
||||
|
||||
// SY -->
|
||||
return getPageLoadResult(params, mangasPage)
|
||||
// SY <--
|
||||
// SY -->
|
||||
getPageLoadResult(params, mangasPage)
|
||||
// SY <--
|
||||
} catch (e: Exception) {
|
||||
LoadResult.Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
// SY -->
|
||||
open fun getPageLoadResult(
|
||||
open suspend fun getPageLoadResult(
|
||||
params: LoadParams<Long>,
|
||||
mangasPage: MangasPage,
|
||||
): LoadResult.Page<Long, /*SY --> */ Pair<SManga, RaisedSearchMetadata?>/*SY <-- */> {
|
||||
): LoadResult.Page<Long, /*SY --> */ Pair<Manga, RaisedSearchMetadata?>/*SY <-- */> {
|
||||
val page = params.key ?: 1
|
||||
|
||||
// SY -->
|
||||
|
|
@ -79,11 +87,14 @@ abstract class SourcePagingSource(
|
|||
}
|
||||
// SY <--
|
||||
|
||||
val manga = mangasPage.mangas.map { it.toDomainManga(source!!.id) }
|
||||
.let { networkToLocalManga(it) }
|
||||
// SY -->
|
||||
.mapIndexed { index, manga -> manga to metadata.getOrNull(index) }
|
||||
// SY <--
|
||||
|
||||
return LoadResult.Page(
|
||||
data = mangasPage.mangas
|
||||
// SY -->
|
||||
.mapIndexed { index, sManga -> sManga to metadata.getOrNull(index) },
|
||||
// SY <--
|
||||
data = manga,
|
||||
prevKey = null,
|
||||
nextKey = if (mangasPage.hasNextPage) page + 1 else null,
|
||||
)
|
||||
|
|
@ -91,7 +102,7 @@ abstract class SourcePagingSource(
|
|||
// SY <--
|
||||
|
||||
override fun getRefreshKey(
|
||||
state: PagingState<Long, /*SY --> */ Pair<SManga, RaisedSearchMetadata?>/*SY <-- */>,
|
||||
state: PagingState<Long, /*SY --> */ Pair<Manga, RaisedSearchMetadata?>/*SY <-- */>,
|
||||
): Long? {
|
||||
return state.anchorPosition?.let { anchorPosition ->
|
||||
val anchorPage = state.closestPageToPosition(anchorPosition)
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import kotlinx.coroutines.flow.map
|
|||
import tachiyomi.data.DatabaseHandler
|
||||
import tachiyomi.domain.source.model.SourceWithCount
|
||||
import tachiyomi.domain.source.model.StubSource
|
||||
import tachiyomi.domain.source.repository.SourcePagingSourceType
|
||||
import tachiyomi.domain.source.repository.SourcePagingSource
|
||||
import tachiyomi.domain.source.repository.SourceRepository
|
||||
import tachiyomi.domain.source.service.SourceManager
|
||||
import tachiyomi.domain.source.model.Source as DomainSource
|
||||
|
|
@ -77,7 +77,7 @@ class SourceRepositoryImpl(
|
|||
sourceId: Long,
|
||||
query: String,
|
||||
filterList: FilterList,
|
||||
): SourcePagingSourceType {
|
||||
): SourcePagingSource {
|
||||
val source = sourceManager.get(sourceId) as CatalogueSource
|
||||
// SY -->
|
||||
if (source.isEhBasedSource()) {
|
||||
|
|
@ -87,7 +87,7 @@ class SourceRepositoryImpl(
|
|||
return SourceSearchPagingSource(source, query, filterList)
|
||||
}
|
||||
|
||||
override fun getPopular(sourceId: Long): SourcePagingSourceType {
|
||||
override fun getPopular(sourceId: Long): SourcePagingSource {
|
||||
val source = sourceManager.get(sourceId) as CatalogueSource
|
||||
// SY -->
|
||||
if (source.isEhBasedSource()) {
|
||||
|
|
@ -97,7 +97,7 @@ class SourceRepositoryImpl(
|
|||
return SourcePopularPagingSource(source)
|
||||
}
|
||||
|
||||
override fun getLatest(sourceId: Long): SourcePagingSourceType {
|
||||
override fun getLatest(sourceId: Long): SourcePagingSource {
|
||||
val source = sourceManager.get(sourceId) as CatalogueSource
|
||||
// SY -->
|
||||
if (source.isEhBasedSource()) {
|
||||
|
|
|
|||
|
|
@ -195,6 +195,34 @@ WHERE _id = :mangaId;
|
|||
selectLastInsertedRowId:
|
||||
SELECT last_insert_rowid();
|
||||
|
||||
insertNetworkManga {
|
||||
-- Insert the manga if it doesn't exist already
|
||||
INSERT INTO mangas(
|
||||
source, url, artist, author, description, genre, title, status, thumbnail_url, favorite,
|
||||
last_update, next_update, initialized, viewer, chapter_flags, cover_last_modified, date_added,
|
||||
update_strategy, calculate_interval, last_modified_at, version
|
||||
)
|
||||
SELECT
|
||||
:source, :url, :artist, :author, :description, :genre, :title, :status, :thumbnailUrl, :favorite,
|
||||
:lastUpdate, :nextUpdate, :initialized, :viewerFlags, :chapterFlags, :coverLastModified, :dateAdded,
|
||||
:updateStrategy, :calculateInterval, 0, :version
|
||||
WHERE NOT EXISTS(SELECT 0 FROM mangas WHERE source = :source AND url = :url);
|
||||
|
||||
-- Update the title if it is not favorite
|
||||
UPDATE mangas
|
||||
SET title = :title
|
||||
WHERE source = :source
|
||||
AND url = :url
|
||||
AND favorite = 0;
|
||||
|
||||
-- Finally return the manga
|
||||
SELECT *
|
||||
FROM mangas
|
||||
WHERE source = :source
|
||||
AND url = :url
|
||||
LIMIT 1;
|
||||
}
|
||||
|
||||
getEhMangaWithMetadata:
|
||||
SELECT mangas.* FROM mangas
|
||||
INNER JOIN search_metadata
|
||||
|
|
|
|||
22
domain/src/main/java/mihon/domain/manga/model/SManga.kt
Normal file
22
domain/src/main/java/mihon/domain/manga/model/SManga.kt
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package mihon.domain.manga.model
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
|
||||
fun SManga.toDomainManga(sourceId: Long): Manga {
|
||||
return Manga.create().copy(
|
||||
url = url,
|
||||
// SY -->
|
||||
ogTitle = title,
|
||||
ogArtist = artist,
|
||||
ogAuthor = author,
|
||||
ogDescription = description,
|
||||
ogGenre = getGenres(),
|
||||
ogStatus = status.toLong(),
|
||||
ogThumbnailUrl = thumbnail_url,
|
||||
// SY <--
|
||||
updateStrategy = update_strategy,
|
||||
initialized = initialized,
|
||||
source = sourceId,
|
||||
)
|
||||
}
|
||||
|
|
@ -7,29 +7,11 @@ class NetworkToLocalManga(
|
|||
private val mangaRepository: MangaRepository,
|
||||
) {
|
||||
|
||||
suspend fun await(manga: Manga): Manga {
|
||||
val localManga = getManga(manga.url, manga.source)
|
||||
return when {
|
||||
localManga == null -> {
|
||||
val id = insertManga(manga)
|
||||
manga.copy(id = id!!)
|
||||
}
|
||||
!localManga.favorite -> {
|
||||
// if the manga isn't a favorite, set its display title from source
|
||||
// if it later becomes a favorite, updated title will go to db
|
||||
localManga.copy(/* SY --> */ogTitle/* SY <-- */ = manga.title)
|
||||
}
|
||||
else -> {
|
||||
localManga
|
||||
}
|
||||
}
|
||||
suspend operator fun invoke(manga: Manga): Manga {
|
||||
return mangaRepository.insertNetworkManga(listOf(manga)).single()
|
||||
}
|
||||
|
||||
private suspend fun getManga(url: String, sourceId: Long): Manga? {
|
||||
return mangaRepository.getMangaByUrlAndSourceId(url, sourceId)
|
||||
}
|
||||
|
||||
private suspend fun insertManga(manga: Manga): Long? {
|
||||
return mangaRepository.insert(manga)
|
||||
suspend operator fun invoke(manga: List<Manga>): List<Manga> {
|
||||
return mangaRepository.insertNetworkManga(manga)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,12 +33,12 @@ interface MangaRepository {
|
|||
|
||||
suspend fun setMangaCategories(mangaId: Long, categoryIds: List<Long>)
|
||||
|
||||
suspend fun insert(manga: Manga): Long?
|
||||
|
||||
suspend fun update(update: MangaUpdate): Boolean
|
||||
|
||||
suspend fun updateAll(mangaUpdates: List<MangaUpdate>): Boolean
|
||||
|
||||
suspend fun insertNetworkManga(manga: List<Manga>): List<Manga>
|
||||
|
||||
// SY -->
|
||||
suspend fun getMangaBySourceId(sourceId: Long): List<Manga>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
package tachiyomi.domain.source.interactor
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import tachiyomi.domain.source.repository.SourcePagingSourceType
|
||||
import tachiyomi.domain.source.repository.SourcePagingSource
|
||||
import tachiyomi.domain.source.repository.SourceRepository
|
||||
|
||||
class GetRemoteManga(
|
||||
private val repository: SourceRepository,
|
||||
) {
|
||||
|
||||
fun subscribe(sourceId: Long, query: String, filterList: FilterList): SourcePagingSourceType {
|
||||
operator fun invoke(sourceId: Long, query: String, filterList: FilterList): SourcePagingSource {
|
||||
return when (query) {
|
||||
QUERY_POPULAR -> repository.getPopular(sourceId)
|
||||
QUERY_LATEST -> repository.getLatest(sourceId)
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ package tachiyomi.domain.source.repository
|
|||
|
||||
import androidx.paging.PagingSource
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import exh.metadata.metadata.RaisedSearchMetadata
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.domain.source.model.Source
|
||||
import tachiyomi.domain.source.model.SourceWithCount
|
||||
|
||||
typealias SourcePagingSourceType = PagingSource<Long, /*SY --> */ Pair<SManga, RaisedSearchMetadata?>/*SY <-- */>
|
||||
typealias SourcePagingSource = PagingSource<Long, /*SY --> */ Pair<Manga, RaisedSearchMetadata?>/*SY <-- */>
|
||||
|
||||
interface SourceRepository {
|
||||
|
||||
|
|
@ -20,9 +20,9 @@ interface SourceRepository {
|
|||
|
||||
fun getSourcesWithNonLibraryManga(): Flow<List<SourceWithCount>>
|
||||
|
||||
fun search(sourceId: Long, query: String, filterList: FilterList): SourcePagingSourceType
|
||||
fun search(sourceId: Long, query: String, filterList: FilterList): SourcePagingSource
|
||||
|
||||
fun getPopular(sourceId: Long): SourcePagingSourceType
|
||||
fun getPopular(sourceId: Long): SourcePagingSource
|
||||
|
||||
fun getLatest(sourceId: Long): SourcePagingSourceType
|
||||
fun getLatest(sourceId: Long): SourcePagingSource
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue