Release version v6.1.4

Offload metadata check to background thread
Add search cache
This commit is contained in:
NerdNumber9 2017-08-28 03:20:35 -04:00
parent 1107b95ebd
commit 7053777997
6 changed files with 141 additions and 113 deletions

View File

@ -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()}\""

View File

@ -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>()
}
} }

View File

@ -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()
} }
} }

View File

@ -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 {

View File

@ -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
} }

View File

@ -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
}