From b5ae4c0d43158268e4a3933b9882b7d50504f73f Mon Sep 17 00:00:00 2001 From: arkon Date: Wed, 26 Oct 2022 23:01:21 -0400 Subject: [PATCH] Refactor network to local manga logic Maybe fixes #8289 (cherry picked from commit d5b4bb49b168adc5b7c3934e530571497c85a916) # Conflicts: # app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SearchPresenter.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchPresenter.kt --- .../kanade/data/manga/MangaRepositoryImpl.kt | 2 +- .../java/eu/kanade/domain/DomainModule.kt | 4 +- .../domain/manga/interactor/GetManga.kt | 8 ++-- .../domain/manga/interactor/InsertManga.kt | 13 ----- .../manga/interactor/NetworkToLocalManga.kt | 35 ++++++++++++++ .../eu/kanade/domain/manga/model/Manga.kt | 17 +++++++ .../data/library/LibraryUpdateService.kt | 24 +++++----- .../source/online/all/MergedSource.kt | 17 ++++--- .../tachiyomi/ui/browse/feed/FeedPresenter.kt | 39 ++------------- .../process/MigrationListPresenter.kt | 38 ++------------- .../source/browse/BrowseSourcePresenter.kt | 38 +++------------ .../browse/source/feed/SourceFeedPresenter.kt | 39 ++------------- .../globalsearch/GlobalSearchPresenter.kt | 39 +++++---------- .../tachiyomi/ui/manga/MangaPresenter.kt | 47 +++++++++---------- app/src/main/java/exh/GalleryAdder.kt | 20 ++++---- .../java/exh/smartsearch/SmartSearchEngine.kt | 43 ++--------------- .../ui/smartsearch/SmartSearchPresenter.kt | 11 ++++- 17 files changed, 157 insertions(+), 277 deletions(-) delete mode 100644 app/src/main/java/eu/kanade/domain/manga/interactor/InsertManga.kt create mode 100644 app/src/main/java/eu/kanade/domain/manga/interactor/NetworkToLocalManga.kt diff --git a/app/src/main/java/eu/kanade/data/manga/MangaRepositoryImpl.kt b/app/src/main/java/eu/kanade/data/manga/MangaRepositoryImpl.kt index be2b78c07..b52f092f0 100644 --- a/app/src/main/java/eu/kanade/data/manga/MangaRepositoryImpl.kt +++ b/app/src/main/java/eu/kanade/data/manga/MangaRepositoryImpl.kt @@ -27,7 +27,7 @@ class MangaRepositoryImpl( } override suspend fun getMangaByUrlAndSourceId(url: String, sourceId: Long): Manga? { - return handler.awaitOneOrNull { mangasQueries.getMangaByUrlAndSource(url, sourceId, mangaMapper) } + return handler.awaitOneOrNull(inTransaction = true) { mangasQueries.getMangaByUrlAndSource(url, sourceId, mangaMapper) } } override fun getMangaByUrlAndSourceIdAsFlow(url: String, sourceId: Long): Flow { diff --git a/app/src/main/java/eu/kanade/domain/DomainModule.kt b/app/src/main/java/eu/kanade/domain/DomainModule.kt index 14d9c761d..ab28ba8ef 100644 --- a/app/src/main/java/eu/kanade/domain/DomainModule.kt +++ b/app/src/main/java/eu/kanade/domain/DomainModule.kt @@ -44,7 +44,7 @@ import eu.kanade.domain.manga.interactor.GetFavorites import eu.kanade.domain.manga.interactor.GetLibraryManga import eu.kanade.domain.manga.interactor.GetManga import eu.kanade.domain.manga.interactor.GetMangaWithChapters -import eu.kanade.domain.manga.interactor.InsertManga +import eu.kanade.domain.manga.interactor.NetworkToLocalManga import eu.kanade.domain.manga.interactor.ResetViewerFlags import eu.kanade.domain.manga.interactor.SetMangaChapterFlags import eu.kanade.domain.manga.interactor.SetMangaViewerFlags @@ -98,7 +98,7 @@ class DomainModule : InjektModule { addFactory { SetMangaChapterFlags(get()) } addFactory { SetMangaDefaultChapterFlags(get(), get(), get()) } addFactory { SetMangaViewerFlags(get()) } - addFactory { InsertManga(get()) } + addFactory { NetworkToLocalManga(get()) } addFactory { UpdateManga(get()) } addFactory { SetMangaCategories(get()) } diff --git a/app/src/main/java/eu/kanade/domain/manga/interactor/GetManga.kt b/app/src/main/java/eu/kanade/domain/manga/interactor/GetManga.kt index 41352921a..4c98da4c2 100644 --- a/app/src/main/java/eu/kanade/domain/manga/interactor/GetManga.kt +++ b/app/src/main/java/eu/kanade/domain/manga/interactor/GetManga.kt @@ -24,15 +24,15 @@ class GetManga( return mangaRepository.getMangaByIdAsFlow(id) } - suspend fun await(url: String, sourceId: Long): Manga? { - return mangaRepository.getMangaByUrlAndSourceId(url, sourceId) - } - fun subscribe(url: String, sourceId: Long): Flow { return mangaRepository.getMangaByUrlAndSourceIdAsFlow(url, sourceId) } // SY --> + suspend fun await(url: String, sourceId: Long): Manga? { + return mangaRepository.getMangaByUrlAndSourceId(url, sourceId) + } + override suspend fun awaitId(url: String, sourceId: Long): Long? { return await(url, sourceId)?.id } diff --git a/app/src/main/java/eu/kanade/domain/manga/interactor/InsertManga.kt b/app/src/main/java/eu/kanade/domain/manga/interactor/InsertManga.kt deleted file mode 100644 index 2477d91f2..000000000 --- a/app/src/main/java/eu/kanade/domain/manga/interactor/InsertManga.kt +++ /dev/null @@ -1,13 +0,0 @@ -package eu.kanade.domain.manga.interactor - -import eu.kanade.domain.manga.model.Manga -import eu.kanade.domain.manga.repository.MangaRepository - -class InsertManga( - private val mangaRepository: MangaRepository, -) { - - suspend fun await(manga: Manga): Long? { - return mangaRepository.insert(manga) - } -} diff --git a/app/src/main/java/eu/kanade/domain/manga/interactor/NetworkToLocalManga.kt b/app/src/main/java/eu/kanade/domain/manga/interactor/NetworkToLocalManga.kt new file mode 100644 index 000000000..d31a8bff7 --- /dev/null +++ b/app/src/main/java/eu/kanade/domain/manga/interactor/NetworkToLocalManga.kt @@ -0,0 +1,35 @@ +package eu.kanade.domain.manga.interactor + +import eu.kanade.domain.manga.model.Manga +import eu.kanade.domain.manga.repository.MangaRepository + +class NetworkToLocalManga( + private val mangaRepository: MangaRepository, +) { + + suspend fun await(manga: Manga, sourceId: Long): Manga { + val localManga = getManga(manga.url, sourceId) + 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 + } + } + } + + 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) + } +} diff --git a/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt b/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt index 7dcfc4413..72bca15dc 100644 --- a/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt +++ b/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt @@ -295,6 +295,23 @@ fun Manga.toMangaUpdate(): MangaUpdate { ) } +fun SManga.toDomainManga(): Manga { + return Manga.create().copy( + url = url, + // SY --> + ogTitle = title, + ogArtist = artist, + ogAuthor = author, + ogDescription = description, + ogGenre = getGenres(), + ogStatus = status.toLong(), + // SY <-- + thumbnailUrl = thumbnail_url, + updateStrategy = update_strategy, + initialized = initialized, + ) +} + fun Manga.isLocal(): Boolean = source == LocalSource.ID fun Manga.hasCustomCover(coverCache: CoverCache = Injekt.get()): Boolean { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt index 6a71605ec..648e3c451 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt @@ -25,7 +25,7 @@ import eu.kanade.domain.manga.interactor.GetLibraryManga import eu.kanade.domain.manga.interactor.GetManga import eu.kanade.domain.manga.interactor.GetMergedMangaForDownloading import eu.kanade.domain.manga.interactor.InsertFlatMetadata -import eu.kanade.domain.manga.interactor.InsertManga +import eu.kanade.domain.manga.interactor.NetworkToLocalManga import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.manga.model.Manga import eu.kanade.domain.manga.model.toDbManga @@ -119,7 +119,7 @@ class LibraryUpdateService( // SY --> private val getFavorites: GetFavorites = Injekt.get(), private val insertFlatMetadata: InsertFlatMetadata = Injekt.get(), - private val insertManga: InsertManga = Injekt.get(), + private val networkToLocalManga: NetworkToLocalManga = Injekt.get(), private val getMergedMangaForDownloading: GetMergedMangaForDownloading = Injekt.get(), // SY <-- ) : Service() { @@ -728,18 +728,16 @@ class LibraryUpdateService( var dbManga = getManga.await(networkManga.url, mangaDex.id) if (dbManga == null) { - val newManga = Manga.create().copy( - url = networkManga.url, - ogTitle = networkManga.title, - source = mangaDex.id, - favorite = true, - dateAdded = System.currentTimeMillis(), + dbManga = networkToLocalManga.await( + Manga.create().copy( + url = networkManga.url, + ogTitle = networkManga.title, + source = mangaDex.id, + favorite = true, + dateAdded = System.currentTimeMillis(), + ), + mangaDex.id, ) - val result = runBlocking { - val id = insertManga.await(newManga) - getManga.await(id!!) - } - dbManga = result ?: return } else if (!dbManga.favorite) { updateManga.awaitUpdateFavorite(dbManga.id, true) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MergedSource.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MergedSource.kt index ad357e3f6..123706c8d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MergedSource.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MergedSource.kt @@ -8,7 +8,7 @@ import eu.kanade.domain.chapter.model.toDbChapter import eu.kanade.domain.download.service.DownloadPreferences import eu.kanade.domain.manga.interactor.GetManga import eu.kanade.domain.manga.interactor.GetMergedReferencesById -import eu.kanade.domain.manga.interactor.InsertManga +import eu.kanade.domain.manga.interactor.NetworkToLocalManga import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.manga.model.Manga import eu.kanade.tachiyomi.data.download.DownloadManager @@ -40,7 +40,7 @@ class MergedSource : HttpSource() { private val getManga: GetManga by injectLazy() private val getMergedReferencesById: GetMergedReferencesById by injectLazy() private val getMergedChaptersByMangaId: GetMergedChapterByMangaId by injectLazy() - private val insertManga: InsertManga by injectLazy() + private val networkToLocalManga: NetworkToLocalManga by injectLazy() private val updateManga: UpdateManga by injectLazy() private val getCategories: GetCategories by injectLazy() private val sourceManager: SourceManager by injectLazy() @@ -187,8 +187,7 @@ class MergedSource : HttpSource() { semaphore.withPermit { values.flatMap { try { - val (source, loadedManga, reference) = - it.load(sourceManager, getManga, insertManga, updateManga) + val (source, loadedManga, reference) = it.load() if (loadedManga != null && reference.getChapterUpdates) { val chapterList = source.getChapterList(loadedManga.toSManga()) val results = @@ -219,19 +218,19 @@ class MergedSource : HttpSource() { } } - suspend fun MergedMangaReference.load(sourceManager: SourceManager, getManga: GetManga, insertManga: InsertManga, updateManga: UpdateManga): LoadedMangaSource { + suspend fun MergedMangaReference.load(): LoadedMangaSource { var manga = getManga.await(mangaUrl, mangaSourceId) val source = sourceManager.getOrStub(manga?.source ?: mangaSourceId) if (manga == null) { - val id = insertManga.await( + val newManga = networkToLocalManga.await( Manga.create().copy( source = mangaSourceId, url = mangaUrl, ), - )!! - val newManga = getManga.await(id)!! + mangaSourceId, + ) updateManga.awaitUpdateFromSource(newManga, source.getMangaDetails(newManga.toSManga()), false) - manga = getManga.await(id)!! + manga = getManga.await(newManga.id)!! } return LoadedMangaSource(source, manga, this) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/feed/FeedPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/feed/FeedPresenter.kt index e3b4c361b..a68e2f07c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/feed/FeedPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/feed/FeedPresenter.kt @@ -4,9 +4,9 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.State import androidx.compose.runtime.produceState import eu.kanade.domain.manga.interactor.GetManga -import eu.kanade.domain.manga.interactor.InsertManga +import eu.kanade.domain.manga.interactor.NetworkToLocalManga import eu.kanade.domain.manga.interactor.UpdateManga -import eu.kanade.domain.manga.model.toDbManga +import eu.kanade.domain.manga.model.toDomainManga import eu.kanade.domain.manga.model.toMangaUpdate import eu.kanade.domain.source.interactor.CountFeedSavedSearchGlobal import eu.kanade.domain.source.interactor.DeleteFeedSavedSearchById @@ -18,13 +18,10 @@ import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.presentation.browse.FeedItemUI import eu.kanade.presentation.browse.FeedState import eu.kanade.presentation.browse.FeedStateImpl -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.database.models.toDomainManga import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.MangasPage -import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.launchNonCancellable import eu.kanade.tachiyomi.util.lang.withIOContext @@ -60,7 +57,7 @@ open class FeedPresenter( val sourceManager: SourceManager = Injekt.get(), val sourcePreferences: SourcePreferences = Injekt.get(), private val getManga: GetManga = Injekt.get(), - private val insertManga: InsertManga = Injekt.get(), + private val networkToLocalManga: NetworkToLocalManga = Injekt.get(), private val updateManga: UpdateManga = Injekt.get(), private val getFeedSavedSearchGlobal: GetFeedSavedSearchGlobal = Injekt.get(), private val getSavedSearchGlobalFeed: GetSavedSearchGlobalFeed = Injekt.get(), @@ -204,8 +201,8 @@ open class FeedPresenter( .subscribeOn(Schedulers.io()) .onErrorReturn { MangasPage(emptyList(), false) } // Ignore timeouts or other exceptions .map { it.mangas } // Get manga from search result. - .map { list -> list.map { networkToLocalManga(it, itemUI.source.id) } } // Convert to local manga. - .map { list -> itemUI.copy(results = list.mapNotNull { it.toDomainManga() }) } + .map { list -> runBlocking { list.map { networkToLocalManga.await(it.toDomainManga(), itemUI.source.id) } } } // Convert to local manga. + .map { list -> itemUI.copy(results = list) } } else { Observable.just(itemUI.copy(results = emptyList())) } @@ -255,32 +252,6 @@ open class FeedPresenter( } } - /** - * Returns a manga from the database for the given manga from network. It creates a new entry - * if the manga is not yet in the database. - * - * @param sManga the manga from the source. - * @return a manga from the database. - */ - private fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga { - var localManga = runBlocking { getManga.await(sManga.url, sourceId) } - if (localManga == null) { - val newManga = Manga.create(sManga.url, sManga.title, sourceId) - newManga.copyFrom(sManga) - newManga.id = -1 - val result = runBlocking { - val id = insertManga.await(newManga.toDomainManga()!!) - getManga.await(id!!) - } - localManga = result - } else if (!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 = localManga.copy(ogTitle = sManga.title) - } - return localManga?.toDbManga()!! - } - /** * Initialize a manga. * diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationListPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationListPresenter.kt index 2bcdba380..89e065f1d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationListPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationListPresenter.kt @@ -15,7 +15,7 @@ import eu.kanade.domain.history.interactor.UpsertHistory import eu.kanade.domain.history.model.HistoryUpdate import eu.kanade.domain.manga.interactor.GetManga import eu.kanade.domain.manga.interactor.GetMergedReferencesById -import eu.kanade.domain.manga.interactor.InsertManga +import eu.kanade.domain.manga.interactor.NetworkToLocalManga import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.manga.model.Manga import eu.kanade.domain.manga.model.MangaUpdate @@ -26,12 +26,10 @@ import eu.kanade.domain.track.interactor.GetTracks import eu.kanade.domain.track.interactor.InsertTrack import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.cache.CoverCache -import eu.kanade.tachiyomi.data.database.models.toDomainManga import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.getNameForMangaInfo -import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.all.EHentai import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.browse.migration.MigrationFlags @@ -64,7 +62,7 @@ class MigrationListPresenter( private val sourceManager: SourceManager = Injekt.get(), private val coverCache: CoverCache = Injekt.get(), private val getManga: GetManga = Injekt.get(), - private val insertManga: InsertManga = Injekt.get(), + private val networkToLocalManga: NetworkToLocalManga = Injekt.get(), private val updateManga: UpdateManga = Injekt.get(), private val syncChaptersWithSource: SyncChaptersWithSource = Injekt.get(), private val updateChapter: UpdateChapter = Injekt.get(), @@ -182,7 +180,7 @@ class MigrationListPresenter( } if (searchResult != null && !(searchResult.url == mangaObj.url && source.id == mangaObj.source)) { - val localManga = networkToLocalManga( + val localManga = networkToLocalManga.await( searchResult, source.id, ) @@ -222,7 +220,7 @@ class MigrationListPresenter( } if (searchResult != null) { - val localManga = networkToLocalManga(searchResult, source.id) + val localManga = networkToLocalManga.await(searchResult, source.id) val chapters = try { if (source is EHentai) { source.getChapterList(localManga.toSManga(), throttleManager::throttle) @@ -385,7 +383,7 @@ class MigrationListPresenter( migratingManga.searchResult.value = SearchResult.Searching presenterScope.launchIO { val result = migratingManga.migrationScope.async { - val localManga = networkToLocalManga(manga.toDbManga(), source.id) + val localManga = networkToLocalManga.await(manga, source.id) try { val chapters = source.getChapterList(localManga.toSManga()) syncChaptersWithSource.await(chapters, localManga, source) @@ -524,30 +522,4 @@ class MigrationListPresenter( it.migrationScope.cancel() } } - - /** - * Returns a manga from the database for the given manga from network. It creates a new entry - * if the manga is not yet in the database. - * - * @param sManga the manga from the source. - * @return a manga from the database. - */ - private suspend fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga { - var localManga = getManga.await(sManga.url, sourceId) - if (localManga == null) { - val newManga = eu.kanade.tachiyomi.data.database.models.Manga.create(sManga.url, sManga.title, sourceId) - newManga.copyFrom(sManga) - newManga.id = -1 - val result = run { - val id = insertManga.await(newManga.toDomainManga()!!) - getManga.await(id!!) - } - localManga = result - } else if (!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 = localManga.copy(ogTitle = sManga.title) - } - return localManga!! - } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt index 658944d67..6567e7405 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt @@ -30,9 +30,10 @@ import eu.kanade.domain.library.service.LibraryPreferences import eu.kanade.domain.manga.interactor.GetDuplicateLibraryManga import eu.kanade.domain.manga.interactor.GetFlatMetadataById import eu.kanade.domain.manga.interactor.GetManga -import eu.kanade.domain.manga.interactor.InsertManga +import eu.kanade.domain.manga.interactor.NetworkToLocalManga import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.manga.model.toDbManga +import eu.kanade.domain.manga.model.toDomainManga import eu.kanade.domain.manga.model.toMangaUpdate import eu.kanade.domain.source.interactor.DeleteSavedSearchById import eu.kanade.domain.source.interactor.GetExhSavedSearch @@ -45,8 +46,6 @@ import eu.kanade.domain.track.model.toDomainTrack import eu.kanade.presentation.browse.BrowseSourceState import eu.kanade.presentation.browse.BrowseSourceStateImpl import eu.kanade.tachiyomi.data.cache.CoverCache -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.database.models.toDomainManga import eu.kanade.tachiyomi.data.track.EnhancedTrackService import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackService @@ -55,7 +54,6 @@ import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.FilterList -import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.MetadataSource import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.browse.source.filter.AutoComplete @@ -124,7 +122,7 @@ open class BrowseSourcePresenter( private val getChapterByMangaId: GetChapterByMangaId = Injekt.get(), private val setMangaCategories: SetMangaCategories = Injekt.get(), private val setMangaDefaultChapterFlags: SetMangaDefaultChapterFlags = Injekt.get(), - private val insertManga: InsertManga = Injekt.get(), + private val networkToLocalManga: NetworkToLocalManga = Injekt.get(), private val updateManga: UpdateManga = Injekt.get(), private val insertTrack: InsertTrack = Injekt.get(), private val syncChaptersWithTrackServiceTwoWay: SyncChaptersWithTrackServiceTwoWay = Injekt.get(), @@ -170,11 +168,11 @@ open class BrowseSourcePresenter( createSourcePagingSource(currentFilter.query, currentFilter.filters) }.flow .map { - it.map { + it.map { (sManga, metadata) -> // SY --> withIOContext { - networkToLocalManga(it.first, sourceId).toDomainManga()!! - } to it.second + networkToLocalManga.await(sManga.toDomainManga(), sourceId) + } to metadata // SY <-- } } @@ -272,30 +270,6 @@ open class BrowseSourcePresenter( // SY <-- } - /** - * Returns a manga from the database for the given manga from network. It creates a new entry - * if the manga is not yet in the database. - * - * @param sManga the manga from the source. - * @return a manga from the database. - */ - private suspend fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga { - var localManga = getManga.await(sManga.url, sourceId) - if (localManga == null) { - val newManga = Manga.create(sManga.url, sManga.title, sourceId) - newManga.copyFrom(sManga) - newManga.id = -1 - val id = insertManga.await(newManga.toDomainManga()!!) - val result = getManga.await(id!!) - localManga = result - } else if (!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 = localManga.copy(ogTitle = sManga.title) - } - return localManga?.toDbManga()!! - } - /** * Initialize a manga. * diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/feed/SourceFeedPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/feed/SourceFeedPresenter.kt index c3fb51d17..a59f88d79 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/feed/SourceFeedPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/feed/SourceFeedPresenter.kt @@ -7,9 +7,9 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.produceState import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.manga.interactor.GetManga -import eu.kanade.domain.manga.interactor.InsertManga +import eu.kanade.domain.manga.interactor.NetworkToLocalManga import eu.kanade.domain.manga.interactor.UpdateManga -import eu.kanade.domain.manga.model.toDbManga +import eu.kanade.domain.manga.model.toDomainManga import eu.kanade.domain.manga.model.toMangaUpdate import eu.kanade.domain.source.interactor.CountFeedSavedSearchBySourceId import eu.kanade.domain.source.interactor.DeleteFeedSavedSearchById @@ -20,12 +20,9 @@ import eu.kanade.domain.source.interactor.InsertFeedSavedSearch import eu.kanade.presentation.browse.SourceFeedState import eu.kanade.presentation.browse.SourceFeedStateImpl import eu.kanade.presentation.browse.SourceFeedUI -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.database.models.toDomainManga 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.SManga import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.util.lang.launchNonCancellable import eu.kanade.tachiyomi.util.lang.withIOContext @@ -60,7 +57,7 @@ open class SourceFeedPresenter( val source: CatalogueSource, private val preferences: BasePreferences = Injekt.get(), private val getManga: GetManga = Injekt.get(), - private val insertManga: InsertManga = Injekt.get(), + private val networkToLocalManga: NetworkToLocalManga = Injekt.get(), private val updateManga: UpdateManga = Injekt.get(), private val getFeedSavedSearchBySourceId: GetFeedSavedSearchBySourceId = Injekt.get(), private val getSavedSearchBySourceIdFeed: GetSavedSearchBySourceIdFeed = Injekt.get(), @@ -162,8 +159,8 @@ open class SourceFeedPresenter( .subscribeOn(Schedulers.io()) .onErrorReturn { MangasPage(emptyList(), false) } // Ignore timeouts or other exceptions .map { it.mangas } // Get manga from search result. - .map { list -> list.map { networkToLocalManga(it, source.id) } } // Convert to local manga. - .map { list -> sourceFeed.withResults(list.mapNotNull { it.toDomainManga() }) } + .map { list -> runBlocking { list.map { networkToLocalManga.await(it.toDomainManga(), source.id) } } } // Convert to local manga. + .map { list -> sourceFeed.withResults(list) } }, 5, ) @@ -211,32 +208,6 @@ open class SourceFeedPresenter( } } - /** - * Returns a manga from the database for the given manga from network. It creates a new entry - * if the manga is not yet in the database. - * - * @param sManga the manga from the source. - * @return a manga from the database. - */ - private fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga { - var localManga = runBlocking { getManga.await(sManga.url, sourceId) } - if (localManga == null) { - val newManga = Manga.create(sManga.url, sManga.title, sourceId) - newManga.copyFrom(sManga) - newManga.id = -1 - val result = runBlocking { - val id = insertManga.await(newManga.toDomainManga()!!) - getManga.await(id!!) - } - localManga = result - } else if (!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 = localManga.copy(ogTitle = sManga.title) - } - return localManga?.toDbManga()!! - } - /** * Initialize a manga. * diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchPresenter.kt index 81407fcee..a3e50cbb0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchPresenter.kt @@ -2,10 +2,10 @@ package eu.kanade.tachiyomi.ui.browse.source.globalsearch import android.os.Bundle import eu.kanade.domain.base.BasePreferences -import eu.kanade.domain.manga.interactor.GetManga -import eu.kanade.domain.manga.interactor.InsertManga +import eu.kanade.domain.manga.interactor.NetworkToLocalManga import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.manga.model.toDbManga +import eu.kanade.domain.manga.model.toDomainManga import eu.kanade.domain.manga.model.toMangaUpdate import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.tachiyomi.data.database.models.Manga @@ -30,6 +30,7 @@ import rx.subjects.PublishSubject import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy +import eu.kanade.domain.manga.model.Manga as DomainManga open class GlobalSearchPresenter( private val initialQuery: String? = "", @@ -38,8 +39,7 @@ open class GlobalSearchPresenter( val sourceManager: SourceManager = Injekt.get(), val preferences: BasePreferences = Injekt.get(), val sourcePreferences: SourcePreferences = Injekt.get(), - private val getManga: GetManga = Injekt.get(), - private val insertManga: InsertManga = Injekt.get(), + private val networkToLocalManga: NetworkToLocalManga = Injekt.get(), private val updateManga: UpdateManga = Injekt.get(), ) : BasePresenter() { @@ -56,7 +56,7 @@ open class GlobalSearchPresenter( /** * Subject which fetches image of given manga. */ - private val fetchImageSubject = PublishSubject.create, Source>>() + private val fetchImageSubject = PublishSubject.create, Source>>() /** * Subscription for fetching images of manga. @@ -172,9 +172,9 @@ open class GlobalSearchPresenter( .subscribeOn(Schedulers.io()) .onErrorReturn { MangasPage(emptyList(), false) } // Ignore timeouts or other exceptions .map { it.mangas } - .map { list -> list.map { networkToLocalManga(it, source.id) } } // Convert to local manga + .map { list -> list.map { runBlocking { networkToLocalManga(it, source.id) } } } // Convert to local manga .doOnNext { fetchImage(it, source) } // Load manga covers - .map { list -> createCatalogueSearchItem(source, list.map { GlobalSearchCardItem(it.toDomainManga()!!) }) } + .map { list -> createCatalogueSearchItem(source, list.map { GlobalSearchCardItem(it) }) } }, 5, ) @@ -212,7 +212,7 @@ open class GlobalSearchPresenter( * * @param manga the list of manga to initialize. */ - private fun fetchImage(manga: List, source: Source) { + private fun fetchImage(manga: List, source: Source) { fetchImageSubject.onNext(Pair(manga, source)) } @@ -224,9 +224,9 @@ open class GlobalSearchPresenter( fetchImageSubscription = fetchImageSubject.observeOn(Schedulers.io()) .flatMap { (first, source) -> Observable.from(first) - .filter { it.thumbnail_url == null && !it.initialized } + .filter { it.thumbnailUrl == null && !it.initialized } .map { Pair(it, source) } - .concatMap { runAsObservable { getMangaDetails(it.first, it.second) } } + .concatMap { runAsObservable { getMangaDetails(it.first.toDbManga(), it.second) } } .map { Pair(source as CatalogueSource, it) } } .onBackpressureBuffer() @@ -263,22 +263,7 @@ open class GlobalSearchPresenter( * @param sManga the manga from the source. * @return a manga from the database. */ - protected open fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga { - var localManga = runBlocking { getManga.await(sManga.url, sourceId) } - if (localManga == null) { - val newManga = Manga.create(sManga.url, sManga.title, sourceId) - newManga.copyFrom(sManga) - newManga.id = -1 - val result = runBlocking { - val id = insertManga.await(newManga.toDomainManga()!!) - getManga.await(id!!) - } - localManga = result - } else if (!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 = localManga.copy(ogTitle = sManga.title) - } - return localManga!!.toDbManga() + protected open suspend fun networkToLocalManga(sManga: SManga, sourceId: Long): DomainManga { + return networkToLocalManga.await(sManga.toDomainManga(), sourceId) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt index ab229eca9..fd9ff0b04 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt @@ -29,8 +29,8 @@ import eu.kanade.domain.manga.interactor.GetMangaWithChapters import eu.kanade.domain.manga.interactor.GetMergedMangaById import eu.kanade.domain.manga.interactor.GetMergedReferencesById import eu.kanade.domain.manga.interactor.GetPagePreviews -import eu.kanade.domain.manga.interactor.InsertManga import eu.kanade.domain.manga.interactor.InsertMergedReference +import eu.kanade.domain.manga.interactor.NetworkToLocalManga import eu.kanade.domain.manga.interactor.SetMangaChapterFlags import eu.kanade.domain.manga.interactor.SetMangaFilteredScanlators import eu.kanade.domain.manga.interactor.UpdateManga @@ -49,7 +49,6 @@ import eu.kanade.domain.track.model.toDomainTrack import eu.kanade.domain.ui.UiPreferences import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Track -import eu.kanade.tachiyomi.data.database.models.toDomainManga import eu.kanade.tachiyomi.data.download.DownloadCache import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.model.Download @@ -125,7 +124,6 @@ import java.text.DecimalFormatSymbols import java.util.Date import eu.kanade.domain.chapter.model.Chapter as DomainChapter import eu.kanade.domain.manga.model.Manga as DomainManga -import eu.kanade.tachiyomi.data.database.models.Manga.Companion as DbManga class MangaPresenter( val mangaId: Long, @@ -149,7 +147,7 @@ class MangaPresenter( private val getMergedReferencesById: GetMergedReferencesById = Injekt.get(), private val insertMergedReference: InsertMergedReference = Injekt.get(), private val updateMergedSettings: UpdateMergedSettings = Injekt.get(), - private val insertManga: InsertManga = Injekt.get(), + private val networkToLocalManga: NetworkToLocalManga = Injekt.get(), private val deleteMangaById: DeleteMangaById = Injekt.get(), private val deleteByMergeId: DeleteByMergeId = Injekt.get(), private val getFlatMetadata: GetFlatMetadataById = Injekt.get(), @@ -577,15 +575,20 @@ class MangaPresenter( return originalManga } else { - val mergedManga = DbManga.create(originalManga.url, originalManga.title, MERGED_SOURCE_ID).apply { - copyFrom(originalManga.toSManga()) - favorite = true - last_update = originalManga.lastUpdate - viewer_flags = originalManga.viewerFlags.toInt() - chapter_flags = originalManga.chapterFlags.toInt() - sorting = DomainManga.CHAPTER_SORTING_NUMBER.toInt() - date_added = System.currentTimeMillis() - } + var mergedManga = DomainManga.create() + .copy( + url = originalManga.url, + ogTitle = originalManga.title, + source = MERGED_SOURCE_ID, + ) + .copyFrom(originalManga.toSManga()) + .copy( + favorite = true, + lastUpdate = originalManga.lastUpdate, + viewerFlags = originalManga.viewerFlags, + chapterFlags = originalManga.chapterFlags, + dateAdded = System.currentTimeMillis(), + ) var existingManga = getManga.await(mergedManga.url, mergedManga.source) while (existingManga != null) { @@ -602,15 +605,11 @@ class MangaPresenter( existingManga = getManga.await(mergedManga.url, mergedManga.source) } - // Reload chapters immediately - mergedManga.initialized = false - mergedManga.id = -1 - val newId = insertManga.await(mergedManga.toDomainManga()!!) - mergedManga.id = newId ?: throw NullPointerException("Invalid new manga id") + mergedManga = networkToLocalManga.await(mergedManga, mergedManga.source) getCategories.await(originalMangaId) .let { - setMangaCategories.await(newId, it.map { it.id }) + setMangaCategories.await(mergedManga.id, it.map { it.id }) } val originalMangaReference = MergedMangaReference( @@ -620,7 +619,7 @@ class MangaPresenter( chapterSortMode = 0, chapterPriority = 0, downloadChapters = true, - mergeId = mergedManga.id!!, + mergeId = mergedManga.id, mergeUrl = mergedManga.url, mangaId = originalManga.id, mangaUrl = originalManga.url, @@ -634,7 +633,7 @@ class MangaPresenter( chapterSortMode = 0, chapterPriority = 0, downloadChapters = true, - mergeId = mergedManga.id!!, + mergeId = mergedManga.id, mergeUrl = mergedManga.url, mangaId = manga.id, mangaUrl = manga.url, @@ -648,16 +647,16 @@ class MangaPresenter( chapterSortMode = 0, chapterPriority = -1, downloadChapters = false, - mergeId = mergedManga.id!!, + mergeId = mergedManga.id, mergeUrl = mergedManga.url, - mangaId = mergedManga.id!!, + mangaId = mergedManga.id, mangaUrl = mergedManga.url, mangaSourceId = MERGED_SOURCE_ID, ) insertMergedReference.awaitAll(listOf(originalMangaReference, newMangaReference, mergedMangaReference)) - return mergedManga.toDomainManga()!! + return mergedManga } // Note that if the manga are merged in a different order, this won't trigger, but I don't care lol diff --git a/app/src/main/java/exh/GalleryAdder.kt b/app/src/main/java/exh/GalleryAdder.kt index 186815aa9..142aea538 100755 --- a/app/src/main/java/exh/GalleryAdder.kt +++ b/app/src/main/java/exh/GalleryAdder.kt @@ -6,7 +6,7 @@ import eu.kanade.domain.chapter.interactor.GetChapter import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource import eu.kanade.domain.chapter.model.Chapter import eu.kanade.domain.manga.interactor.GetManga -import eu.kanade.domain.manga.interactor.InsertManga +import eu.kanade.domain.manga.interactor.NetworkToLocalManga import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.manga.model.Manga import eu.kanade.domain.source.service.SourcePreferences @@ -22,7 +22,7 @@ import uy.kohesive.injekt.api.get class GalleryAdder( private val getManga: GetManga = Injekt.get(), private val updateManga: UpdateManga = Injekt.get(), - private val insertManga: InsertManga = Injekt.get(), + private val networkToLocalManga: NetworkToLocalManga = Injekt.get(), private val getChapter: GetChapter = Injekt.get(), private val syncChaptersWithSource: SyncChaptersWithSource = Injekt.get(), private val sourceManager: SourceManager = Injekt.get(), @@ -129,15 +129,13 @@ class GalleryAdder( // Use manga in DB if possible, otherwise, make a new manga var manga = getManga.await(cleanedMangaUrl, source.id) - ?: run { - insertManga.await( - Manga.create().copy( - source = source.id, - url = cleanedMangaUrl, - ), - ) - getManga.await(cleanedMangaUrl, source.id)!! - } + ?: networkToLocalManga.await( + Manga.create().copy( + source = source.id, + url = cleanedMangaUrl, + ), + source.id, + ) // Fetch and copy details val newManga = source.getMangaDetails(manga.toSManga()) diff --git a/app/src/main/java/exh/smartsearch/SmartSearchEngine.kt b/app/src/main/java/exh/smartsearch/SmartSearchEngine.kt index ff40338ff..dec502894 100644 --- a/app/src/main/java/exh/smartsearch/SmartSearchEngine.kt +++ b/app/src/main/java/exh/smartsearch/SmartSearchEngine.kt @@ -1,9 +1,7 @@ package exh.smartsearch -import eu.kanade.domain.manga.interactor.GetManga -import eu.kanade.domain.manga.interactor.InsertManga import eu.kanade.domain.manga.model.Manga -import eu.kanade.tachiyomi.data.database.models.toDomainManga +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 @@ -12,19 +10,14 @@ import info.debatty.java.stringsimilarity.NormalizedLevenshtein import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.supervisorScope -import uy.kohesive.injekt.injectLazy import java.util.Locale -import eu.kanade.tachiyomi.data.database.models.Manga.Companion as DbManga class SmartSearchEngine( private val extraSearchParams: String? = null, ) { - private val getManga: GetManga by injectLazy() - private val insertManga: InsertManga by injectLazy() - private val normalizedLevenshtein = NormalizedLevenshtein() - suspend fun smartSearch(source: CatalogueSource, title: String): SManga? { + suspend fun smartSearch(source: CatalogueSource, title: String): Manga? { val cleanedTitle = cleanSmartSearchTitle(title) val queries = getSmartSearchQueries(cleanedTitle) @@ -51,10 +44,10 @@ class SmartSearchEngine( }.flatMap { it.await() } } - return eligibleManga.maxByOrNull { it.dist }?.manga + return eligibleManga.maxByOrNull { it.dist }?.manga?.toDomainManga() } - suspend fun normalSearch(source: CatalogueSource, title: String): SManga? { + suspend fun normalSearch(source: CatalogueSource, title: String): Manga? { val eligibleManga = supervisorScope { val searchQuery = if (extraSearchParams != null) { "$title ${extraSearchParams.trim()}" @@ -75,7 +68,7 @@ class SmartSearchEngine( } } - return eligibleManga.maxByOrNull { it.dist }?.manga + return eligibleManga.maxByOrNull { it.dist }?.manga?.toDomainManga() } private fun getSmartSearchQueries(cleanedTitle: String): List { @@ -169,32 +162,6 @@ class SmartSearchEngine( return result.toString() } - /** - * Returns a manga from the database for the given manga from network. It creates a new entry - * if the manga is not yet in the database. - * - * @param sManga the manga from the source. - * @return a manga from the database. - */ - suspend fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga { - var localManga = getManga.await(sManga.url, sourceId) - if (localManga == null) { - val newManga = DbManga.create(sManga.url, sManga.title, sourceId) - newManga.copyFrom(sManga) - newManga.id = -1 - val result = run { - val id = insertManga.await(newManga.toDomainManga()!!) - getManga.await(id!!) - } - localManga = result - } else if (!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 = localManga.copy(ogTitle = sManga.title) - } - return localManga!! - } - companion object { const val MIN_SMART_ELIGIBLE_THRESHOLD = 0.4 const val MIN_NORMAL_ELIGIBLE_THRESHOLD = 0.4 diff --git a/app/src/main/java/exh/ui/smartsearch/SmartSearchPresenter.kt b/app/src/main/java/exh/ui/smartsearch/SmartSearchPresenter.kt index 3852387b4..75fb659f2 100644 --- a/app/src/main/java/exh/ui/smartsearch/SmartSearchPresenter.kt +++ b/app/src/main/java/exh/ui/smartsearch/SmartSearchPresenter.kt @@ -1,6 +1,7 @@ package exh.ui.smartsearch import android.os.Bundle +import eu.kanade.domain.manga.interactor.NetworkToLocalManga import eu.kanade.domain.manga.model.Manga import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter @@ -10,8 +11,14 @@ import exh.smartsearch.SmartSearchEngine import kotlinx.coroutines.CancellationException import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get -class SmartSearchPresenter(private val source: CatalogueSource, private val config: SourcesController.SmartSearchConfig) : +class SmartSearchPresenter( + private val source: CatalogueSource, + private val config: SourcesController.SmartSearchConfig, + private val networkToLocalManga: NetworkToLocalManga = Injekt.get(), +) : BasePresenter() { private val _smartSearchFlow = MutableSharedFlow() @@ -26,7 +33,7 @@ class SmartSearchPresenter(private val source: CatalogueSource, private val conf val result = try { val resultManga = smartSearchEngine.smartSearch(source, config.origTitle) if (resultManga != null) { - val localManga = smartSearchEngine.networkToLocalManga(resultManga, source.id) + val localManga = networkToLocalManga.await(resultManga, source.id) SearchResults.Found(localManga) } else { SearchResults.NotFound