mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-31 14:27:57 +01:00 
			
		
		
		
	Release version v6.1.4
Offload metadata check to background thread Add search cache
This commit is contained in:
		| @@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.library | ||||
| import eu.davidea.flexibleadapter.FlexibleAdapter | ||||
| import eu.kanade.tachiyomi.data.database.models.Manga | ||||
| import exh.* | ||||
| import exh.metadata.metadataClass | ||||
| import exh.metadata.models.ExGalleryMetadata | ||||
| import exh.metadata.models.NHentaiMetadata | ||||
| import exh.metadata.models.PervEdenGalleryMetadata | ||||
| @@ -12,6 +13,7 @@ import exh.search.SearchEngine | ||||
| import exh.util.defRealm | ||||
| import io.realm.RealmResults | ||||
| import timber.log.Timber | ||||
| import java.util.concurrent.ConcurrentHashMap | ||||
| import kotlin.concurrent.thread | ||||
|  | ||||
| /** | ||||
| @@ -42,11 +44,9 @@ class LibraryCategoryAdapter(val view: LibraryCategoryView) : | ||||
|         // Sync manga IDs in background (EH) | ||||
|         thread { | ||||
|             //Wait 1s to reduce UI stutter during animations | ||||
|             Thread.sleep(1000) | ||||
|             Thread.sleep(2000) | ||||
|             defRealm { | ||||
|                 it.syncMangaIds(list.map { | ||||
|                     it.manga | ||||
|                 }) | ||||
|                 it.syncMangaIds(mangas) | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -64,93 +64,103 @@ class LibraryCategoryAdapter(val view: LibraryCategoryView) : | ||||
|  | ||||
|     fun performFilter() { | ||||
|         if(searchText.isNotBlank()) { | ||||
|             if(cacheText != searchText) { | ||||
|                 globalSearchCache.clear() | ||||
|                 cacheText = searchText | ||||
|             } | ||||
|  | ||||
|             try { | ||||
|                 val parsedQuery = searchEngine.parseQuery(searchText) | ||||
|                 val metadata = view.meta!!.map { | ||||
|                     val meta: RealmResults<out SearchableGalleryMetadata> = if (it.value.isNotEmpty()) | ||||
|                         searchEngine.filterResults(it.value.where(), | ||||
|                                 parsedQuery, | ||||
|                                 it.value.first().titleFields) | ||||
|                                 .findAllSorted(SearchableGalleryMetadata::mangaId.name) | ||||
|                     else | ||||
|                         it.value | ||||
|                     Pair(it.key, meta) | ||||
|                 }.toMap() | ||||
|                 // --> Possible data set compare algorithm? | ||||
| //            var curUnfilteredMetaIndex = 0 | ||||
| //            var curFilteredMetaIndex = 0 | ||||
| //            val res = mangas.sortedBy { it.manga.id }.filter { manga -> | ||||
|                 val res = mangas.filter { manga -> | ||||
|                     // --> EH | ||||
|                     try { | ||||
|                         if (isLewdSource(manga.manga.source)) { | ||||
|                             val unfilteredMeta: RealmResults<out SearchableGalleryMetadata>? | ||||
|                             val filteredMeta: RealmResults<out SearchableGalleryMetadata>? | ||||
|                             when (manga.manga.source) { | ||||
|                                 EH_SOURCE_ID, | ||||
|                                 EXH_SOURCE_ID -> { | ||||
|                                     unfilteredMeta = view.meta!![ExGalleryMetadata::class] | ||||
|                                     filteredMeta = metadata[ExGalleryMetadata::class] | ||||
|                                 } | ||||
|                                 PERV_EDEN_IT_SOURCE_ID, | ||||
|                                 PERV_EDEN_EN_SOURCE_ID -> { | ||||
|                                     unfilteredMeta = view.meta!![PervEdenGalleryMetadata::class] | ||||
|                                     filteredMeta = metadata[PervEdenGalleryMetadata::class] | ||||
|                                 } | ||||
|                                 NHENTAI_SOURCE_ID -> { | ||||
|                                     unfilteredMeta = view.meta!![NHentaiMetadata::class] | ||||
|                                     filteredMeta = metadata[NHentaiMetadata::class] | ||||
|                                 } | ||||
|                                 else -> { | ||||
|                                     unfilteredMeta = null | ||||
|                                     filteredMeta = null | ||||
|                 val thisCache = globalSearchCache.getOrPut(view.category.name, { | ||||
|                     SearchCache(mangas.size) | ||||
|                 }) | ||||
|  | ||||
|                 if(thisCache.ready) { | ||||
|                     //Skip everything if cache matches our query exactly | ||||
|                     updateDataSet(mangas.filter { | ||||
|                         thisCache.cache[it.manga.id] ?: false | ||||
|                     }) | ||||
|                 } else { | ||||
|                     thisCache.cache.clear() | ||||
|  | ||||
|                     val parsedQuery = searchEngine.parseQuery(searchText) | ||||
|                     var totalFilteredSize = 0 | ||||
|  | ||||
|                     val metadata = view.controller.meta!!.map { | ||||
|                         val meta: RealmResults<out SearchableGalleryMetadata> = if (it.value.isNotEmpty()) | ||||
|                             searchEngine.filterResults(it.value.where(), | ||||
|                                     parsedQuery, | ||||
|                                     it.value.first().titleFields) | ||||
|                                     .findAllSorted(SearchableGalleryMetadata::mangaId.name).apply { | ||||
|                                 totalFilteredSize += size | ||||
|                             } | ||||
|                         else | ||||
|                             it.value | ||||
|                         Pair(it.key, meta) | ||||
|                     }.toMap() | ||||
|  | ||||
|                     val out = ArrayList<LibraryItem>(mangas.size) | ||||
|  | ||||
|                     var lewdMatches = 0 | ||||
|  | ||||
|                     for(manga in mangas) { | ||||
|                         // --> EH | ||||
|                         try { | ||||
|                             if (isLewdSource(manga.manga.source)) { | ||||
|                                 //Stop matching lewd manga if we have matched them all already! | ||||
|                                 if (lewdMatches >= totalFilteredSize) | ||||
|                                     continue | ||||
|  | ||||
|                                 val metaClass = manga.manga.metadataClass | ||||
|                                 val unfilteredMeta = view.controller.meta!![metaClass] | ||||
|                                 val filteredMeta = metadata[metaClass] | ||||
|  | ||||
|                                 val hasMeta = manga.hasMetadata ?: (unfilteredMeta | ||||
|                                         ?.where() | ||||
|                                         ?.equalTo(SearchableGalleryMetadata::mangaId.name, manga.manga.id) | ||||
|                                         ?.count() ?: 0 > 0) | ||||
|  | ||||
|                                 if (hasMeta) { | ||||
|                                     if (filteredMeta!!.where() | ||||
|                                             .equalTo(SearchableGalleryMetadata::mangaId.name, manga.manga.id) | ||||
|                                             .count() > 0) { | ||||
|                                         //Metadata match! | ||||
|                                         lewdMatches++ | ||||
|                                         thisCache.cache[manga.manga.id!!] = true | ||||
|                                         out += manga | ||||
|                                         continue | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
|  | ||||
|                             /* | ||||
|                     --> Possible data set compare algorithm? | ||||
|  | ||||
|                     var atUnfilteredMeta = unfilteredMeta?.getOrNull(curUnfilteredMetaIndex) | ||||
|  | ||||
|                     while(atUnfilteredMeta?.mangaId != null | ||||
|                             && atUnfilteredMeta.mangaId!! < manga.manga.id!!) { | ||||
|                         curUnfilteredMetaIndex++ | ||||
|                         atUnfilteredMeta = unfilteredMeta?.getOrNull(curUnfilteredMetaIndex) | ||||
|                     } | ||||
|  | ||||
|                     if(atUnfilteredMeta?.mangaId == manga.manga.id) { | ||||
|                         var atFilteredMeta = filteredMeta?.getOrNull(curFilteredMetaIndex) | ||||
|                         while(atFilteredMeta?.mangaId != null | ||||
|                                 && atFilteredMeta.mangaId!! < manga.manga.id!!) { | ||||
|                             curFilteredMetaIndex++ | ||||
|                             atFilteredMeta = filteredMeta?.getOrNull(curFilteredMetaIndex) | ||||
|                         } catch (e: Exception) { | ||||
|                             Timber.w(e, "Could not filter manga!", manga.manga) | ||||
|                         } | ||||
|  | ||||
|                         return@filter atFilteredMeta?.mangaId == manga.manga.id | ||||
|                     }*/ | ||||
|                             val hasMeta = unfilteredMeta | ||||
|                                     ?.where() | ||||
|                                     ?.equalTo(SearchableGalleryMetadata::mangaId.name, manga.manga.id) | ||||
|                                     ?.count() ?: 0 > 0 | ||||
|                             if (hasMeta) | ||||
|                                 return@filter filteredMeta!!.where() | ||||
|                                         .equalTo(SearchableGalleryMetadata::mangaId.name, manga.manga.id) | ||||
|                                         .count() > 0 | ||||
|                         } | ||||
|                     } catch(e: Exception) { | ||||
|                         Timber.w(e, "Could not filter manga!", manga.manga) | ||||
|                         //Fallback to regular filter | ||||
|                         val filterRes = manga.filter(searchText) | ||||
|                         thisCache.cache[manga.manga.id!!] = filterRes | ||||
|                         if(filterRes) out += manga | ||||
|                         // <-- EH | ||||
|                     } | ||||
|                     manga.filter(searchText) | ||||
|                     // <-- EH | ||||
|                     thisCache.ready = true | ||||
|                     updateDataSet(out) | ||||
|                 } | ||||
|                 updateDataSet(res) | ||||
|             } catch(e: Exception) { | ||||
|                 Timber.w(e, "Could not filter mangas!") | ||||
|                 updateDataSet(mangas) | ||||
|             } | ||||
|         } else { | ||||
|             globalSearchCache.clear() | ||||
|             updateDataSet(mangas) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     class SearchCache(size: Int) { | ||||
|         var ready = false | ||||
|         var cache = HashMap<Long, Boolean>(size) | ||||
|     } | ||||
|  | ||||
|     companion object { | ||||
|         var cacheText: String? = null | ||||
|         val globalSearchCache = ConcurrentHashMap<String, SearchCache>() | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -16,14 +16,9 @@ import eu.kanade.tachiyomi.util.inflate | ||||
| import eu.kanade.tachiyomi.util.plusAssign | ||||
| import eu.kanade.tachiyomi.util.toast | ||||
| import eu.kanade.tachiyomi.widget.AutofitRecyclerView | ||||
| import exh.metadata.loadAllMetadata | ||||
| import exh.metadata.models.SearchableGalleryMetadata | ||||
| import io.realm.Realm | ||||
| import io.realm.RealmResults | ||||
| import kotlinx.android.synthetic.main.library_category.view.* | ||||
| import rx.subscriptions.CompositeSubscription | ||||
| import uy.kohesive.injekt.injectLazy | ||||
| import kotlin.reflect.KClass | ||||
|  | ||||
| /** | ||||
|  * Fragment containing the library manga for a certain category. | ||||
| @@ -41,7 +36,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att | ||||
|     /** | ||||
|      * The fragment containing this view. | ||||
|      */ | ||||
|     private lateinit var controller: LibraryController | ||||
|     lateinit var controller: LibraryController | ||||
|  | ||||
|     /** | ||||
|      * Category for this view. | ||||
| @@ -64,14 +59,6 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att | ||||
|      */ | ||||
|     private var subscriptions = CompositeSubscription() | ||||
|  | ||||
|     // --> EH | ||||
|     // Cached Realm instance | ||||
|     var realm: Realm? = null | ||||
|  | ||||
|     // Cached metadata (auto-updating) | ||||
|     var meta: Map<KClass<out SearchableGalleryMetadata>, RealmResults<out SearchableGalleryMetadata>>? = null | ||||
|     // <-- EH | ||||
|  | ||||
|     fun onCreate(controller: LibraryController) { | ||||
|         this.controller = controller | ||||
|  | ||||
| @@ -139,22 +126,9 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att | ||||
|         subscriptions.clear() | ||||
|     } | ||||
|  | ||||
|     override fun onAttachedToWindow() { | ||||
|         // --> EH | ||||
|         realm?.close() | ||||
|         realm = Realm.getDefaultInstance()?.apply { | ||||
|             meta = loadAllMetadata() | ||||
|         } | ||||
|         // <-- EH | ||||
|         super.onAttachedToWindow() | ||||
|     } | ||||
|  | ||||
|     override fun onDetachedFromWindow() { | ||||
|         subscriptions.clear() | ||||
|         // --> EH | ||||
|         meta = null | ||||
|         realm?.close() | ||||
|         // <-- EH | ||||
|         super.onDetachedFromWindow() | ||||
|     } | ||||
|  | ||||
| @@ -270,5 +244,4 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att | ||||
|         controller.setSelection(item.manga, !adapter.isSelected(position)) | ||||
|         controller.invalidateActionMode() | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -36,6 +36,10 @@ import eu.kanade.tachiyomi.ui.manga.MangaController | ||||
| import eu.kanade.tachiyomi.util.inflate | ||||
| import eu.kanade.tachiyomi.util.toast | ||||
| import eu.kanade.tachiyomi.widget.DrawerSwipeCloseListener | ||||
| import exh.metadata.loadAllMetadata | ||||
| import exh.metadata.models.SearchableGalleryMetadata | ||||
| import io.realm.Realm | ||||
| import io.realm.RealmResults | ||||
| import kotlinx.android.synthetic.main.main_activity.* | ||||
| import kotlinx.android.synthetic.main.library_controller.view.* | ||||
| import rx.Subscription | ||||
| @@ -45,6 +49,7 @@ import uy.kohesive.injekt.Injekt | ||||
| import uy.kohesive.injekt.api.get | ||||
| import java.io.IOException | ||||
| import java.util.concurrent.TimeUnit | ||||
| import kotlin.reflect.KClass | ||||
|  | ||||
|  | ||||
| class LibraryController( | ||||
| @@ -126,6 +131,13 @@ class LibraryController( | ||||
|  | ||||
|     private var tabsVisibilitySubscription: Subscription? = null | ||||
|  | ||||
|     // --> EH | ||||
|     //Cached realm | ||||
|     var realm: Realm? = null | ||||
|     //Cached metadata | ||||
|     var meta: Map<KClass<out SearchableGalleryMetadata>, RealmResults<out SearchableGalleryMetadata>>? = null | ||||
|     // <-- EH | ||||
|  | ||||
|     init { | ||||
|         setHasOptionsMenu(true) | ||||
|     } | ||||
| @@ -142,6 +154,16 @@ class LibraryController( | ||||
|         return inflater.inflate(R.layout.library_controller, container, false) | ||||
|     } | ||||
|  | ||||
|     // --> EH | ||||
|     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup, savedViewState: Bundle?): View { | ||||
|         //Load realm | ||||
|         realm = Realm.getDefaultInstance()?.apply { | ||||
|             meta = loadAllMetadata() | ||||
|         } | ||||
|         return super.onCreateView(inflater, container, savedViewState) | ||||
|     } | ||||
|     // <-- EH | ||||
|  | ||||
|     override fun onViewCreated(view: View, savedViewState: Bundle?) { | ||||
|         super.onViewCreated(view, savedViewState) | ||||
|  | ||||
| @@ -183,6 +205,12 @@ class LibraryController( | ||||
|         actionMode = null | ||||
|         tabsVisibilitySubscription?.unsubscribe() | ||||
|         tabsVisibilitySubscription = null | ||||
|  | ||||
|         // --> EH | ||||
|         //Clean up realm | ||||
|         realm?.close() | ||||
|         meta = null | ||||
|         // <-- EH | ||||
|     } | ||||
|  | ||||
|     override fun createSecondaryDrawer(drawer: DrawerLayout): ViewGroup { | ||||
|   | ||||
| @@ -15,6 +15,10 @@ import eu.kanade.tachiyomi.widget.AutofitRecyclerView | ||||
| import kotlinx.android.synthetic.main.catalogue_grid_item.view.* | ||||
|  | ||||
| class LibraryItem(val manga: Manga) : AbstractFlexibleItem<LibraryHolder>(), IFilterable { | ||||
|     // Temp metadata holder EH | ||||
|     @Volatile | ||||
|     var hasMetadata: Boolean? = null | ||||
|  | ||||
|     override fun getLayoutRes(): Int { | ||||
|         return R.layout.catalogue_grid_item | ||||
|     } | ||||
|   | ||||
| @@ -2,6 +2,7 @@ package exh.metadata | ||||
|  | ||||
| import eu.kanade.tachiyomi.data.database.models.Manga | ||||
| import eu.kanade.tachiyomi.source.model.SManga | ||||
| import eu.kanade.tachiyomi.ui.library.LibraryItem | ||||
| import exh.* | ||||
| import exh.metadata.models.ExGalleryMetadata | ||||
| import exh.metadata.models.NHentaiMetadata | ||||
| @@ -116,20 +117,32 @@ fun Realm.queryMetadataFromManga(manga: Manga, | ||||
|         else -> throw IllegalArgumentException("Unknown source type!") | ||||
|     } | ||||
|  | ||||
| fun Realm.syncMangaIds(mangas: List<Manga>) { | ||||
| fun Realm.syncMangaIds(mangas: List<LibraryItem>) { | ||||
|     Timber.d("--> EH: Begin syncing ${mangas.size} manga IDs...") | ||||
|     executeTransaction { | ||||
|         mangas.filter { | ||||
|             isLewdSource(it.source) | ||||
|             isLewdSource(it.manga.source) | ||||
|         }.forEach { manga -> | ||||
|             try { | ||||
|                 queryMetadataFromManga(manga).findFirst()?.let { meta -> | ||||
|                     meta.mangaId = manga.id | ||||
|                 } | ||||
|                 manga.hasMetadata = | ||||
|                         queryMetadataFromManga(manga.manga).findFirst()?.let { meta -> | ||||
|                             meta.mangaId = manga.manga.id | ||||
|                             true | ||||
|                         } ?: false | ||||
|             } catch(e: Exception) { | ||||
|                 Timber.w(e, "Error syncing manga IDs! Ignoring...") | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     Timber.d("--> EH: Finish syncing ${mangas.size} manga IDs!") | ||||
| } | ||||
| } | ||||
|  | ||||
| val Manga.metadataClass | ||||
|     get() = when (source) { | ||||
|         EH_SOURCE_ID, | ||||
|         EXH_SOURCE_ID -> ExGalleryMetadata::class | ||||
|         PERV_EDEN_IT_SOURCE_ID, | ||||
|         PERV_EDEN_EN_SOURCE_ID -> PervEdenGalleryMetadata::class | ||||
|         NHENTAI_SOURCE_ID -> NHentaiMetadata::class | ||||
|         else -> null | ||||
|     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user