Small cleanup and optimizations, add a coroutine version of insertFlatMetadata

This commit is contained in:
Jobobby04
2021-01-20 21:00:23 -05:00
parent e6d62dd1dc
commit 0a4fcb480d
25 changed files with 137 additions and 308 deletions
@@ -10,7 +10,6 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.model.toSManga
import eu.kanade.tachiyomi.util.lang.await
import exh.EH_SOURCE_ID
import exh.EXHMigrations
import exh.EXH_SOURCE_ID
@@ -18,7 +17,7 @@ import exh.eh.EHentaiThrottleManager
import exh.eh.EHentaiUpdateWorker
import exh.metadata.metadata.EHentaiSearchMetadata
import exh.metadata.metadata.base.getFlatMetadataForManga
import exh.metadata.metadata.base.insertFlatMetadata
import exh.metadata.metadata.base.insertFlatMetadataAsync
import exh.savedsearches.JsonSavedSearch
import exh.util.cancellable
import exh.util.executeOnIO
@@ -64,7 +63,7 @@ object DebugFunctions {
val meta = db.getFlatMetadataForManga(manga.id!!).executeAsBlocking()?.raise<EHentaiSearchMetadata>() ?: return@forEach
// remove age flag
meta.aged = false
db.insertFlatMetadata(meta.flatten()).await()
db.insertFlatMetadataAsync(meta.flatten()).await()
}
}
}
@@ -19,14 +19,13 @@ import eu.kanade.tachiyomi.source.model.toSChapter
import eu.kanade.tachiyomi.source.model.toSManga
import eu.kanade.tachiyomi.source.online.all.EHentai
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import eu.kanade.tachiyomi.util.lang.await
import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID
import exh.debug.DebugToggles
import exh.eh.EHentaiUpdateWorkerConstants.UPDATES_PER_ITERATION
import exh.metadata.metadata.EHentaiSearchMetadata
import exh.metadata.metadata.base.getFlatMetadataForManga
import exh.metadata.metadata.base.insertFlatMetadata
import exh.metadata.metadata.base.insertFlatMetadataAsync
import exh.util.cancellable
import exh.util.executeOnIO
import exh.util.jobScheduler
@@ -280,7 +279,7 @@ class EHentaiUpdateWorker : JobService(), CoroutineScope {
// Age dead galleries
logger.d("Aged %s - notfound", manga.id)
meta.aged = true
db.insertFlatMetadata(meta.flatten()).await()
db.insertFlatMetadataAsync(meta.flatten()).await()
}
throw GalleryNotUpdatedException(false, t)
}
@@ -10,10 +10,10 @@ import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaCategory
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.all.EHentai
import eu.kanade.tachiyomi.util.lang.await
import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.system.powerManager
import eu.kanade.tachiyomi.util.system.toast
@@ -239,17 +239,15 @@ class FavoritesSyncHelper(val context: Context) {
private suspend fun addGalleryRemote(errorList: MutableList<String>, gallery: FavoriteEntry) {
val url = "${exh.baseUrl}/gallerypopups.php?gid=${gallery.gid}&t=${gallery.token}&act=addfav"
val request = Request.Builder()
.url(url)
.post(
FormBody.Builder()
.add("favcat", gallery.category.toString())
.add("favnote", "")
.add("apply", "Add to Favorites")
.add("update", "1")
.build()
)
.build()
val request = POST(
url = url,
body = FormBody.Builder()
.add("favcat", gallery.category.toString())
.add("favnote", "")
.add("apply", "Add to Favorites")
.add("update", "1")
.build()
)
if (!explicitlyRetryExhRequest(10, request)) {
val errorString = "Unable to add gallery to remote server: '${gallery.title}' (GID: ${gallery.gid})!"
@@ -296,10 +294,10 @@ class FavoritesSyncHelper(val context: Context) {
formBody.add("modifygids[]", it.gid)
}
val request = Request.Builder()
.url("https://exhentai.org/favorites.php")
.post(formBody.build())
.build()
val request = POST(
url = "https://exhentai.org/favorites.php",
body = formBody.build()
)
if (!explicitlyRetryExhRequest(10, request)) {
val errorString = context.getString(R.string.favorites_sync_unable_to_delete)
@@ -408,8 +406,8 @@ class FavoritesSyncHelper(val context: Context) {
}
// Can't do too many DB OPs in one go
insertedMangaCategories.chunked(10).map {
Pair(it.map { it.first }, it.map { it.second })
insertedMangaCategories.chunked(10).map { mangaCategories ->
mangaCategories.map { it.first } to mangaCategories.map { it.second }
}.forEach {
db.setMangaCategories(it.first, it.second)
}
@@ -15,7 +15,8 @@ import exh.metadata.metadata.MangaDexSearchMetadata
import exh.metadata.metadata.base.RaisedTag
import exh.metadata.metadata.base.getFlatMetadataForManga
import exh.metadata.metadata.base.insertFlatMetadata
import exh.util.await
import exh.metadata.metadata.base.insertFlatMetadataAsync
import exh.util.executeOnIO
import exh.util.floor
import exh.util.nullIfZero
import okhttp3.Response
@@ -70,16 +71,16 @@ class ApiMangaParser(private val lang: String) {
}
suspend fun parseToManga(manga: MangaInfo, input: Response, coverUrls: List<String>, sourceId: Long): MangaInfo {
val mangaId = db.getManga(manga.key, sourceId).await()?.id
val mangaId = db.getManga(manga.key, sourceId).executeOnIO()?.id
val metadata = if (mangaId != null) {
val flatMetadata = db.getFlatMetadataForManga(mangaId).await()
val flatMetadata = db.getFlatMetadataForManga(mangaId).executeOnIO()
flatMetadata?.raise(metaClass) ?: newMetaInstance()
} else newMetaInstance()
parseInfoIntoMetadata(metadata, input, coverUrls)
if (mangaId != null) {
metadata.mangaId = mangaId
db.insertFlatMetadata(metadata.flatten()).await()
db.insertFlatMetadataAsync(metadata.flatten()).await()
}
return metadata.createMangaInfo(manga)
@@ -5,9 +5,12 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper
import exh.metadata.sql.models.SearchMetadata
import exh.metadata.sql.models.SearchTag
import exh.metadata.sql.models.SearchTitle
import exh.util.executeOnIO
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.serialization.InternalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.serializer
import rx.Completable
import rx.Single
@@ -99,3 +102,15 @@ fun DatabaseHelper.insertFlatMetadata(flatMetadata: FlatMetadata): Completable =
setSearchTitlesForManga(flatMetadata.metadata.mangaId, flatMetadata.titles)
}
}
suspend fun DatabaseHelper.insertFlatMetadataAsync(flatMetadata: FlatMetadata): Deferred<Unit> = coroutineScope {
async {
require(flatMetadata.metadata.mangaId != -1L)
inTransaction {
insertSearchMetadata(flatMetadata.metadata).executeOnIO()
setSearchTagsForManga(flatMetadata.metadata.mangaId, flatMetadata.tags)
setSearchTitlesForManga(flatMetadata.metadata.mangaId, flatMetadata.titles)
}
}
}
@@ -5,7 +5,6 @@ import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.lang.await
import eu.kanade.tachiyomi.util.lang.awaitSingle
import exh.util.executeOnIO
import info.debatty.java.stringsimilarity.NormalizedLevenshtein
+7 -8
View File
@@ -1,11 +1,10 @@
package exh.util
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
class DataSaver {
private val preferences: PreferencesHelper = Injekt.get()
object DataSaver {
private val preferences: PreferencesHelper by injectLazy()
fun compress(imageUrl: String): String {
return if (preferences.dataSaver().get() && preferences.dataSaverServer().get().isNotBlank() && !imageUrl.contains(preferences.dataSaverServer().get() + "/?")) {
@@ -20,10 +19,10 @@ class DataSaver {
private fun getUrl(imageUrl: String): String {
val server = preferences.dataSaverServer().get() + "/?"
val format = "jpg=${if (preferences.dataSaverImageFormatJpeg().get()) "1" else "0"}"
val quality = "&l=${preferences.dataSaverImageQuality().get()}"
val colorBW = "&bw=${if (preferences.dataSaverColorBW().get()) "1" else "0"}"
val url = "$server$format$quality$colorBW&url="
val quality = "l=${preferences.dataSaverImageQuality().get()}"
val colorBW = "bw=${if (preferences.dataSaverColorBW().get()) "1" else "0"}"
val url = "url=$imageUrl"
return url + imageUrl
return "$server&$format&$quality&$colorBW&$url"
}
}
@@ -1,5 +1,6 @@
package exh.util
import com.pushtorefresh.storio.operations.PreparedOperation
import com.pushtorefresh.storio.sqlite.operations.get.PreparedGetListOfObjects
import com.pushtorefresh.storio.sqlite.operations.get.PreparedGetObject
import com.pushtorefresh.storio.sqlite.operations.put.PreparedPutCollectionOfObjects
@@ -16,3 +17,5 @@ suspend fun <T> PreparedGetObject<T>.executeOnIO(): T? = withContext(Dispatchers
suspend fun <T> PreparedPutObject<T>.executeOnIO(): PutResult = withContext(Dispatchers.IO) { executeAsBlocking() }
suspend fun <T> PreparedPutCollectionOfObjects<T>.executeOnIO(): PutResults<T> = withContext(Dispatchers.IO) { executeAsBlocking() }
suspend fun <T> PreparedOperation<T>.executeOnIO(): T? = withContext(Dispatchers.IO) { executeAsBlocking() }
-176
View File
@@ -1,176 +0,0 @@
package exh.util
// Zero-allocation-overhead mutable collection shims
private inline class CollectionShim<E>(private val coll: Collection<E>) : FakeMutableCollection<E> {
override val size: Int get() = coll.size
override fun contains(element: E) = coll.contains(element)
override fun containsAll(elements: Collection<E>) = coll.containsAll(elements)
override fun isEmpty() = coll.isEmpty()
override fun fakeIterator() = coll.iterator()
}
interface FakeMutableCollection<E> : MutableCollection<E>, FakeMutableIterable<E> {
override fun add(element: E): Boolean {
throw UnsupportedOperationException("This collection is immutable!")
}
override fun addAll(elements: Collection<E>): Boolean {
throw UnsupportedOperationException("This collection is immutable!")
}
override fun clear() {
throw UnsupportedOperationException("This collection is immutable!")
}
override fun remove(element: E): Boolean {
throw UnsupportedOperationException("This collection is immutable!")
}
override fun removeAll(elements: Collection<E>): Boolean {
throw UnsupportedOperationException("This collection is immutable!")
}
override fun retainAll(elements: Collection<E>): Boolean {
throw UnsupportedOperationException("This collection is immutable!")
}
override fun iterator(): MutableIterator<E> = super.iterator()
companion object {
fun <E> fromCollection(coll: Collection<E>): FakeMutableCollection<E> = CollectionShim(coll)
}
}
private inline class SetShim<E>(private val set: Set<E>) : FakeMutableSet<E> {
override val size: Int get() = set.size
override fun contains(element: E) = set.contains(element)
override fun containsAll(elements: Collection<E>) = set.containsAll(elements)
override fun isEmpty() = set.isEmpty()
override fun fakeIterator() = set.iterator()
}
interface FakeMutableSet<E> : MutableSet<E>, FakeMutableCollection<E> {
/**
* Adds the specified element to the set.
*
* @return `true` if the element has been added, `false` if the element is already contained in the set.
*/
override fun add(element: E): Boolean = super.add(element)
override fun addAll(elements: Collection<E>): Boolean = super.addAll(elements)
override fun clear() = super.clear()
override fun remove(element: E): Boolean = super.remove(element)
override fun removeAll(elements: Collection<E>): Boolean = super.removeAll(elements)
override fun retainAll(elements: Collection<E>): Boolean = super.retainAll(elements)
override fun iterator(): MutableIterator<E> = super.iterator()
companion object {
fun <E> fromSet(set: Set<E>): FakeMutableSet<E> = SetShim(set)
}
}
private inline class IterableShim<E>(private val iterable: Iterable<E>) : FakeMutableIterable<E> {
override fun fakeIterator() = iterable.iterator()
}
interface FakeMutableIterable<E> : MutableIterable<E> {
/**
* Returns an iterator over the elements of this sequence that supports removing elements during iteration.
*/
override fun iterator(): MutableIterator<E> = FakeMutableIterator.fromIterator(fakeIterator())
fun fakeIterator(): Iterator<E>
companion object {
fun <E> fromIterable(iterable: Iterable<E>): FakeMutableIterable<E> = IterableShim(iterable)
}
}
private inline class IteratorShim<E>(private val iterator: Iterator<E>) : FakeMutableIterator<E> {
/**
* Returns `true` if the iteration has more elements.
*/
override fun hasNext() = iterator.hasNext()
/**
* Returns the next element in the iteration.
*/
override fun next() = iterator.next()
}
interface FakeMutableIterator<E> : MutableIterator<E> {
/**
* Removes from the underlying collection the last element returned by this iterator.
*/
override fun remove() {
throw UnsupportedOperationException("This set is immutable!")
}
companion object {
fun <E> fromIterator(iterator: Iterator<E>): FakeMutableIterator<E> = IteratorShim(iterator)
}
}
private inline class EntryShim<K, V>(private val entry: Map.Entry<K, V>) : FakeMutableEntry<K, V> {
/**
* Returns the key of this key/value pair.
*/
override val key: K
get() = entry.key
/**
* Returns the value of this key/value pair.
*/
override val value: V
get() = entry.value
}
private inline class PairShim<K, V>(private val pair: Pair<K, V>) : FakeMutableEntry<K, V> {
/**
* Returns the key of this key/value pair.
*/
override val key: K get() = pair.first
/**
* Returns the value of this key/value pair.
*/
override val value: V get() = pair.second
}
interface FakeMutableEntry<K, V> : MutableMap.MutableEntry<K, V> {
override fun setValue(newValue: V): V {
throw UnsupportedOperationException("This entry is immutable!")
}
companion object {
fun <K, V> fromEntry(entry: Map.Entry<K, V>): FakeMutableEntry<K, V> = EntryShim(entry)
fun <K, V> fromPair(pair: Pair<K, V>): FakeMutableEntry<K, V> = PairShim(pair)
fun <K, V> fromPair(key: K, value: V) = object : FakeMutableEntry<K, V> {
/**
* Returns the key of this key/value pair.
*/
override val key: K = key
/**
* Returns the value of this key/value pair.
*/
override val value: V = value
}
}
}
+21 -16
View File
@@ -24,21 +24,27 @@ fun Manga.mangaType(context: Context): String {
/**
* The type of comic the manga is (ie. manga, manhwa, manhua)
*/
fun Manga.mangaType(): MangaType {
val sourceName = Injekt.get<SourceManager>().getOrStub(source).name
fun Manga.mangaType(sourceName: String = Injekt.get<SourceManager>().getOrStub(source).name): MangaType {
val currentTags = getGenres().orEmpty()
return if (currentTags.any { tag -> isMangaTag(tag) }) {
MangaType.TYPE_MANGA
} else if (currentTags.any { tag -> isWebtoonTag(tag) } || isWebtoonSource(sourceName)) {
MangaType.TYPE_WEBTOON
} else if (currentTags.any { tag -> isComicTag(tag) } || isComicSource(sourceName)) {
MangaType.TYPE_COMIC
} else if (currentTags.any { tag -> isManhuaTag(tag) } || isManhuaSource(sourceName)) {
MangaType.TYPE_MANHUA
} else if (currentTags.any { tag -> isManhwaTag(tag) } || isManhwaSource(sourceName)) {
MangaType.TYPE_MANHWA
} else {
MangaType.TYPE_MANGA
return when {
currentTags.any { tag -> isMangaTag(tag) } -> {
MangaType.TYPE_MANGA
}
currentTags.any { tag -> isWebtoonTag(tag) } || isWebtoonSource(sourceName) -> {
MangaType.TYPE_WEBTOON
}
currentTags.any { tag -> isComicTag(tag) } || isComicSource(sourceName) -> {
MangaType.TYPE_COMIC
}
currentTags.any { tag -> isManhuaTag(tag) } || isManhuaSource(sourceName) -> {
MangaType.TYPE_MANHUA
}
currentTags.any { tag -> isManhwaTag(tag) } || isManhwaSource(sourceName) -> {
MangaType.TYPE_MANHWA
}
else -> {
MangaType.TYPE_MANGA
}
}
}
@@ -46,8 +52,7 @@ fun Manga.mangaType(): MangaType {
* The type the reader should use. Different from manga type as certain manga has different
* read types
*/
fun Manga.defaultReaderType(): Int? {
val type = mangaType()
fun Manga.defaultReaderType(type: MangaType = mangaType()): Int? {
return if (type == MangaType.TYPE_MANHWA || type == MangaType.TYPE_WEBTOON) {
ReaderActivity.WEBTOON
} else null
+6 -30
View File
@@ -1,32 +1,8 @@
package exh.util
import com.pushtorefresh.storio.operations.PreparedOperation
import com.pushtorefresh.storio.sqlite.operations.get.PreparedGetObject
import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import rx.Completable
import rx.CompletableSubscriber
import rx.Emitter
import rx.Observable
import rx.Observer
import rx.Scheduler
import rx.Single
import rx.SingleSubscriber
import rx.Subscriber
import rx.Subscription
import rx.subjects.ReplaySubject
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
/**
* Transform a cold single to a hot single
@@ -49,7 +25,7 @@ fun <T> Observable<T>.melt(): Observable<T> {
subscribe(rs)
return rs
}
/*
suspend fun <T> Single<T>.await(subscribeOn: Scheduler? = null): T {
return suspendCancellableCoroutine { continuation ->
val self = if (subscribeOn != null) subscribeOn(subscribeOn) else this
@@ -181,11 +157,11 @@ private suspend fun <T> Observable<T>.awaitOne(): T = suspendCancellableCoroutin
}
override fun onError(e: Throwable) {
/*
*//*
* Rx1 observable throws NoSuchElementException if cancellation happened before
* element emission. To mitigate this we try to atomically resume continuation with exception:
* if resume failed, then we know that continuation successfully cancelled itself
*/
*//*
val token = cont.tryResumeWithException(e)
if (token != null) {
cont.completeResume(token)
@@ -220,10 +196,10 @@ fun <T : Any> Observable<T>.asFlow(): Flow<T> = callbackFlow {
fun <T : Any> Flow<T>.asObservable(backpressureMode: Emitter.BackpressureMode = Emitter.BackpressureMode.NONE): Observable<T> {
return Observable.create(
{ emitter ->
/*
*//*
* ATOMIC is used here to provide stable behaviour of subscribe+dispose pair even if
* asObservable is already invoked from unconfined
*/
*//*
val job = GlobalScope.launch(Dispatchers.Unconfined, start = CoroutineStart.ATOMIC) {
try {
collect { emitter.onNext(it) }
@@ -241,4 +217,4 @@ fun <T : Any> Flow<T>.asObservable(backpressureMode: Emitter.BackpressureMode =
},
backpressureMode
)
}
}*/
+5 -7
View File
@@ -23,13 +23,11 @@ fun UrlImportableSource.urlImportFetchSearchManga(context: Context, query: Strin
})
.map { res ->
MangasPage(
(
if (res is GalleryAddEvent.Success) {
listOf(res.manga)
} else {
emptyList()
}
),
if (res is GalleryAddEvent.Success) {
listOf(res.manga)
} else {
emptyList()
},
false
)
}