Convert java threads to kotlin coroutines
This commit is contained in:
parent
46998d81f4
commit
3b5249c8bc
@ -43,6 +43,7 @@ import exh.md.utils.MdUtil
|
|||||||
import exh.metadata.metadata.MangaDexSearchMetadata
|
import exh.metadata.metadata.MangaDexSearchMetadata
|
||||||
import exh.source.DelegatedHttpSource
|
import exh.source.DelegatedHttpSource
|
||||||
import exh.ui.metadata.adapters.MangaDexDescriptionAdapter
|
import exh.ui.metadata.adapters.MangaDexDescriptionAdapter
|
||||||
|
import exh.util.asObservable
|
||||||
import exh.util.urlImportFetchSearchManga
|
import exh.util.urlImportFetchSearchManga
|
||||||
import exh.widget.preference.MangadexLoginDialog
|
import exh.widget.preference.MangadexLoginDialog
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@ -266,20 +267,22 @@ class MangaDex(delegate: HttpSource, val context: Context) :
|
|||||||
private fun importIdToMdId(query: String, fail: () -> Observable<MangasPage>): Observable<MangasPage> =
|
private fun importIdToMdId(query: String, fail: () -> Observable<MangasPage>): Observable<MangasPage> =
|
||||||
when {
|
when {
|
||||||
query.toIntOrNull() != null -> {
|
query.toIntOrNull() != null -> {
|
||||||
Observable.fromCallable {
|
flow {
|
||||||
// MdUtil.
|
emit(GalleryAdder().addGallery(context, MdUtil.baseUrl + MdUtil.mapMdIdToMangaUrl(query.toInt()), false, this@MangaDex))
|
||||||
val res = GalleryAdder().addGallery(context, MdUtil.baseUrl + MdUtil.mapMdIdToMangaUrl(query.toInt()), false, this)
|
|
||||||
MangasPage(
|
|
||||||
(
|
|
||||||
if (res is GalleryAddEvent.Success) {
|
|
||||||
listOf(res.manga)
|
|
||||||
} else {
|
|
||||||
emptyList()
|
|
||||||
}
|
|
||||||
),
|
|
||||||
false
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
.asObservable()
|
||||||
|
.map { res ->
|
||||||
|
MangasPage(
|
||||||
|
(
|
||||||
|
if (res is GalleryAddEvent.Success) {
|
||||||
|
listOf(res.manga)
|
||||||
|
} else {
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else -> fail()
|
else -> fail()
|
||||||
}
|
}
|
||||||
|
@ -91,9 +91,9 @@ class ChapterLoader(
|
|||||||
return when {
|
return when {
|
||||||
// SY -->
|
// SY -->
|
||||||
source is MergedSource -> {
|
source is MergedSource -> {
|
||||||
val mangaReference = mergedReferences.firstOrNull { it.mangaId == chapter.chapter.manga_id } ?: throw Exception("Merge reference null")
|
val mangaReference = mergedReferences.firstOrNull { it.mangaId == chapter.chapter.manga_id } ?: error("Merge reference null")
|
||||||
val source = sourceManager.get(mangaReference.mangaSourceId) ?: throw Exception("Source ${mangaReference.mangaSourceId} was null")
|
val source = sourceManager.get(mangaReference.mangaSourceId) ?: error("Source ${mangaReference.mangaSourceId} was null")
|
||||||
val manga = mergedManga.firstOrNull { it.id == chapter.chapter.manga_id } ?: throw Exception("Manga for merged chapter was null")
|
val manga = mergedManga.firstOrNull { it.id == chapter.chapter.manga_id } ?: error("Manga for merged chapter was null")
|
||||||
val isMergedMangaDownloaded = downloadManager.isChapterDownloaded(chapter.chapter, manga, true)
|
val isMergedMangaDownloaded = downloadManager.isChapterDownloaded(chapter.chapter, manga, true)
|
||||||
when {
|
when {
|
||||||
isMergedMangaDownloaded -> DownloadPageLoader(chapter, manga, source, downloadManager)
|
isMergedMangaDownloaded -> DownloadPageLoader(chapter, manga, source, downloadManager)
|
||||||
|
@ -11,6 +11,8 @@ import eu.kanade.tachiyomi.source.online.UrlImportableSource
|
|||||||
import eu.kanade.tachiyomi.source.online.all.EHentai
|
import eu.kanade.tachiyomi.source.online.all.EHentai
|
||||||
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
||||||
import exh.source.getMainSource
|
import exh.source.getMainSource
|
||||||
|
import exh.util.await
|
||||||
|
import exh.util.awaitSingle
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
@ -34,7 +36,7 @@ class GalleryAdder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addGallery(
|
suspend fun addGallery(
|
||||||
context: Context,
|
context: Context,
|
||||||
url: String,
|
url: String,
|
||||||
fav: Boolean = false,
|
fav: Boolean = false,
|
||||||
@ -84,7 +86,7 @@ class GalleryAdder {
|
|||||||
} ?: return GalleryAddEvent.Fail.UnknownType(url, context)
|
} ?: return GalleryAddEvent.Fail.UnknownType(url, context)
|
||||||
|
|
||||||
// Use manga in DB if possible, otherwise, make a new manga
|
// Use manga in DB if possible, otherwise, make a new manga
|
||||||
val manga = db.getManga(cleanedUrl, source.id).executeAsBlocking()
|
val manga = db.getManga(cleanedUrl, source.id).await()
|
||||||
?: Manga.create(source.id).apply {
|
?: Manga.create(source.id).apply {
|
||||||
this.url = cleanedUrl
|
this.url = cleanedUrl
|
||||||
title = realUrl
|
title = realUrl
|
||||||
@ -93,13 +95,13 @@ class GalleryAdder {
|
|||||||
// Insert created manga if not in DB before fetching details
|
// Insert created manga if not in DB before fetching details
|
||||||
// This allows us to keep the metadata when fetching details
|
// This allows us to keep the metadata when fetching details
|
||||||
if (manga.id == null) {
|
if (manga.id == null) {
|
||||||
db.insertManga(manga).executeAsBlocking().insertedId()?.let {
|
db.insertManga(manga).await().insertedId()?.let {
|
||||||
manga.id = it
|
manga.id = it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch and copy details
|
// Fetch and copy details
|
||||||
val newManga = source.fetchMangaDetails(manga).toBlocking().first()
|
val newManga = source.fetchMangaDetails(manga).awaitSingle()
|
||||||
manga.copyFrom(newManga)
|
manga.copyFrom(newManga)
|
||||||
manga.initialized = true
|
manga.initialized = true
|
||||||
|
|
||||||
@ -108,7 +110,7 @@ class GalleryAdder {
|
|||||||
manga.date_added = Date().time
|
manga.date_added = Date().time
|
||||||
}
|
}
|
||||||
|
|
||||||
db.insertManga(manga).executeAsBlocking()
|
db.insertManga(manga).await()
|
||||||
|
|
||||||
// Fetch and copy chapters
|
// Fetch and copy chapters
|
||||||
try {
|
try {
|
||||||
@ -119,7 +121,7 @@ class GalleryAdder {
|
|||||||
}
|
}
|
||||||
chapterListObs.map {
|
chapterListObs.map {
|
||||||
syncChaptersWithSource(db, it, manga, source)
|
syncChaptersWithSource(db, it, manga, source)
|
||||||
}.toBlocking().first()
|
}.awaitSingle()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
XLog.w(context.getString(R.string.gallery_adder_chapter_fetch_error, manga.title), e)
|
XLog.w(context.getString(R.string.gallery_adder_chapter_fetch_error, manga.title), e)
|
||||||
return GalleryAddEvent.Fail.Error(url, context.getString(R.string.gallery_adder_chapter_fetch_error, url))
|
return GalleryAddEvent.Fail.Error(url, context.getString(R.string.gallery_adder_chapter_fetch_error, url))
|
||||||
|
@ -10,6 +10,7 @@ import eu.kanade.tachiyomi.data.database.models.Category
|
|||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
import eu.kanade.tachiyomi.data.database.models.MangaCategory
|
import eu.kanade.tachiyomi.data.database.models.MangaCategory
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
|
import eu.kanade.tachiyomi.network.await
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
import eu.kanade.tachiyomi.source.online.all.EHentai
|
import eu.kanade.tachiyomi.source.online.all.EHentai
|
||||||
import eu.kanade.tachiyomi.util.lang.launchUI
|
import eu.kanade.tachiyomi.util.lang.launchUI
|
||||||
@ -21,22 +22,28 @@ import exh.GalleryAddEvent
|
|||||||
import exh.GalleryAdder
|
import exh.GalleryAdder
|
||||||
import exh.eh.EHentaiThrottleManager
|
import exh.eh.EHentaiThrottleManager
|
||||||
import exh.eh.EHentaiUpdateWorker
|
import exh.eh.EHentaiUpdateWorker
|
||||||
|
import exh.util.await
|
||||||
import exh.util.ignore
|
import exh.util.ignore
|
||||||
import exh.util.trans
|
import exh.util.trans
|
||||||
import exh.util.wifiManager
|
import exh.util.wifiManager
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import okhttp3.FormBody
|
import okhttp3.FormBody
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import rx.subjects.BehaviorSubject
|
import rx.subjects.BehaviorSubject
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import kotlin.concurrent.thread
|
|
||||||
|
|
||||||
class FavoritesSyncHelper(val context: Context) {
|
class FavoritesSyncHelper(val context: Context) {
|
||||||
private val db: DatabaseHelper by injectLazy()
|
private val db: DatabaseHelper by injectLazy()
|
||||||
|
|
||||||
private val prefs: PreferencesHelper by injectLazy()
|
private val prefs: PreferencesHelper by injectLazy()
|
||||||
|
|
||||||
|
private val scope = CoroutineScope(Job() + Dispatchers.Main)
|
||||||
|
|
||||||
private val exh by lazy {
|
private val exh by lazy {
|
||||||
Injekt.get<SourceManager>().get(EXH_SOURCE_ID) as? EHentai
|
Injekt.get<SourceManager>().get(EXH_SOURCE_ID) as? EHentai
|
||||||
?: EHentai(0, true, context)
|
?: EHentai(0, true, context)
|
||||||
@ -53,7 +60,7 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
|
|
||||||
private val logger = XLog.tag("EHFavSync").build()
|
private val logger = XLog.tag("EHFavSync").build()
|
||||||
|
|
||||||
val status: BehaviorSubject<FavoritesSyncStatus> = BehaviorSubject.create<FavoritesSyncStatus>(FavoritesSyncStatus.Idle(context))
|
val status: BehaviorSubject<FavoritesSyncStatus> = BehaviorSubject.create(FavoritesSyncStatus.Idle(context))
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun runSync() {
|
fun runSync() {
|
||||||
@ -63,10 +70,10 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
|
|
||||||
status.onNext(FavoritesSyncStatus.Initializing(context))
|
status.onNext(FavoritesSyncStatus.Initializing(context))
|
||||||
|
|
||||||
thread { beginSync() }
|
scope.launch(Dispatchers.IO) { beginSync() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun beginSync() {
|
private suspend fun beginSync() {
|
||||||
// Check if logged in
|
// Check if logged in
|
||||||
if (!prefs.enableExhentai().get()) {
|
if (!prefs.enableExhentai().get()) {
|
||||||
status.onNext(FavoritesSyncStatus.Error(context.getString(R.string.please_login)))
|
status.onNext(FavoritesSyncStatus.Error(context.getString(R.string.please_login)))
|
||||||
@ -75,13 +82,13 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
|
|
||||||
// Validate library state
|
// Validate library state
|
||||||
status.onNext(FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_verifying_library), context = context))
|
status.onNext(FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_verifying_library), context = context))
|
||||||
val libraryManga = db.getLibraryMangas().executeAsBlocking()
|
val libraryManga = db.getLibraryMangas().await()
|
||||||
val seenManga = HashSet<Long>(libraryManga.size)
|
val seenManga = HashSet<Long>(libraryManga.size)
|
||||||
libraryManga.forEach {
|
libraryManga.forEach {
|
||||||
if (it.source != EXH_SOURCE_ID && it.source != EH_SOURCE_ID) return@forEach
|
if (it.source != EXH_SOURCE_ID && it.source != EH_SOURCE_ID) return@forEach
|
||||||
|
|
||||||
if (it.id in seenManga) {
|
if (it.id in seenManga) {
|
||||||
val inCategories = db.getCategoriesForManga(it).executeAsBlocking()
|
val inCategories = db.getCategoriesForManga(it).await()
|
||||||
status.onNext(
|
status.onNext(
|
||||||
FavoritesSyncStatus.BadLibraryState
|
FavoritesSyncStatus.BadLibraryState
|
||||||
.MangaInMultipleCategories(it, inCategories, context)
|
.MangaInMultipleCategories(it, inCategories, context)
|
||||||
@ -153,9 +160,8 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val theContext = context
|
|
||||||
launchUI {
|
launchUI {
|
||||||
theContext.toast(context.getString(R.string.favorites_sync_complete))
|
context.toast(context.getString(R.string.favorites_sync_complete))
|
||||||
}
|
}
|
||||||
} catch (e: IgnoredException) {
|
} catch (e: IgnoredException) {
|
||||||
// Do not display error as this error has already been reported
|
// Do not display error as this error has already been reported
|
||||||
@ -187,8 +193,8 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun applyRemoteCategories(categories: List<String>) {
|
private suspend fun applyRemoteCategories(categories: List<String>) {
|
||||||
val localCategories = db.getCategories().executeAsBlocking()
|
val localCategories = db.getCategories().await()
|
||||||
|
|
||||||
val newLocalCategories = localCategories.toMutableList()
|
val newLocalCategories = localCategories.toMutableList()
|
||||||
|
|
||||||
@ -226,11 +232,11 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
|
|
||||||
// Only insert categories if changed
|
// Only insert categories if changed
|
||||||
if (changed) {
|
if (changed) {
|
||||||
db.insertCategories(newLocalCategories).executeAsBlocking()
|
db.insertCategories(newLocalCategories).await()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addGalleryRemote(errorList: MutableList<String>, gallery: FavoriteEntry) {
|
private suspend fun addGalleryRemote(errorList: MutableList<String>, gallery: FavoriteEntry) {
|
||||||
val url = "${exh.baseUrl}/gallerypopups.php?gid=${gallery.gid}&t=${gallery.token}&act=addfav"
|
val url = "${exh.baseUrl}/gallerypopups.php?gid=${gallery.gid}&t=${gallery.token}&act=addfav"
|
||||||
|
|
||||||
val request = Request.Builder()
|
val request = Request.Builder()
|
||||||
@ -257,12 +263,12 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun explicitlyRetryExhRequest(retryCount: Int, request: Request): Boolean {
|
private suspend fun explicitlyRetryExhRequest(retryCount: Int, request: Request): Boolean {
|
||||||
var success = false
|
var success = false
|
||||||
|
|
||||||
for (i in 1..retryCount) {
|
for (i in 1..retryCount) {
|
||||||
try {
|
try {
|
||||||
val resp = exh.client.newCall(request).execute()
|
val resp = exh.client.newCall(request).await()
|
||||||
|
|
||||||
if (resp.isSuccessful) {
|
if (resp.isSuccessful) {
|
||||||
success = true
|
success = true
|
||||||
@ -276,7 +282,7 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
return success
|
return success
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun applyChangeSetToRemote(errorList: MutableList<String>, changeSet: ChangeSet) {
|
private suspend fun applyChangeSetToRemote(errorList: MutableList<String>, changeSet: ChangeSet) {
|
||||||
// Apply removals
|
// Apply removals
|
||||||
if (changeSet.removed.isNotEmpty()) {
|
if (changeSet.removed.isNotEmpty()) {
|
||||||
status.onNext(FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_removing_galleries, changeSet.removed.size), context = context))
|
status.onNext(FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_removing_galleries, changeSet.removed.size), context = context))
|
||||||
@ -324,7 +330,7 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun applyChangeSetToLocal(errorList: MutableList<String>, changeSet: ChangeSet) {
|
private suspend fun applyChangeSetToLocal(errorList: MutableList<String>, changeSet: ChangeSet) {
|
||||||
val removedManga = mutableListOf<Manga>()
|
val removedManga = mutableListOf<Manga>()
|
||||||
|
|
||||||
// Apply removals
|
// Apply removals
|
||||||
@ -337,12 +343,12 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
db.getManga(url, EXH_SOURCE_ID),
|
db.getManga(url, EXH_SOURCE_ID),
|
||||||
db.getManga(url, EH_SOURCE_ID)
|
db.getManga(url, EH_SOURCE_ID)
|
||||||
).forEach {
|
).forEach {
|
||||||
val manga = it.executeAsBlocking()
|
val manga = it.await()
|
||||||
|
|
||||||
if (manga?.favorite == true) {
|
if (manga?.favorite == true) {
|
||||||
manga.favorite = false
|
manga.favorite = false
|
||||||
manga.date_added = 0
|
manga.date_added = 0
|
||||||
db.updateMangaFavorite(manga).executeAsBlocking()
|
db.updateMangaFavorite(manga).await()
|
||||||
removedManga += manga
|
removedManga += manga
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -350,11 +356,11 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
|
|
||||||
// Can't do too many DB OPs in one go
|
// Can't do too many DB OPs in one go
|
||||||
removedManga.chunked(10).forEach {
|
removedManga.chunked(10).forEach {
|
||||||
db.deleteOldMangasCategories(it).executeAsBlocking()
|
db.deleteOldMangasCategories(it).await()
|
||||||
}
|
}
|
||||||
|
|
||||||
val insertedMangaCategories = mutableListOf<Pair<MangaCategory, Manga>>()
|
val insertedMangaCategories = mutableListOf<Pair<MangaCategory, Manga>>()
|
||||||
val categories = db.getCategories().executeAsBlocking()
|
val categories = db.getCategories().await()
|
||||||
|
|
||||||
// Apply additions
|
// Apply additions
|
||||||
throttleManager.resetThrottle()
|
throttleManager.resetThrottle()
|
||||||
|
@ -9,22 +9,27 @@ import eu.kanade.tachiyomi.R
|
|||||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||||
import eu.kanade.tachiyomi.util.lang.launchUI
|
import eu.kanade.tachiyomi.util.lang.launchUI
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import kotlin.concurrent.thread
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
class ConfiguringDialogController : DialogController() {
|
class ConfiguringDialogController : DialogController() {
|
||||||
private var materialDialog: MaterialDialog? = null
|
private var materialDialog: MaterialDialog? = null
|
||||||
|
val scope = CoroutineScope(Job() + Dispatchers.Main)
|
||||||
|
|
||||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||||
if (savedViewState == null) {
|
if (savedViewState == null) {
|
||||||
thread {
|
scope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
EHConfigurator(activity!!).configureAll()
|
EHConfigurator(activity!!).configureAll()
|
||||||
launchUI {
|
launchUI {
|
||||||
activity?.toast(activity?.getString(R.string.eh_settings_successfully_uploaded))
|
activity?.toast(activity?.getString(R.string.eh_settings_successfully_uploaded))
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
activity?.let {
|
withContext(Dispatchers.Main) {
|
||||||
it.runOnUiThread {
|
activity?.let {
|
||||||
MaterialDialog(it)
|
MaterialDialog(it)
|
||||||
.title(R.string.eh_settings_configuration_failed)
|
.title(R.string.eh_settings_configuration_failed)
|
||||||
.message(text = it.getString(R.string.eh_settings_configuration_failed_message, e.message))
|
.message(text = it.getString(R.string.eh_settings_configuration_failed_message, e.message))
|
||||||
|
@ -4,6 +4,7 @@ import android.content.Context
|
|||||||
import com.elvishew.xlog.XLog
|
import com.elvishew.xlog.XLog
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
|
import eu.kanade.tachiyomi.network.await
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
import eu.kanade.tachiyomi.source.online.all.EHentai
|
import eu.kanade.tachiyomi.source.online.all.EHentai
|
||||||
import eu.kanade.tachiyomi.util.asJsoup
|
import eu.kanade.tachiyomi.util.asJsoup
|
||||||
@ -26,7 +27,7 @@ class EHConfigurator(val context: Context) {
|
|||||||
private fun EHentai.requestWithCreds(sp: Int = 1) = Request.Builder()
|
private fun EHentai.requestWithCreds(sp: Int = 1) = Request.Builder()
|
||||||
.addHeader("Cookie", cookiesHeader(sp))
|
.addHeader("Cookie", cookiesHeader(sp))
|
||||||
|
|
||||||
private fun EHentai.execProfileActions(
|
private suspend fun EHentai.execProfileActions(
|
||||||
action: String,
|
action: String,
|
||||||
name: String,
|
name: String,
|
||||||
set: String,
|
set: String,
|
||||||
@ -44,11 +45,11 @@ class EHConfigurator(val context: Context) {
|
|||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.execute()
|
.await()
|
||||||
|
|
||||||
private val EHentai.uconfigUrl get() = baseUrl + UCONFIG_URL
|
private val EHentai.uconfigUrl get() = baseUrl + UCONFIG_URL
|
||||||
|
|
||||||
fun configureAll() {
|
suspend fun configureAll() {
|
||||||
val ehSource = sources.get(EH_SOURCE_ID) as EHentai
|
val ehSource = sources.get(EH_SOURCE_ID) as EHentai
|
||||||
val exhSource = sources.get(EXH_SOURCE_ID) as EHentai
|
val exhSource = sources.get(EXH_SOURCE_ID) as EHentai
|
||||||
|
|
||||||
@ -58,7 +59,7 @@ class EHConfigurator(val context: Context) {
|
|||||||
.url(HATH_PERKS_URL)
|
.url(HATH_PERKS_URL)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.execute().asJsoup()
|
.await().asJsoup()
|
||||||
|
|
||||||
val hathPerks = EHHathPerksResponse()
|
val hathPerks = EHHathPerksResponse()
|
||||||
|
|
||||||
@ -85,10 +86,10 @@ class EHConfigurator(val context: Context) {
|
|||||||
configure(exhSource, hathPerks)
|
configure(exhSource, hathPerks)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun configure(source: EHentai, hathPerks: EHHathPerksResponse) {
|
private suspend fun configure(source: EHentai, hathPerks: EHHathPerksResponse) {
|
||||||
// Delete old app profiles
|
// Delete old app profiles
|
||||||
val scanReq = source.requestWithCreds().url(source.uconfigUrl).build()
|
val scanReq = source.requestWithCreds().url(source.uconfigUrl).build()
|
||||||
val resp = configuratorClient.newCall(scanReq).execute().asJsoup()
|
val resp = configuratorClient.newCall(scanReq).await().asJsoup()
|
||||||
var lastDoc = resp
|
var lastDoc = resp
|
||||||
resp.select(PROFILE_SELECTOR).forEach {
|
resp.select(PROFILE_SELECTOR).forEach {
|
||||||
if (it.text() == PROFILE_NAME) {
|
if (it.text() == PROFILE_NAME) {
|
||||||
@ -127,7 +128,7 @@ class EHConfigurator(val context: Context) {
|
|||||||
.url(source.uconfigUrl)
|
.url(source.uconfigUrl)
|
||||||
.post(form)
|
.post(form)
|
||||||
.build()
|
.build()
|
||||||
).execute()
|
).await()
|
||||||
|
|
||||||
// Persist slot + sk
|
// Persist slot + sk
|
||||||
source.spPref().set(slot)
|
source.spPref().set(slot)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package exh.ui.batchadd
|
package exh.ui.batchadd
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import com.elvishew.xlog.XLog
|
||||||
import com.jakewharton.rxrelay.BehaviorRelay
|
import com.jakewharton.rxrelay.BehaviorRelay
|
||||||
import com.jakewharton.rxrelay.ReplayRelay
|
import com.jakewharton.rxrelay.ReplayRelay
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
@ -9,13 +10,20 @@ import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
|||||||
import exh.GalleryAddEvent
|
import exh.GalleryAddEvent
|
||||||
import exh.GalleryAdder
|
import exh.GalleryAdder
|
||||||
import exh.util.trimOrNull
|
import exh.util.trimOrNull
|
||||||
|
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.ensureActive
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import kotlin.concurrent.thread
|
|
||||||
|
|
||||||
class BatchAddPresenter : BasePresenter<BatchAddController>() {
|
class BatchAddPresenter : BasePresenter<BatchAddController>() {
|
||||||
|
|
||||||
private val galleryAdder by lazy { GalleryAdder() }
|
private val galleryAdder by lazy { GalleryAdder() }
|
||||||
|
private val scope = CoroutineScope(Job() + Dispatchers.Main)
|
||||||
|
|
||||||
val progressTotalRelay = BehaviorRelay.create(0)!!
|
val progressTotalRelay = BehaviorRelay.create(0)!!
|
||||||
val progressRelay = BehaviorRelay.create(0)!!
|
val progressRelay = BehaviorRelay.create(0)!!
|
||||||
@ -50,12 +58,17 @@ class BatchAddPresenter : BasePresenter<BatchAddController>() {
|
|||||||
|
|
||||||
currentlyAddingRelay.call(STATE_INPUT_TO_PROGRESS)
|
currentlyAddingRelay.call(STATE_INPUT_TO_PROGRESS)
|
||||||
|
|
||||||
thread {
|
val handler = CoroutineExceptionHandler { _, throwable ->
|
||||||
|
XLog.e(throwable)
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.launch(Dispatchers.IO + handler) {
|
||||||
val succeeded = mutableListOf<String>()
|
val succeeded = mutableListOf<String>()
|
||||||
val failed = mutableListOf<String>()
|
val failed = mutableListOf<String>()
|
||||||
|
|
||||||
splitGalleries.forEachIndexed { i, s ->
|
splitGalleries.forEachIndexed { i, s ->
|
||||||
val result = galleryAdder.addGallery(context, s, true)
|
ensureActive()
|
||||||
|
val result = withContext(Dispatchers.IO) { galleryAdder.addGallery(context, s, true) }
|
||||||
if (result is GalleryAddEvent.Success) {
|
if (result is GalleryAddEvent.Success) {
|
||||||
succeeded.add(s)
|
succeeded.add(s)
|
||||||
} else {
|
} else {
|
||||||
|
@ -25,6 +25,9 @@ import exh.source.DelegatedHttpSource
|
|||||||
import exh.util.melt
|
import exh.util.melt
|
||||||
import kotlinx.android.synthetic.main.eh_activity_captcha.toolbar
|
import kotlinx.android.synthetic.main.eh_activity_captcha.toolbar
|
||||||
import kotlinx.android.synthetic.main.eh_activity_captcha.webview
|
import kotlinx.android.synthetic.main.eh_activity_captcha.webview
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.serialization.decodeFromString
|
import kotlinx.serialization.decodeFromString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.JsonObject
|
import kotlinx.serialization.json.JsonObject
|
||||||
@ -179,13 +182,13 @@ class BrowserActionActivity : AppCompatActivity() {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun captchaSolveFail() {
|
suspend fun captchaSolveFail() {
|
||||||
currentLoopId = null
|
currentLoopId = null
|
||||||
validateCurrentLoopId = null
|
validateCurrentLoopId = null
|
||||||
XLog.e(IllegalStateException("Captcha solve failure!"))
|
XLog.e(IllegalStateException("Captcha solve failure!"))
|
||||||
runOnUiThread {
|
withContext(Dispatchers.Main) {
|
||||||
webview.evaluateJavascript(SOLVE_UI_SCRIPT_HIDE, null)
|
webview.evaluateJavascript(SOLVE_UI_SCRIPT_HIDE, null)
|
||||||
MaterialDialog(this)
|
MaterialDialog(this@BrowserActionActivity)
|
||||||
.title(R.string.captcha_solve_failure)
|
.title(R.string.captcha_solve_failure)
|
||||||
.message(R.string.captcha_solve_failure_message)
|
.message(R.string.captcha_solve_failure_message)
|
||||||
.cancelable(true)
|
.cancelable(true)
|
||||||
@ -196,7 +199,7 @@ class BrowserActionActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
fun callback(result: String?, loopId: String, stage: Int) {
|
suspend fun callback(result: String?, loopId: String, stage: Int) {
|
||||||
if (loopId != currentLoopId) return
|
if (loopId != currentLoopId) return
|
||||||
|
|
||||||
when (stage) {
|
when (stage) {
|
||||||
@ -259,7 +262,7 @@ class BrowserActionActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
captchaSolveFail()
|
runBlocking { captchaSolveFail() }
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@ -456,7 +459,7 @@ class BrowserActionActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
fun validateCaptchaCallback(result: Boolean, loopId: String) {
|
suspend fun validateCaptchaCallback(result: Boolean, loopId: String) {
|
||||||
if (loopId != validateCurrentLoopId) return
|
if (loopId != validateCurrentLoopId) return
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
|
@ -16,10 +16,11 @@ import eu.kanade.tachiyomi.ui.main.MainActivity
|
|||||||
import eu.kanade.tachiyomi.ui.manga.MangaController
|
import eu.kanade.tachiyomi.ui.manga.MangaController
|
||||||
import exh.GalleryAddEvent
|
import exh.GalleryAddEvent
|
||||||
import exh.GalleryAdder
|
import exh.GalleryAdder
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import rx.Subscription
|
import rx.Subscription
|
||||||
import rx.android.schedulers.AndroidSchedulers
|
import rx.android.schedulers.AndroidSchedulers
|
||||||
import rx.subjects.BehaviorSubject
|
import rx.subjects.BehaviorSubject
|
||||||
import kotlin.concurrent.thread
|
|
||||||
|
|
||||||
class InterceptActivity : BaseActivity<EhActivityInterceptBinding>() {
|
class InterceptActivity : BaseActivity<EhActivityInterceptBinding>() {
|
||||||
private var statusSubscription: Subscription? = null
|
private var statusSubscription: Subscription? = null
|
||||||
@ -119,14 +120,14 @@ class InterceptActivity : BaseActivity<EhActivityInterceptBinding>() {
|
|||||||
@Synchronized
|
@Synchronized
|
||||||
private fun loadGalleryEnd(gallery: String, source: UrlImportableSource? = null) {
|
private fun loadGalleryEnd(gallery: String, source: UrlImportableSource? = null) {
|
||||||
// Load gallery async
|
// Load gallery async
|
||||||
thread {
|
scope.launch(Dispatchers.IO) {
|
||||||
val result = galleryAdder.addGallery(this, gallery, forceSource = source)
|
val result = galleryAdder.addGallery(this@InterceptActivity, gallery, forceSource = source)
|
||||||
|
|
||||||
status.onNext(
|
status.onNext(
|
||||||
when (result) {
|
when (result) {
|
||||||
is GalleryAddEvent.Success -> result.manga.id?.let {
|
is GalleryAddEvent.Success -> result.manga.id?.let {
|
||||||
InterceptResult.Success(it)
|
InterceptResult.Success(it)
|
||||||
} ?: InterceptResult.Failure(this.getString(R.string.manga_id_is_null))
|
} ?: InterceptResult.Failure(this@InterceptActivity.getString(R.string.manga_id_is_null))
|
||||||
is GalleryAddEvent.Fail -> InterceptResult.Failure(result.logMessage)
|
is GalleryAddEvent.Fail -> InterceptResult.Failure(result.logMessage)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -5,6 +5,7 @@ import eu.kanade.tachiyomi.source.model.MangasPage
|
|||||||
import eu.kanade.tachiyomi.source.online.UrlImportableSource
|
import eu.kanade.tachiyomi.source.online.UrlImportableSource
|
||||||
import exh.GalleryAddEvent
|
import exh.GalleryAddEvent
|
||||||
import exh.GalleryAdder
|
import exh.GalleryAdder
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
|
|
||||||
private val galleryAdder by lazy {
|
private val galleryAdder by lazy {
|
||||||
@ -17,19 +18,24 @@ private val galleryAdder by lazy {
|
|||||||
fun UrlImportableSource.urlImportFetchSearchManga(context: Context, query: String, fail: () -> Observable<MangasPage>): Observable<MangasPage> =
|
fun UrlImportableSource.urlImportFetchSearchManga(context: Context, query: String, fail: () -> Observable<MangasPage>): Observable<MangasPage> =
|
||||||
when {
|
when {
|
||||||
query.startsWith("http://") || query.startsWith("https://") -> {
|
query.startsWith("http://") || query.startsWith("https://") -> {
|
||||||
Observable.fromCallable {
|
flow {
|
||||||
val res = galleryAdder.addGallery(context, query, false, this)
|
emit(
|
||||||
MangasPage(
|
galleryAdder.addGallery(context, query, false, this@urlImportFetchSearchManga)
|
||||||
(
|
|
||||||
if (res is GalleryAddEvent.Success) {
|
|
||||||
listOf(res.manga)
|
|
||||||
} else {
|
|
||||||
emptyList()
|
|
||||||
}
|
|
||||||
),
|
|
||||||
false
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
.asObservable()
|
||||||
|
.map { res ->
|
||||||
|
MangasPage(
|
||||||
|
(
|
||||||
|
if (res is GalleryAddEvent.Success) {
|
||||||
|
listOf(res.manga)
|
||||||
|
} else {
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else -> fail()
|
else -> fail()
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user