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

View File

@ -36,7 +36,7 @@ import eu.kanade.tachiyomi.source.online.MetadataSource
import eu.kanade.tachiyomi.source.online.all.MergedSource
import exh.MERGED_SOURCE_ID
import exh.metadata.metadata.base.getFlatMetadataForManga
import exh.metadata.metadata.base.insertFlatMetadata
import exh.metadata.metadata.base.insertFlatMetadataAsync
import exh.savedsearches.JsonSavedSearch
import exh.source.getMainSource
import kotlinx.serialization.ExperimentalSerializationApi
@ -527,12 +527,12 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) {
}
}
internal fun restoreFlatMetadata(manga: Manga, backupFlatMetadata: BackupFlatMetadata) {
internal suspend fun restoreFlatMetadata(manga: Manga, backupFlatMetadata: BackupFlatMetadata) {
manga.id?.let { mangaId ->
databaseHelper.getFlatMetadataForManga(mangaId).executeAsBlocking().let {
if (it == null) {
val flatMetadata = backupFlatMetadata.getFlatMetadata(mangaId)
databaseHelper.insertFlatMetadata(flatMetadata).await()
databaseHelper.insertFlatMetadataAsync(flatMetadata).await()
}
}
}

View File

@ -223,7 +223,7 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val
}
}
private fun restoreExtraForManga(manga: Manga, categories: List<Int>, history: List<BackupHistory>, tracks: List<Track>, backupCategories: List<BackupCategory>, mergedMangaReferences: List<BackupMergedMangaReference>, flatMetadata: BackupFlatMetadata?) {
private suspend fun restoreExtraForManga(manga: Manga, categories: List<Int>, history: List<BackupHistory>, tracks: List<Track>, backupCategories: List<BackupCategory>, mergedMangaReferences: List<BackupMergedMangaReference>, flatMetadata: BackupFlatMetadata?) {
// Restore categories
backupManager.restoreCategoriesForManga(manga, categories, backupCategories)

View File

@ -32,7 +32,6 @@ import eu.kanade.tachiyomi.ui.library.LibraryGroup
import eu.kanade.tachiyomi.ui.manga.track.TrackItem
import eu.kanade.tachiyomi.util.chapter.NoChaptersException
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import eu.kanade.tachiyomi.util.lang.await
import eu.kanade.tachiyomi.util.lang.runAsObservable
import eu.kanade.tachiyomi.util.prepUpdateCover
import eu.kanade.tachiyomi.util.shouldDownloadNewChapters

View File

@ -10,12 +10,11 @@ import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.lang.await
import exh.md.utils.FollowStatus
import exh.md.utils.MdUtil
import exh.metadata.metadata.MangaDexSearchMetadata
import exh.metadata.metadata.base.getFlatMetadataForManga
import exh.metadata.metadata.base.insertFlatMetadata
import exh.metadata.metadata.base.insertFlatMetadataAsync
import exh.util.executeOnIO
import exh.util.floor
import uy.kohesive.injekt.injectLazy
@ -59,7 +58,7 @@ class MdList(private val context: Context, id: Int) : TrackService(id) {
if (mangaMetadata.follow_status != followStatus.int) {
mdex.updateFollowStatus(MdUtil.getMangaId(track.tracking_url), followStatus)
mangaMetadata.follow_status = followStatus.int
db.insertFlatMetadata(mangaMetadata.flatten()).await()
db.insertFlatMetadataAsync(mangaMetadata.flatten()).await()
}
if (track.score.toInt() > 0) {
@ -78,7 +77,7 @@ class MdList(private val context: Context, id: Int) : TrackService(id) {
track.status = FollowStatus.READING.int
mdex.updateFollowStatus(MdUtil.getMangaId(track.tracking_url), newFollowStatus)
mangaMetadata.follow_status = newFollowStatus.int
db.insertFlatMetadata(mangaMetadata.flatten()).await()
db.insertFlatMetadataAsync(mangaMetadata.flatten()).await()
}
mdex.updateReadingProgress(track)

View File

@ -18,8 +18,7 @@ open class Page(
// SY -->
var imageUrl = imageUrl
get() {
if (field == null) return null
return DataSaver().compress(field!!)
return field?.let { DataSaver.compress(it) }
}
// SY <--

View File

@ -11,7 +11,8 @@ import eu.kanade.tachiyomi.ui.manga.MangaController
import exh.metadata.metadata.base.RaisedSearchMetadata
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 rx.Completable
import rx.Single
import tachiyomi.source.model.MangaInfo
@ -79,14 +80,14 @@ interface MetadataSource<M : RaisedSearchMetadata, I> : CatalogueSource {
suspend fun parseToManga(manga: MangaInfo, input: I): MangaInfo {
val mangaId = manga.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)
if (mangaId != null) {
metadata.mangaId = mangaId
db.insertFlatMetadata(metadata.flatten()).await()
db.insertFlatMetadataAsync(metadata.flatten()).await()
}
return metadata.createMangaInfo(manga)
@ -134,7 +135,7 @@ interface MetadataSource<M : RaisedSearchMetadata, I> : CatalogueSource {
*/
suspend fun fetchOrLoadMetadata(mangaId: Long?, inputProducer: suspend () -> I): M {
val meta = if (mangaId != null) {
val flatMetadata = db.getFlatMetadataForManga(mangaId).await()
val flatMetadata = db.getFlatMetadataForManga(mangaId).executeOnIO()
flatMetadata?.raise(metaClass)
} else {
null
@ -145,14 +146,14 @@ interface MetadataSource<M : RaisedSearchMetadata, I> : CatalogueSource {
parseInfoIntoMetadata(newMeta, input)
if (mangaId != null) {
newMeta.mangaId = mangaId
db.insertFlatMetadata(newMeta.flatten()).let { newMeta }
db.insertFlatMetadataAsync(newMeta.flatten()).await().let { newMeta }
} else newMeta
}
}
fun getDescriptionAdapter(controller: MangaController): RecyclerView.Adapter<*>?
suspend fun MangaInfo.id() = db.getManga(key, id).await()?.id
suspend fun MangaInfo.id() = db.getManga(key, id).executeOnIO()?.id
val SManga.id get() = (this as? Manga)?.id
val SChapter.mangaId get() = (this as? Chapter)?.manga_id
}

View File

@ -35,7 +35,7 @@ import eu.kanade.tachiyomi.ui.browse.migration.advanced.design.PreMigrationContr
import eu.kanade.tachiyomi.ui.browse.migration.search.SearchController
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import eu.kanade.tachiyomi.util.lang.await
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.lang.withIOContext
import eu.kanade.tachiyomi.util.system.getResourceColor
@ -49,7 +49,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit
import timber.log.Timber
@ -115,7 +114,7 @@ class MigrationListController(bundle: Bundle? = null) :
adapter?.updateDataSet(newMigratingManga.map { it.toModal() })
if (migrationsJob == null) {
migrationsJob = viewScope.launch(Dispatchers.IO) {
migrationsJob = viewScope.launchIO {
runMigrations(newMigratingManga)
}
}
@ -128,7 +127,7 @@ class MigrationListController(bundle: Bundle? = null) :
val useSmartSearch = preferences.smartMigration().get()
val sources = preferences.migrationSources().get().split("/").mapNotNull {
val value = it.toLongOrNull() ?: return
val value = it.toLongOrNull() ?: return@mapNotNull null
sourceManager.get(value) as? CatalogueSource
}
for (manga in mangas) {

View File

@ -17,11 +17,10 @@ import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.browse.BrowseController
import eu.kanade.tachiyomi.ui.browse.migration.advanced.design.PreMigrationController
import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrationMangaController
import eu.kanade.tachiyomi.util.lang.await
import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.lang.withUIContext
import eu.kanade.tachiyomi.util.system.openInBrowser
import rx.schedulers.Schedulers
import exh.util.executeOnIO
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@ -84,8 +83,9 @@ class MigrationSourcesController :
override fun onItemClick(view: View?, position: Int): Boolean {
val item = adapter?.getItem(position) as? SourceItem ?: return false
val controller = MigrationMangaController(item.source.id, item.source.name)
val parentController = parentController
if (parentController is BrowseController) {
parentController!!.router.pushController(controller.withFadeTransaction())
parentController.router.pushController(controller.withFadeTransaction())
} else {
router.pushController(controller.withFadeTransaction())
}
@ -97,15 +97,18 @@ class MigrationSourcesController :
val item = adapter?.getItem(position) as? SourceItem ?: return
launchUI {
val manga = Injekt.get<DatabaseHelper>().getFavoriteMangas().asRxSingle().await(Schedulers.io())
val sourceMangas = manga.asSequence().filter { it.source == item.source.id }.map { it.id!! }.toList()
val manga = Injekt.get<DatabaseHelper>().getFavoriteMangas().executeOnIO()
val sourceMangas = manga.asSequence().filter { it.source == item.source.id }.mapNotNull { it.id }.toList()
withUIContext {
PreMigrationController.navigateToMigration(
Injekt.get<PreferencesHelper>().skipPreMigration().get(),
if (parentController is BrowseController) {
parentController!!.router
} else {
router
run {
val parentController = parentController
if (parentController is BrowseController) {
parentController.router
} else {
router
}
},
sourceMangas
)

View File

@ -208,7 +208,7 @@ class EditMangaDialog : DialogController {
addView(addTagChip)
}
private fun ChipGroup.getTextStrings(): List<String>? = children.mapNotNull {
private fun ChipGroup.getTextStrings(): List<String> = children.mapNotNull {
if (it is Chip && !it.text.toString().contains(context.getString(R.string.add_tag), ignoreCase = true)) {
it.text.toString()
} else null

View File

@ -52,7 +52,7 @@ import exh.merged.sql.models.MergedMangaReference
import exh.metadata.metadata.base.FlatMetadata
import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.metadata.metadata.base.getFlatMetadataForManga
import exh.metadata.metadata.base.insertFlatMetadata
import exh.metadata.metadata.base.insertFlatMetadataAsync
import exh.source.getMainSource
import exh.util.shouldDeleteChapters
import exh.util.trimOrNull
@ -1002,11 +1002,11 @@ class MangaPresenter(
}
// SY -->
fun setScanlatorFilter(filteredScanlators: Set<String>) {
suspend fun setScanlatorFilter(filteredScanlators: Set<String>) {
val meta = meta ?: return
meta.filteredScanlators = if (filteredScanlators.size == allChapterScanlators.size) null else MdUtil.getScanlatorString(filteredScanlators)
meta.flatten().let {
db.insertFlatMetadata(it).await()
db.insertFlatMetadataAsync(it).await()
}
refreshChapters()
}

View File

@ -11,6 +11,8 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.source.online.MetadataSource
import eu.kanade.tachiyomi.ui.manga.MangaPresenter
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.withUIContext
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.view.popupMenu
import eu.kanade.tachiyomi.widget.ExtendedNavigationView
@ -19,6 +21,7 @@ import eu.kanade.tachiyomi.widget.TabbedBottomSheetDialog
import exh.md.utils.MdUtil
import exh.metadata.metadata.MangaDexSearchMetadata
import exh.source.getMainSource
import kotlinx.coroutines.supervisorScope
class ChaptersSettingsSheet(
private val router: Router,
@ -132,13 +135,21 @@ class ChaptersSettingsSheet(
MaterialDialog(context)
.title(R.string.select_scanlators)
.listItemsMultiChoice(items = presenter.allChapterScanlators.toList(), initialSelection = preselected) { _, selections, _ ->
val selected = selections.map { scanlators[it] }.toSet()
presenter.setScanlatorFilter(selected)
onGroupClicked(this)
launchIO {
supervisorScope {
val selected = selections.map { scanlators[it] }.toSet()
presenter.setScanlatorFilter(selected)
withUIContext { onGroupClicked(this@FilterGroup) }
}
}
}
.negativeButton(R.string.action_reset) {
presenter.setScanlatorFilter(presenter.allChapterScanlators)
onGroupClicked(this)
launchIO {
supervisorScope {
presenter.setScanlatorFilter(presenter.allChapterScanlators)
withUIContext { onGroupClicked(this@FilterGroup) }
}
}
}
.positiveButton(android.R.string.ok)
.show()

View File

@ -71,6 +71,7 @@ import eu.kanade.tachiyomi.widget.SimpleAnimationListener
import eu.kanade.tachiyomi.widget.SimpleSeekBarListener
import exh.isEhBasedSource
import exh.util.defaultReaderType
import exh.util.mangaType
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
@ -729,7 +730,7 @@ class ReaderActivity : BaseRxActivity<ReaderActivityBinding, ReaderPresenter>()
binding.viewerContainer.addView(newViewer.getView())
// SY -->
val defaultReaderType = manga.defaultReaderType()
val defaultReaderType = manga.defaultReaderType(manga.mangaType(sourceName = sourceManager.getOrStub(manga.source).name))
if (preferences.useAutoWebtoon().get() && manga.viewer == 0 && defaultReaderType != null && defaultReaderType == WEBTOON) {
binding.root.snack(resources.getString(R.string.eh_auto_webtoon_snack), Snackbar.LENGTH_LONG)
} else if (preferences.showReadingMode()) {

View File

@ -44,6 +44,7 @@ import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.metadata.metadata.base.getFlatMetadataForManga
import exh.source.getMainSource
import exh.util.defaultReaderType
import exh.util.mangaType
import exh.util.shouldDeleteChapters
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
@ -598,7 +599,7 @@ class ReaderPresenter(
val manga = manga ?: return preferences.defaultViewer()
// SY -->
return if (manga.viewer == 0 && preferences.useAutoWebtoon().get()) {
manga.defaultReaderType() ?: if (manga.viewer == 0) preferences.defaultViewer() else manga.viewer
manga.defaultReaderType(manga.mangaType(sourceName = sourceManager.getOrStub(manga.source).name)) ?: if (manga.viewer == 0) preferences.defaultViewer() else manga.viewer
} else if (manga.viewer == 0) {
preferences.defaultViewer()
} else {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

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

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

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
)
}
}*/

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