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