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 9fcb8f79d..43d0be0e9 100644
--- a/app/src/main/java/eu/kanade/data/manga/MangaRepositoryImpl.kt
+++ b/app/src/main/java/eu/kanade/data/manga/MangaRepositoryImpl.kt
@@ -25,7 +25,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<Manga?> {
diff --git a/app/src/main/java/eu/kanade/domain/DomainModule.kt b/app/src/main/java/eu/kanade/domain/DomainModule.kt
index b4047afef..751e6f5ea 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 e2c54b993..22518acc4 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
@@ -23,10 +23,6 @@ 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<Manga?> {
         return mangaRepository.getMangaByUrlAndSourceIdAsFlow(url, sourceId)
     }
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..b8793d635
--- /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(title = 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 ba577518c..57b38f4a7 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
@@ -232,6 +232,21 @@ fun Manga.toMangaUpdate(): MangaUpdate {
     )
 }
 
+fun SManga.toDomainManga(): Manga {
+    return Manga.create().copy(
+        url = url,
+        title = title,
+        artist = artist,
+        author = author,
+        description = description,
+        genre = getGenres(),
+        status = status.toLong(),
+        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/ui/browse/migration/search/SearchPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SearchPresenter.kt
index 97ae7bc24..936814bbe 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SearchPresenter.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SearchPresenter.kt
@@ -79,11 +79,10 @@ class SearchPresenter(
         return GlobalSearchItem(source, results, source.id == manga.source)
     }
 
-    override fun networkToLocalManga(sManga: SManga, sourceId: Long): eu.kanade.tachiyomi.data.database.models.Manga {
+    override suspend fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga {
         val localManga = super.networkToLocalManga(sManga, sourceId)
         // For migration, displayed title should always match source rather than local DB
-        localManga.title = sManga.title
-        return localManga
+        return localManga.copy(title = sManga.title)
     }
 
     fun migrateManga(prevManga: Manga, manga: Manga, replace: Boolean) {
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 51c9a6880..12db91691 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
@@ -28,9 +28,10 @@ import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay
 import eu.kanade.domain.library.service.LibraryPreferences
 import eu.kanade.domain.manga.interactor.GetDuplicateLibraryManga
 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.GetRemoteManga
 import eu.kanade.domain.source.service.SourcePreferences
@@ -39,8 +40,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
@@ -49,7 +48,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.ui.base.presenter.BasePresenter
 import eu.kanade.tachiyomi.ui.browse.source.filter.CheckboxItem
 import eu.kanade.tachiyomi.ui.browse.source.filter.CheckboxSectionItem
@@ -97,7 +95,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(),
@@ -131,9 +129,9 @@ open class BrowseSourcePresenter(
                 getRemoteManga.subscribe(sourceId, currentFilter.query, currentFilter.filters)
             }.flow
                 .map {
-                    it.map {
+                    it.map { sManga ->
                         withIOContext {
-                            networkToLocalManga(it, sourceId).toDomainManga()!!
+                            networkToLocalManga.await(sManga.toDomainManga(), sourceId)
                         }
                     }
                 }
@@ -183,30 +181,6 @@ open class BrowseSourcePresenter(
         state.filters = source!!.getFilterList()
     }
 
-    /**
-     * 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(title = 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 483752ca6..3aee01d46 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? = "",
@@ -37,8 +38,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<GlobalSearchController>() {
 
@@ -55,7 +55,7 @@ open class GlobalSearchPresenter(
     /**
      * Subject which fetches image of given manga.
      */
-    private val fetchImageSubject = PublishSubject.create<Pair<List<Manga>, Source>>()
+    private val fetchImageSubject = PublishSubject.create<Pair<List<DomainManga>, Source>>()
 
     /**
      * Subscription for fetching images of manga.
@@ -168,9 +168,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,
             )
@@ -208,7 +208,7 @@ open class GlobalSearchPresenter(
      *
      * @param manga the list of manga to initialize.
      */
-    private fun fetchImage(manga: List<Manga>, source: Source) {
+    private fun fetchImage(manga: List<DomainManga>, source: Source) {
         fetchImageSubject.onNext(Pair(manga, source))
     }
 
@@ -220,9 +220,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()
@@ -259,22 +259,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(title = sManga.title)
-        }
-        return localManga!!.toDbManga()
+    protected open suspend fun networkToLocalManga(sManga: SManga, sourceId: Long): DomainManga {
+        return networkToLocalManga.await(sManga.toDomainManga(), sourceId)
     }
 }