Optimize imports, disallow wildcard imports because of klint, run linter

This commit is contained in:
jobobby04 2020-04-04 16:30:05 -04:00 committed by Jobobby04
parent f18891a07e
commit 23ac3d18e5
138 changed files with 1192 additions and 1027 deletions

View File

@ -43,7 +43,6 @@ class CategoryPutResolver : DefaultPutResolver<Category>() {
put(COL_FLAGS, obj.flags) put(COL_FLAGS, obj.flags)
val orderString = obj.mangaOrder.joinToString("/") val orderString = obj.mangaOrder.joinToString("/")
put(COL_MANGA_ORDER, orderString) put(COL_MANGA_ORDER, orderString)
} }
} }

View File

@ -29,6 +29,4 @@ class MangaUrlPutResolver : PutResolver<Manga>() {
fun mapToContentValues(manga: Manga) = ContentValues(1).apply { fun mapToContentValues(manga: Manga) = ContentValues(1).apply {
put(MangaTable.COL_URL, manga.url) put(MangaTable.COL_URL, manga.url)
} }
} }

View File

@ -24,7 +24,6 @@ object CategoryTable {
$COL_MANGA_ORDER TEXT NOT NULL $COL_MANGA_ORDER TEXT NOT NULL
)""" )"""
val addMangaOrder: String val addMangaOrder: String
get() = "ALTER TABLE $TABLE ADD COLUMN $COL_MANGA_ORDER TEXT" get() = "ALTER TABLE $TABLE ADD COLUMN $COL_MANGA_ORDER TEXT"
} }

View File

@ -9,11 +9,11 @@ import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.metadata.metadata.base.getFlatMetadataForManga import exh.metadata.metadata.base.getFlatMetadataForManga
import exh.metadata.metadata.base.insertFlatMetadata import exh.metadata.metadata.base.insertFlatMetadata
import kotlin.reflect.KClass
import rx.Completable import rx.Completable
import rx.Single import rx.Single
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import kotlin.reflect.KClass
/** /**
* LEWD! * LEWD!

View File

@ -3,7 +3,12 @@ package eu.kanade.tachiyomi.source.online.all
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import com.elvishew.xlog.XLog import com.elvishew.xlog.XLog
import com.github.salomonbrys.kotson.* import com.github.salomonbrys.kotson.array
import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.int
import com.github.salomonbrys.kotson.obj
import com.github.salomonbrys.kotson.set
import com.github.salomonbrys.kotson.string
import com.google.gson.JsonArray import com.google.gson.JsonArray
import com.google.gson.JsonObject import com.google.gson.JsonObject
import com.google.gson.JsonParser import com.google.gson.JsonParser
@ -13,7 +18,12 @@ import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.network.asObservableWithAsyncStacktrace import eu.kanade.tachiyomi.network.asObservableWithAsyncStacktrace
import eu.kanade.tachiyomi.source.model.* import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.LewdSource import eu.kanade.tachiyomi.source.online.LewdSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource import eu.kanade.tachiyomi.source.online.UrlImportableSource
@ -36,23 +46,30 @@ import exh.util.UriFilter
import exh.util.UriGroup import exh.util.UriGroup
import exh.util.ignore import exh.util.ignore
import exh.util.urlImportFetchSearchManga import exh.util.urlImportFetchSearchManga
import java.net.URLEncoder
import java.util.ArrayList
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import okhttp3.* import okhttp3.CacheControl
import okhttp3.CookieJar
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.Request
import okhttp3.RequestBody
import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import org.jsoup.nodes.TextNode import org.jsoup.nodes.TextNode
import rx.Observable import rx.Observable
import rx.Single import rx.Single
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.net.URLEncoder
import java.util.*
// TODO Consider gallery updating when doing tabbed browsing // TODO Consider gallery updating when doing tabbed browsing
class EHentai(override val id: Long, class EHentai(
override val id: Long,
val exh: Boolean, val exh: Boolean,
val context: Context) : HttpSource(), LewdSource<EHentaiSearchMetadata, Document>, UrlImportableSource { val context: Context
) : HttpSource(), LewdSource<EHentaiSearchMetadata, Document>, UrlImportableSource {
override val metaClass = EHentaiSearchMetadata::class override val metaClass = EHentaiSearchMetadata::class
val schema: String val schema: String
@ -111,8 +128,8 @@ class EHentai(override val id: Long,
val parsedLocation = doc.location().toHttpUrlOrNull() val parsedLocation = doc.location().toHttpUrlOrNull()
// Add to page if required // Add to page if required
val hasNextPage = if (parsedLocation == null val hasNextPage = if (parsedLocation == null ||
|| !parsedLocation.queryParameterNames.contains(REVERSE_PARAM)) { !parsedLocation.queryParameterNames.contains(REVERSE_PARAM)) {
select("a[onclick=return false]").last()?.let { select("a[onclick=return false]").last()?.let {
it.text() == ">" it.text() == ">"
} ?: false } ?: false
@ -212,8 +229,11 @@ class EHentai(override val id: Long,
} }
}!! }!!
private fun fetchChapterPage(chapter: SChapter, np: String, private fun fetchChapterPage(
pastUrls: List<String> = emptyList()): Observable<List<String>> { chapter: SChapter,
np: String,
pastUrls: List<String> = emptyList()
): Observable<List<String>> {
val urls = ArrayList(pastUrls) val urls = ArrayList(pastUrls)
return chapterPageCall(np).flatMap { return chapterPageCall(np).flatMap {
val jsoup = it.asJsoup() val jsoup = it.asJsoup()
@ -407,8 +427,8 @@ class EHentai(override val id: Long,
} }
lastUpdateCheck = System.currentTimeMillis() lastUpdateCheck = System.currentTimeMillis()
if (datePosted != null if (datePosted != null &&
&& lastUpdateCheck - datePosted!! > EHentaiUpdateWorkerConstants.GALLERY_AGE_TIME) { lastUpdateCheck - datePosted!! > EHentaiUpdateWorkerConstants.GALLERY_AGE_TIME) {
aged = true aged = true
XLog.d("aged %s - too old", title) XLog.d("aged %s - too old", title)
} }
@ -713,7 +733,6 @@ class EHentai(override val id: Long,
return "${uri.scheme}://${uri.host}/g/${obj["gid"].int}/${obj["token"].string}/" return "${uri.scheme}://${uri.host}/g/${obj["gid"].int}/${obj["token"].string}/"
} }
companion object { companion object {
private const val QUERY_PREFIX = "?f_apply=Apply+Filter" private const val QUERY_PREFIX = "?f_apply=Apply+Filter"
private const val TR_SUFFIX = "TR" private const val TR_SUFFIX = "TR"
@ -738,6 +757,5 @@ class EHentai(override val id: Long,
fun buildCookies(cookies: Map<String, String>) = cookies.entries.joinToString(separator = "; ") { fun buildCookies(cookies: Map<String, String>) = cookies.entries.joinToString(separator = "; ") {
"${URLEncoder.encode(it.key, "UTF-8")}=${URLEncoder.encode(it.value, "UTF-8")}" "${URLEncoder.encode(it.key, "UTF-8")}=${URLEncoder.encode(it.value, "UTF-8")}"
} }
} }
} }

View File

@ -10,7 +10,11 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.* import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.LewdSource import eu.kanade.tachiyomi.source.online.LewdSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource import eu.kanade.tachiyomi.source.online.UrlImportableSource
@ -24,6 +28,8 @@ import exh.metadata.metadata.HitomiSearchMetadata.Companion.TAG_TYPE_DEFAULT
import exh.metadata.metadata.base.RaisedSearchMetadata.Companion.TAG_TYPE_VIRTUAL import exh.metadata.metadata.base.RaisedSearchMetadata.Companion.TAG_TYPE_VIRTUAL
import exh.metadata.metadata.base.RaisedTag import exh.metadata.metadata.base.RaisedTag
import exh.util.urlImportFetchSearchManga import exh.util.urlImportFetchSearchManga
import java.text.SimpleDateFormat
import java.util.Locale
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
@ -32,8 +38,6 @@ import rx.Observable
import rx.Single import rx.Single
import rx.schedulers.Schedulers import rx.schedulers.Schedulers
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat
import java.util.*
/** /**
* Man, I hate this source :( * Man, I hate this source :(
@ -61,8 +65,8 @@ class Hitomi : HttpSource(), LewdSource<HitomiSearchMetadata, Document>, UrlImpo
private var tagIndexVersionCacheTime: Long = 0 private var tagIndexVersionCacheTime: Long = 0
private fun tagIndexVersion(): Single<Long> { private fun tagIndexVersion(): Single<Long> {
val sCachedTagIndexVersion = cachedTagIndexVersion val sCachedTagIndexVersion = cachedTagIndexVersion
return if (sCachedTagIndexVersion == null return if (sCachedTagIndexVersion == null ||
|| tagIndexVersionCacheTime + INDEX_VERSION_CACHE_TIME_MS < System.currentTimeMillis()) { tagIndexVersionCacheTime + INDEX_VERSION_CACHE_TIME_MS < System.currentTimeMillis()) {
HitomiNozomi.getIndexVersion(client, "tagindex").subscribeOn(Schedulers.io()).doOnNext { HitomiNozomi.getIndexVersion(client, "tagindex").subscribeOn(Schedulers.io()).doOnNext {
cachedTagIndexVersion = it cachedTagIndexVersion = it
tagIndexVersionCacheTime = System.currentTimeMillis() tagIndexVersionCacheTime = System.currentTimeMillis()
@ -76,8 +80,8 @@ class Hitomi : HttpSource(), LewdSource<HitomiSearchMetadata, Document>, UrlImpo
private var galleryIndexVersionCacheTime: Long = 0 private var galleryIndexVersionCacheTime: Long = 0
private fun galleryIndexVersion(): Single<Long> { private fun galleryIndexVersion(): Single<Long> {
val sCachedGalleryIndexVersion = cachedGalleryIndexVersion val sCachedGalleryIndexVersion = cachedGalleryIndexVersion
return if (sCachedGalleryIndexVersion == null return if (sCachedGalleryIndexVersion == null ||
|| galleryIndexVersionCacheTime + INDEX_VERSION_CACHE_TIME_MS < System.currentTimeMillis()) { galleryIndexVersionCacheTime + INDEX_VERSION_CACHE_TIME_MS < System.currentTimeMillis()) {
HitomiNozomi.getIndexVersion(client, "galleriesindex").subscribeOn(Schedulers.io()).doOnNext { HitomiNozomi.getIndexVersion(client, "galleriesindex").subscribeOn(Schedulers.io()).doOnNext {
cachedGalleryIndexVersion = it cachedGalleryIndexVersion = it
galleryIndexVersionCacheTime = System.currentTimeMillis() galleryIndexVersionCacheTime = System.currentTimeMillis()
@ -307,7 +311,6 @@ class Hitomi : HttpSource(), LewdSource<HitomiSearchMetadata, Document>, UrlImpo
} }
} }
/** /**
* Returns an observable with the updated details for a manga. Normally it's not needed to * Returns an observable with the updated details for a manga. Normally it's not needed to
* override this method. * override this method.
@ -423,5 +426,4 @@ class Hitomi : HttpSource(), LewdSource<HitomiSearchMetadata, Document>, UrlImpo
SimpleDateFormat("yyyy-MM-dd HH:mm:ss'-05'", Locale.US) SimpleDateFormat("yyyy-MM-dd HH:mm:ss'-05'", Locale.US)
} }
} }
} }

View File

@ -2,13 +2,22 @@ package eu.kanade.tachiyomi.source.online.all
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import com.github.salomonbrys.kotson.* import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.nullArray
import com.github.salomonbrys.kotson.nullLong
import com.github.salomonbrys.kotson.nullObj
import com.github.salomonbrys.kotson.nullString
import com.google.gson.JsonParser import com.google.gson.JsonParser
import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.* import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.LewdSource import eu.kanade.tachiyomi.source.online.LewdSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource import eu.kanade.tachiyomi.source.online.UrlImportableSource
@ -305,7 +314,6 @@ class NHentai(context: Context) : HttpSource(), LewdSource<NHentaiSearchMetadata
Pair("Chinese", " chinese") Pair("Chinese", " chinese")
) )
val jsonParser by lazy { val jsonParser by lazy {
JsonParser() JsonParser()
} }

View File

@ -3,7 +3,12 @@ package eu.kanade.tachiyomi.source.online.all
import android.net.Uri import android.net.Uri
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.* import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.LewdSource import eu.kanade.tachiyomi.source.online.LewdSource
import eu.kanade.tachiyomi.source.online.ParsedHttpSource import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource import eu.kanade.tachiyomi.source.online.UrlImportableSource
@ -17,14 +22,15 @@ import exh.metadata.metadata.base.RaisedTag
import exh.util.UriFilter import exh.util.UriFilter
import exh.util.UriGroup import exh.util.UriGroup
import exh.util.urlImportFetchSearchManga import exh.util.urlImportFetchSearchManga
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.TimeZone
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import org.jsoup.nodes.TextNode import org.jsoup.nodes.TextNode
import rx.Observable import rx.Observable
import java.text.SimpleDateFormat
import java.util.*
// TODO Transform into delegated source // TODO Transform into delegated source
class PervEden(override val id: Long, val pvLang: PervEdenLang) : ParsedHttpSource(), class PervEden(override val id: Long, val pvLang: PervEdenLang) : ParsedHttpSource(),

View File

@ -2,10 +2,14 @@ package eu.kanade.tachiyomi.source.online.english
import android.net.Uri import android.net.Uri
import com.kizitonwose.time.hours import com.kizitonwose.time.hours
import hu.akarnokd.rxjava.interop.RxJavaInterop
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.* import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.LewdSource import eu.kanade.tachiyomi.source.online.LewdSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource import eu.kanade.tachiyomi.source.online.UrlImportableSource
@ -17,13 +21,18 @@ import exh.util.CachedField
import exh.util.NakedTrie import exh.util.NakedTrie
import exh.util.await import exh.util.await
import exh.util.urlImportFetchSearchManga import exh.util.urlImportFetchSearchManga
import hu.akarnokd.rxjava.interop.RxJavaInterop
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.rx2.asSingle import kotlinx.coroutines.rx2.asSingle
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import okhttp3.* import okhttp3.Headers
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element

View File

@ -4,12 +4,16 @@ import android.net.Uri
import com.github.salomonbrys.kotson.array import com.github.salomonbrys.kotson.array
import com.github.salomonbrys.kotson.string import com.github.salomonbrys.kotson.string
import com.google.gson.JsonParser import com.google.gson.JsonParser
import hu.akarnokd.rxjava.interop.RxJavaInterop
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservable import eu.kanade.tachiyomi.network.asObservable
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.* import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.LewdSource import eu.kanade.tachiyomi.source.online.LewdSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource import eu.kanade.tachiyomi.source.online.UrlImportableSource
@ -23,7 +27,9 @@ import exh.search.Text
import exh.util.await import exh.util.await
import exh.util.dropBlank import exh.util.dropBlank
import exh.util.urlImportFetchSearchManga import exh.util.urlImportFetchSearchManga
import hu.akarnokd.rxjava.interop.RxJavaInterop
import info.debatty.java.stringsimilarity.Levenshtein import info.debatty.java.stringsimilarity.Levenshtein
import kotlin.math.ceil
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async import kotlinx.coroutines.async
@ -36,7 +42,6 @@ import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import rx.Observable import rx.Observable
import rx.schedulers.Schedulers import rx.schedulers.Schedulers
import kotlin.math.ceil
class HBrowse : HttpSource(), LewdSource<HBrowseSearchMetadata, Document>, UrlImportableSource { class HBrowse : HttpSource(), LewdSource<HBrowseSearchMetadata, Document>, UrlImportableSource {
/** /**
@ -182,7 +187,6 @@ class HBrowse : HttpSource(), LewdSource<HBrowseSearchMetadata, Document>, UrlIm
} }
} }
"/result" "/result"
} else { } else {
"/search" "/search"

View File

@ -4,29 +4,25 @@ import android.net.Uri
import com.google.gson.JsonParser import com.google.gson.JsonParser
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.LewdSource import eu.kanade.tachiyomi.source.online.LewdSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource import eu.kanade.tachiyomi.source.online.UrlImportableSource
import eu.kanade.tachiyomi.util.system.asJsoup import eu.kanade.tachiyomi.util.system.asJsoup
import exh.metadata.metadata.TsuminoSearchMetadata import exh.metadata.metadata.TsuminoSearchMetadata
import exh.metadata.metadata.TsuminoSearchMetadata.Companion.BASE_URL
import exh.metadata.metadata.TsuminoSearchMetadata.Companion.TAG_TYPE_DEFAULT import exh.metadata.metadata.TsuminoSearchMetadata.Companion.TAG_TYPE_DEFAULT
import exh.metadata.metadata.base.RaisedSearchMetadata.Companion.TAG_TYPE_VIRTUAL import exh.metadata.metadata.base.RaisedSearchMetadata.Companion.TAG_TYPE_VIRTUAL
import exh.metadata.metadata.base.RaisedTag import exh.metadata.metadata.base.RaisedTag
import exh.source.DelegatedHttpSource import exh.source.DelegatedHttpSource
import exh.util.dropBlank
import exh.util.trimAll
import exh.util.urlImportFetchSearchManga import exh.util.urlImportFetchSearchManga
import java.text.SimpleDateFormat
import java.util.Locale
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import rx.Observable import rx.Observable
import java.text.SimpleDateFormat
import java.util.*
class Tsumino(delegate: HttpSource) : DelegatedHttpSource(delegate), class Tsumino(delegate: HttpSource) : DelegatedHttpSource(delegate),
LewdSource<TsuminoSearchMetadata, Document>, UrlImportableSource { LewdSource<TsuminoSearchMetadata, Document>, UrlImportableSource {
override val metaClass = TsuminoSearchMetadata::class; override val metaClass = TsuminoSearchMetadata::class
override val lang = "en" override val lang = "en"
// Support direct URL importing // Support direct URL importing

View File

@ -1,7 +1,6 @@
package eu.kanade.tachiyomi.ui.browse.source.filter package eu.kanade.tachiyomi.ui.browse.source.filter
import android.view.View import android.view.View
import androidx.recyclerview.widget.RecyclerView
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractExpandableHeaderItem import eu.davidea.flexibleadapter.items.AbstractExpandableHeaderItem
import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.flexibleadapter.items.IFlexible

View File

@ -5,21 +5,24 @@ import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.tables.MangaTable import eu.kanade.tachiyomi.data.database.tables.MangaTable
import eu.kanade.tachiyomi.ui.category.CategoryAdapter
import exh.isLewdSource import exh.isLewdSource
import exh.metadata.sql.tables.SearchMetadataTable import exh.metadata.sql.tables.SearchMetadataTable
import exh.search.SearchEngine import exh.search.SearchEngine
import exh.util.await import exh.util.await
import exh.util.cancellable import exh.util.cancellable
import kotlinx.coroutines.* import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.toList import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber import timber.log.Timber
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import androidx.recyclerview.widget.RecyclerView
import eu.davidea.flexibleadapter.SelectableAdapter
import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.ui.category.CategoryAdapter
/** /**
* Adapter storing a list of manga in a certain category. * Adapter storing a list of manga in a certain category.

View File

@ -1,8 +1,8 @@
package eu.kanade.tachiyomi.ui.library package eu.kanade.tachiyomi.ui.library
import android.view.View import android.view.View
import eu.davidea.flexibleadapter.FlexibleAdapter
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
@ -26,7 +26,6 @@ abstract class LibraryHolder(
*/ */
abstract fun onSetValues(item: LibraryItem) abstract fun onSetValues(item: LibraryItem)
/** /**
* Called when an item is released. * Called when an item is released.
* *
@ -36,5 +35,4 @@ abstract class LibraryHolder(
super.onItemReleased(position) super.onItemReleased(position)
(adapter as? LibraryCategoryAdapter)?.onItemReleaseListener?.onItemReleased(position) (adapter as? LibraryCategoryAdapter)?.onItemReleaseListener?.onItemReleased(position)
} }
} }

View File

@ -235,4 +235,3 @@ class MangaInfoPresenter(
return toInsert return toInsert
} }
} }

View File

@ -17,7 +17,17 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.preference.PreferenceKeys import eu.kanade.tachiyomi.data.preference.PreferenceKeys
import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.util.preference.* import eu.kanade.tachiyomi.util.preference.defaultValue
import eu.kanade.tachiyomi.util.preference.entriesRes
import eu.kanade.tachiyomi.util.preference.intListPreference
import eu.kanade.tachiyomi.util.preference.listPreference
import eu.kanade.tachiyomi.util.preference.multiSelectListPreference
import eu.kanade.tachiyomi.util.preference.onChange
import eu.kanade.tachiyomi.util.preference.onClick
import eu.kanade.tachiyomi.util.preference.preference
import eu.kanade.tachiyomi.util.preference.preferenceCategory
import eu.kanade.tachiyomi.util.preference.summaryRes
import eu.kanade.tachiyomi.util.preference.switchPreference
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import exh.EH_SOURCE_ID import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID import exh.EXH_SOURCE_ID
@ -34,6 +44,7 @@ import exh.ui.login.LoginController
import exh.util.await import exh.util.await
import exh.util.trans import exh.util.trans
import humanize.Humanize import humanize.Humanize
import java.util.Date
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -41,7 +52,6 @@ import kotlinx.coroutines.withContext
import rx.android.schedulers.AndroidSchedulers import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers import rx.schedulers.Schedulers
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.util.*
/** /**
* EH Settings fragment * EH Settings fragment

View File

@ -53,7 +53,7 @@ private val lewdDelegatedSourceIds = SourceManager.DELEGATED_SOURCES.filter {
}.map { it.value.sourceId }.sorted() }.map { it.value.sourceId }.sorted()
// This method MUST be fast! // This method MUST be fast!
fun isLewdSource(source: Long) = source in 6900..6999 fun isLewdSource(source: Long) = source in 6900..6999 ||
|| lewdDelegatedSourceIds.binarySearch(source) >= 0 lewdDelegatedSourceIds.binarySearch(source) >= 0
fun Source.isEhBasedSource() = id == EH_SOURCE_ID || id == EXH_SOURCE_ID fun Source.isEhBasedSource() = id == EH_SOURCE_ID || id == EXH_SOURCE_ID

View File

@ -2,6 +2,8 @@ package exh
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
data class EXHSavedSearch(val name: String, data class EXHSavedSearch(
val name: String,
val query: String, val query: String,
val filterList: FilterList) val filterList: FilterList
)

View File

@ -16,10 +16,12 @@ class GalleryAdder {
private val sourceManager: SourceManager by injectLazy() private val sourceManager: SourceManager by injectLazy()
fun addGallery(url: String, fun addGallery(
url: String,
fav: Boolean = false, fav: Boolean = false,
forceSource: UrlImportableSource? = null, forceSource: UrlImportableSource? = null,
throttleFunc: () -> Unit = {}): GalleryAddEvent { throttleFunc: () -> Unit = {}
): GalleryAddEvent {
XLog.d("Importing gallery (url: %s, fav: %s, forceSource: %s)...", url, fav, forceSource) XLog.d("Importing gallery (url: %s, fav: %s, forceSource: %s)...", url, fav, forceSource)
try { try {
val uri = Uri.parse(url) val uri = Uri.parse(url)
@ -120,8 +122,10 @@ sealed class GalleryAddEvent {
abstract val galleryUrl: String abstract val galleryUrl: String
open val galleryTitle: String? = null open val galleryTitle: String? = null
class Success(override val galleryUrl: String, class Success(
val manga: Manga): GalleryAddEvent() { override val galleryUrl: String,
val manga: Manga
) : GalleryAddEvent() {
override val galleryTitle = manga.title override val galleryTitle = manga.title
override val logMessage = "Added gallery: $galleryTitle" override val logMessage = "Added gallery: $galleryTitle"
} }
@ -131,8 +135,10 @@ sealed class GalleryAddEvent {
override val logMessage = "Unknown gallery type for gallery: $galleryUrl" override val logMessage = "Unknown gallery type for gallery: $galleryUrl"
} }
open class Error(override val galleryUrl: String, open class Error(
override val logMessage: String): Fail() override val galleryUrl: String,
override val logMessage: String
) : Fail()
class NotFound(galleryUrl: String) : class NotFound(galleryUrl: String) :
Error(galleryUrl, "Gallery does not exist: $galleryUrl") Error(galleryUrl, "Gallery does not exist: $galleryUrl")

View File

@ -9,20 +9,19 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.util.system.jobScheduler import eu.kanade.tachiyomi.util.system.jobScheduler
import exh.EH_SOURCE_ID import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID
import exh.EXHMigrations import exh.EXHMigrations
import exh.EXH_SOURCE_ID
import exh.eh.EHentaiUpdateWorker import exh.eh.EHentaiUpdateWorker
import exh.metadata.metadata.EHentaiSearchMetadata import exh.metadata.metadata.EHentaiSearchMetadata
import exh.metadata.metadata.base.getFlatMetadataForManga import exh.metadata.metadata.base.getFlatMetadataForManga
import exh.metadata.metadata.base.insertFlatMetadata import exh.metadata.metadata.base.insertFlatMetadata
import exh.util.await import exh.util.await
import exh.util.cancellable import exh.util.cancellable
import uy.kohesive.injekt.injectLazy
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.toList import kotlinx.coroutines.flow.toList
import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.runBlocking
import uy.kohesive.injekt.injectLazy
object DebugFunctions { object DebugFunctions {
val app: Application by injectLazy() val app: Application by injectLazy()

View File

@ -8,8 +8,12 @@ import android.widget.HorizontalScrollView
import android.widget.TextView import android.widget.TextView
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import eu.kanade.tachiyomi.ui.setting.* import eu.kanade.tachiyomi.ui.setting.SettingsController
import eu.kanade.tachiyomi.util.preference.* import eu.kanade.tachiyomi.util.preference.defaultValue
import eu.kanade.tachiyomi.util.preference.onClick
import eu.kanade.tachiyomi.util.preference.preference
import eu.kanade.tachiyomi.util.preference.preferenceCategory
import eu.kanade.tachiyomi.util.preference.switchPreference
import kotlin.reflect.KVisibility import kotlin.reflect.KVisibility
import kotlin.reflect.full.declaredFunctions import kotlin.reflect.full.declaredFunctions

View File

@ -1,7 +1,9 @@
package exh.eh package exh.eh
class EHentaiThrottleManager(private val max: Int = THROTTLE_MAX, class EHentaiThrottleManager(
private val inc: Int = THROTTLE_INC) { private val max: Int = THROTTLE_MAX,
private val inc: Int = THROTTLE_INC
) {
private var lastThrottleTime: Long = 0 private var lastThrottleTime: Long = 0
var throttleTime: Long = 0 var throttleTime: Long = 0
private set private set

View File

@ -8,10 +8,10 @@ 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 exh.metadata.metadata.EHentaiSearchMetadata import exh.metadata.metadata.EHentaiSearchMetadata
import exh.metadata.metadata.base.getFlatMetadataForManga import exh.metadata.metadata.base.getFlatMetadataForManga
import java.io.File
import rx.Observable import rx.Observable
import rx.Single import rx.Single
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.io.File
data class ChapterChain(val manga: Manga, val chapters: List<Chapter>) data class ChapterChain(val manga: Manga, val chapters: List<Chapter>)
@ -75,9 +75,9 @@ class EHentaiUpdateHelper(context: Context) {
chain.chapters.map { chapter -> chain.chapters.map { chapter ->
// Convert old style chapters to new style chapters if possible // Convert old style chapters to new style chapters if possible
if(chapter.date_upload <= 0 if (chapter.date_upload <= 0 &&
&& meta?.datePosted != null meta?.datePosted != null &&
&& meta?.title != null) { meta?.title != null) {
chapter.name = meta!!.title!! chapter.name = meta!!.title!!
chapter.date_upload = meta!!.datePosted!! chapter.date_upload = meta!!.datePosted!!
} }

View File

@ -20,8 +20,8 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.data.preference.getOrDefault
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.system.jobScheduler
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import eu.kanade.tachiyomi.util.system.jobScheduler
import exh.EH_SOURCE_ID import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID import exh.EXH_SOURCE_ID
import exh.debug.DebugToggles import exh.debug.DebugToggles
@ -31,15 +31,20 @@ import exh.metadata.metadata.base.getFlatMetadataForManga
import exh.metadata.metadata.base.insertFlatMetadata import exh.metadata.metadata.base.insertFlatMetadata
import exh.util.await import exh.util.await
import exh.util.cancellable import exh.util.cancellable
import kotlinx.coroutines.* import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.toList import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import rx.schedulers.Schedulers import rx.schedulers.Schedulers
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.coroutines.CoroutineContext
@RequiresApi(Build.VERSION_CODES.LOLLIPOP) @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
class EHentaiUpdateWorker : JobService(), CoroutineScope { class EHentaiUpdateWorker : JobService(), CoroutineScope {
@ -215,8 +220,8 @@ class EHentaiUpdateWorker: JobService(), CoroutineScope {
val (acceptedRoot, discardedRoots, hasNew) = val (acceptedRoot, discardedRoots, hasNew) =
updateHelper.findAcceptedRootAndDiscardOthers(manga.source, chapters).await() updateHelper.findAcceptedRootAndDiscardOthers(manga.source, chapters).await()
if((new.isNotEmpty() && manga.id == acceptedRoot.manga.id) if ((new.isNotEmpty() && manga.id == acceptedRoot.manga.id) ||
|| (hasNew && updatedManga.none { it.id == acceptedRoot.manga.id })) { (hasNew && updatedManga.none { it.id == acceptedRoot.manga.id })) {
updatedManga += acceptedRoot.manga updatedManga += acceptedRoot.manga
} }
@ -290,9 +295,11 @@ class EHentaiUpdateWorker: JobService(), CoroutineScope {
else JOB_ID_UPDATE_BACKGROUND, componentName()) else JOB_ID_UPDATE_BACKGROUND, componentName())
} }
private fun Context.periodicBackgroundJobInfo(period: Long, private fun Context.periodicBackgroundJobInfo(
period: Long,
requireCharging: Boolean, requireCharging: Boolean,
requireUnmetered: Boolean): JobInfo { requireUnmetered: Boolean
): JobInfo {
return baseBackgroundJobInfo(false) return baseBackgroundJobInfo(false)
.setPeriodic(period) .setPeriodic(period)
.setPersisted(true) .setPersisted(true)

View File

@ -3,9 +3,6 @@ package exh.eh
import android.util.SparseArray import android.util.SparseArray
import androidx.core.util.AtomicFile import androidx.core.util.AtomicFile
import com.elvishew.xlog.XLog import com.elvishew.xlog.XLog
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import java.io.Closeable import java.io.Closeable
import java.io.File import java.io.File
import java.io.FileNotFoundException import java.io.FileNotFoundException
@ -13,6 +10,18 @@ import java.io.InputStream
import java.nio.ByteBuffer import java.nio.ByteBuffer
import kotlin.concurrent.thread import kotlin.concurrent.thread
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
/** /**
* In memory Int -> Obj lookup table implementation that * In memory Int -> Obj lookup table implementation that

View File

@ -5,7 +5,7 @@ import io.realm.RealmObject
import io.realm.annotations.Index import io.realm.annotations.Index
import io.realm.annotations.PrimaryKey import io.realm.annotations.PrimaryKey
import io.realm.annotations.RealmClass import io.realm.annotations.RealmClass
import java.util.* import java.util.UUID
@RealmClass @RealmClass
open class FavoriteEntry : RealmObject() { open class FavoriteEntry : RealmObject() {

View File

@ -17,18 +17,21 @@ import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.system.powerManager import eu.kanade.tachiyomi.util.system.powerManager
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.system.wifiManager import eu.kanade.tachiyomi.util.system.wifiManager
import exh.* import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID
import exh.GalleryAddEvent
import exh.GalleryAdder
import exh.eh.EHentaiThrottleManager import exh.eh.EHentaiThrottleManager
import exh.eh.EHentaiUpdateWorker import exh.eh.EHentaiUpdateWorker
import exh.util.ignore import exh.util.ignore
import exh.util.trans import exh.util.trans
import kotlin.concurrent.thread
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()
@ -385,8 +388,8 @@ class FavoritesSyncHelper(val context: Context) {
} }
} }
fun needWarnThrottle() fun needWarnThrottle() =
= throttleManager.throttleTime >= THROTTLE_WARN throttleManager.throttleTime >= THROTTLE_WARN
class IgnoredException : RuntimeException() class IgnoredException : RuntimeException()
@ -399,8 +402,10 @@ sealed class FavoritesSyncStatus(val message: String) {
class Error(message: String) : FavoritesSyncStatus(message) class Error(message: String) : FavoritesSyncStatus(message)
class Idle : FavoritesSyncStatus("Waiting for sync to start") class Idle : FavoritesSyncStatus("Waiting for sync to start")
sealed class BadLibraryState(message: String) : FavoritesSyncStatus(message) { sealed class BadLibraryState(message: String) : FavoritesSyncStatus(message) {
class MangaInMultipleCategories(val manga: Manga, class MangaInMultipleCategories(
val categories: List<Category>): val manga: Manga,
val categories: List<Category>
) :
BadLibraryState("The gallery: ${manga.title} is in more than one category (${categories.joinToString { it.name }})!") BadLibraryState("The gallery: ${manga.title} is in more than one category (${categories.joinToString { it.name }})!")
} }
class Initializing : FavoritesSyncStatus("Initializing sync") class Initializing : FavoritesSyncStatus("Initializing sync")

View File

@ -20,8 +20,8 @@ class LocalFavoritesStorage {
fun getRealm() = Realm.getInstance(realmConfig) fun getRealm() = Realm.getInstance(realmConfig)
fun getChangedDbEntries(realm: Realm) fun getChangedDbEntries(realm: Realm) =
= getChangedEntries(realm, getChangedEntries(realm,
parseToFavoriteEntries( parseToFavoriteEntries(
loadDbCategories( loadDbCategories(
db.getFavoriteMangas() db.getFavoriteMangas()
@ -31,8 +31,8 @@ class LocalFavoritesStorage {
) )
) )
fun getChangedRemoteEntries(realm: Realm, entries: List<EHentai.ParsedManga>) fun getChangedRemoteEntries(realm: Realm, entries: List<EHentai.ParsedManga>) =
= getChangedEntries(realm, getChangedEntries(realm,
parseToFavoriteEntries( parseToFavoriteEntries(
entries.asSequence().map { entries.asSequence().map {
Pair(it.fav, it.manga.apply { Pair(it.fav, it.manga.apply {
@ -80,18 +80,18 @@ class LocalFavoritesStorage {
return ChangeSet(added, removed) return ChangeSet(added, removed)
} }
private fun Realm.queryRealmForEntry(entry: FavoriteEntry) private fun Realm.queryRealmForEntry(entry: FavoriteEntry) =
= where(FavoriteEntry::class.java) where(FavoriteEntry::class.java)
.equalTo(FavoriteEntry::gid.name, entry.gid) .equalTo(FavoriteEntry::gid.name, entry.gid)
.equalTo(FavoriteEntry::token.name, entry.token) .equalTo(FavoriteEntry::token.name, entry.token)
.equalTo(FavoriteEntry::category.name, entry.category) .equalTo(FavoriteEntry::category.name, entry.category)
.findFirst() .findFirst()
private fun queryListForEntry(list: List<FavoriteEntry>, entry: FavoriteEntry) private fun queryListForEntry(list: List<FavoriteEntry>, entry: FavoriteEntry) =
= list.find { list.find {
it.gid == entry.gid it.gid == entry.gid &&
&& it.token == entry.token it.token == entry.token &&
&& it.category == entry.category it.category == entry.category
} }
private fun loadDbCategories(manga: Sequence<Manga>): Sequence<Pair<Int, Manga>> { private fun loadDbCategories(manga: Sequence<Manga>): Sequence<Pair<Int, Manga>> {
@ -105,8 +105,8 @@ class LocalFavoritesStorage {
} }
} }
private fun parseToFavoriteEntries(manga: Sequence<Pair<Int, Manga>>) private fun parseToFavoriteEntries(manga: Sequence<Pair<Int, Manga>>) =
= manga.filter { manga.filter {
validateDbManga(it.second) validateDbManga(it.second)
}.mapNotNull { }.mapNotNull {
FavoriteEntry().apply { FavoriteEntry().apply {
@ -120,13 +120,15 @@ class LocalFavoritesStorage {
} }
} }
private fun validateDbManga(manga: Manga) private fun validateDbManga(manga: Manga) =
= manga.favorite && (manga.source == EH_SOURCE_ID || manga.source == EXH_SOURCE_ID) manga.favorite && (manga.source == EH_SOURCE_ID || manga.source == EXH_SOURCE_ID)
companion object { companion object {
const val MAX_CATEGORIES = 9 const val MAX_CATEGORIES = 9
} }
} }
data class ChangeSet(val added: List<FavoriteEntry>, data class ChangeSet(
val removed: List<FavoriteEntry>) val added: List<FavoriteEntry>,
val removed: List<FavoriteEntry>
)

View File

@ -4,28 +4,32 @@ import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservable import eu.kanade.tachiyomi.network.asObservable
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
import exh.metadata.metadata.HitomiSearchMetadata.Companion.LTN_BASE_URL import exh.metadata.metadata.HitomiSearchMetadata.Companion.LTN_BASE_URL
import java.security.MessageDigest
import okhttp3.Headers import okhttp3.Headers
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import org.vepta.vdm.ByteCursor import org.vepta.vdm.ByteCursor
import rx.Observable import rx.Observable
import rx.Single import rx.Single
import java.security.MessageDigest
private typealias HashedTerm = ByteArray private typealias HashedTerm = ByteArray
private data class DataPair(val offset: Long, val length: Int) private data class DataPair(val offset: Long, val length: Int)
private data class Node(val keys: List<ByteArray>, private data class Node(
val keys: List<ByteArray>,
val datas: List<DataPair>, val datas: List<DataPair>,
val subnodeAddresses: List<Long>) val subnodeAddresses: List<Long>
)
/** /**
* Kotlin port of the hitomi.la search algorithm * Kotlin port of the hitomi.la search algorithm
* @author NerdNumber9 * @author NerdNumber9
*/ */
class HitomiNozomi(private val client: OkHttpClient, class HitomiNozomi(
private val client: OkHttpClient,
private val tagIndexVersion: Long, private val tagIndexVersion: Long,
private val galleriesIndexVersion: Long) { private val galleriesIndexVersion: Long
) {
fun getGalleryIdsForQuery(query: String): Single<List<Int>> { fun getGalleryIdsForQuery(query: String): Single<List<Int>> {
val replacedQuery = query.replace('_', ' ') val replacedQuery = query.replace('_', ' ')
@ -90,9 +94,9 @@ class HitomiNozomi(private val client: OkHttpClient,
val expectedLength = numberOfGalleryIds * 4 + 4 val expectedLength = numberOfGalleryIds * 4 + 4
if(numberOfGalleryIds > 10000000 if (numberOfGalleryIds > 10000000 ||
|| numberOfGalleryIds <= 0 numberOfGalleryIds <= 0 ||
|| inbuf.size != expectedLength) { inbuf.size != expectedLength) {
return@map emptyList<Int>() return@map emptyList<Int>()
} }
@ -234,7 +238,6 @@ class HitomiNozomi(private val client: OkHttpClient,
.build()) .build())
} }
fun getIndexVersion(httpClient: OkHttpClient, name: String): Observable<Long> { fun getIndexVersion(httpClient: OkHttpClient, name: String): Observable<Long> {
return httpClient.newCall(GET("$LTN_BASE_URL/$name/version?_=${System.currentTimeMillis()}")) return httpClient.newCall(GET("$LTN_BASE_URL/$name/version?_=${System.currentTimeMillis()}"))
.asObservableSuccess() .asObservableSuccess()

View File

@ -4,11 +4,11 @@ import android.content.Context
import android.text.Html import android.text.Html
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import com.ms_square.debugoverlay.DataObserver import com.ms_square.debugoverlay.DataObserver
import com.ms_square.debugoverlay.OverlayModule import com.ms_square.debugoverlay.OverlayModule
import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.BuildConfig
import android.widget.LinearLayout
import android.widget.TextView
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.data.preference.getOrDefault import eu.kanade.tachiyomi.data.preference.getOrDefault

View File

@ -1,7 +1,7 @@
package exh.metadata package exh.metadata
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.Locale
/** /**
* Metadata utils * Metadata utils
@ -35,7 +35,6 @@ fun parseHumanReadableByteCount(arg0: String): Double? {
return null return null
} }
fun String?.nullIfBlank(): String? = if (isNullOrBlank()) fun String?.nullIfBlank(): String? = if (isNullOrBlank())
null null
else else

View File

@ -4,12 +4,14 @@ import android.net.Uri
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.* import exh.metadata.EX_DATE_FORMAT
import exh.metadata.ONGOING_SUFFIX
import exh.metadata.humanReadableByteCount
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.plusAssign import exh.plusAssign
import java.util.Date
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.util.*
class EHentaiSearchMetadata : RaisedSearchMetadata() { class EHentaiSearchMetadata : RaisedSearchMetadata() {
var gId: String? var gId: String?
@ -114,8 +116,8 @@ class EHentaiSearchMetadata : RaisedSearchMetadata() {
const val EH_GENRE_NAMESPACE = "genre" const val EH_GENRE_NAMESPACE = "genre"
private const val EH_ARTIST_NAMESPACE = "artist" private const val EH_ARTIST_NAMESPACE = "artist"
private fun splitGalleryUrl(url: String) private fun splitGalleryUrl(url: String) =
= url.let { url.let {
// Only parse URL if is full URL // Only parse URL if is full URL
val pathSegments = if (it.startsWith("http")) val pathSegments = if (it.startsWith("http"))
Uri.parse(it).pathSegments Uri.parse(it).pathSegments
@ -129,10 +131,10 @@ class EHentaiSearchMetadata : RaisedSearchMetadata() {
fun galleryToken(url: String) = fun galleryToken(url: String) =
splitGalleryUrl(url)[2] splitGalleryUrl(url)[2]
fun normalizeUrl(url: String) fun normalizeUrl(url: String) =
= idAndTokenToUrl(galleryId(url), galleryToken(url)) idAndTokenToUrl(galleryId(url), galleryToken(url))
fun idAndTokenToUrl(id: String, token: String) fun idAndTokenToUrl(id: String, token: String) =
= "/g/$id/$token/?nw=always" "/g/$id/$token/?nw=always"
} }
} }

View File

@ -34,7 +34,6 @@ class EightMusesSearchMetadata : RaisedSearchMetadata() {
manga.description = listOf(titleDesc.toString(), tagsDesc.toString()) manga.description = listOf(titleDesc.toString(), tagsDesc.toString())
.filter(String::isNotBlank) .filter(String::isNotBlank)
.joinToString(separator = "\n") .joinToString(separator = "\n")
} }
companion object { companion object {

View File

@ -49,7 +49,7 @@ class HentaiCafeSearchMetadata : RaisedSearchMetadata() {
const val BASE_URL = "https://hentai.cafe" const val BASE_URL = "https://hentai.cafe"
fun hcIdFromUrl(url: String) fun hcIdFromUrl(url: String) =
= url.split("/").last { it.isNotBlank() } url.split("/").last { it.isNotBlank() }
} }
} }

View File

@ -4,7 +4,7 @@ import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.EX_DATE_FORMAT import exh.metadata.EX_DATE_FORMAT
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.plusAssign import exh.plusAssign
import java.util.* import java.util.Date
class HitomiSearchMetadata : RaisedSearchMetadata() { class HitomiSearchMetadata : RaisedSearchMetadata() {
var url get() = hlId?.let { urlFromHlId(it) } var url get() = hlId?.let { urlFromHlId(it) }
@ -92,10 +92,10 @@ class HitomiSearchMetadata: RaisedSearchMetadata() {
const val LTN_BASE_URL = "https://ltn.hitomi.la" const val LTN_BASE_URL = "https://ltn.hitomi.la"
const val BASE_URL = "https://hitomi.la" const val BASE_URL = "https://hitomi.la"
fun hlIdFromUrl(url: String) fun hlIdFromUrl(url: String) =
= url.split('/').last().split('-').last().substringBeforeLast('.') url.split('/').last().split('-').last().substringBeforeLast('.')
fun urlFromHlId(id: String) fun urlFromHlId(id: String) =
= "$BASE_URL/galleries/$id.html" "$BASE_URL/galleries/$id.html"
} }
} }

View File

@ -3,12 +3,14 @@ package exh.metadata.metadata
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.* import exh.metadata.EX_DATE_FORMAT
import exh.metadata.ONGOING_SUFFIX
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.metadata.nullIfBlank
import exh.plusAssign import exh.plusAssign
import java.util.Date
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.util.*
class NHentaiSearchMetadata : RaisedSearchMetadata() { class NHentaiSearchMetadata : RaisedSearchMetadata() {
var url get() = nhId?.let { BASE_URL + nhIdToPath(it) } var url get() = nhId?.let { BASE_URL + nhIdToPath(it) }
@ -112,8 +114,8 @@ class NHentaiSearchMetadata : RaisedSearchMetadata() {
else -> null else -> null
} }
fun nhUrlToId(url: String) fun nhUrlToId(url: String) =
= url.split("/").last { it.isNotBlank() }.toLong() url.split("/").last { it.isNotBlank() }.toLong()
fun nhIdToPath(id: Long) = "/g/$id/" fun nhIdToPath(id: Long) = "/g/$id/"
} }

View File

@ -80,15 +80,14 @@ class PervEdenSearchMetadata : RaisedSearchMetadata() {
.joinToString(separator = "\n") .joinToString(separator = "\n")
} }
companion object { companion object {
private const val TITLE_TYPE_MAIN = 0 private const val TITLE_TYPE_MAIN = 0
private const val TITLE_TYPE_ALT = 1 private const val TITLE_TYPE_ALT = 1
const val TAG_TYPE_DEFAULT = 0 const val TAG_TYPE_DEFAULT = 0
private fun splitGalleryUrl(url: String) private fun splitGalleryUrl(url: String) =
= url.let { url.let {
Uri.parse(it).pathSegments.filterNot(String::isNullOrBlank) Uri.parse(it).pathSegments.filterNot(String::isNullOrBlank)
} }
@ -102,8 +101,8 @@ enum class PervEdenLang(val id: Long) {
it(PERV_EDEN_IT_SOURCE_ID); it(PERV_EDEN_IT_SOURCE_ID);
companion object { companion object {
fun source(id: Long) fun source(id: Long) =
= values().find { it.id == id } values().find { it.id == id }
?: throw IllegalArgumentException("Unknown source ID: $id!") ?: throw IllegalArgumentException("Unknown source ID: $id!")
} }
} }

View File

@ -5,7 +5,7 @@ import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.EX_DATE_FORMAT import exh.metadata.EX_DATE_FORMAT
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.plusAssign import exh.plusAssign
import java.util.* import java.util.Date
class TsuminoSearchMetadata : RaisedSearchMetadata() { class TsuminoSearchMetadata : RaisedSearchMetadata() {
var tmId: Int? = null var tmId: Int? = null
@ -76,8 +76,8 @@ class TsuminoSearchMetadata : RaisedSearchMetadata() {
val BASE_URL = "https://www.tsumino.com" val BASE_URL = "https://www.tsumino.com"
fun tmIdFromUrl(url: String) fun tmIdFromUrl(url: String) =
= Uri.parse(url).lastPathSegment Uri.parse(url).lastPathSegment
fun mangaUrlFromId(id: String) = "/Book/Info/$id" fun mangaUrlFromId(id: String) = "/Book/Info/$id"

View File

@ -5,9 +5,9 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper
import exh.metadata.sql.models.SearchMetadata import exh.metadata.sql.models.SearchMetadata
import exh.metadata.sql.models.SearchTag import exh.metadata.sql.models.SearchTag
import exh.metadata.sql.models.SearchTitle import exh.metadata.sql.models.SearchTitle
import kotlin.reflect.KClass
import rx.Completable import rx.Completable
import rx.Single import rx.Single
import kotlin.reflect.KClass
data class FlatMetadata( data class FlatMetadata(
val metadata: SearchMetadata, val metadata: SearchMetadata,
@ -16,8 +16,8 @@ data class FlatMetadata(
) { ) {
inline fun <reified T : RaisedSearchMetadata> raise(): T = raise(T::class) inline fun <reified T : RaisedSearchMetadata> raise(): T = raise(T::class)
fun <T : RaisedSearchMetadata> raise(clazz: KClass<T>) fun <T : RaisedSearchMetadata> raise(clazz: KClass<T>) =
= RaisedSearchMetadata.raiseFlattenGson RaisedSearchMetadata.raiseFlattenGson
.fromJson(metadata.extra, clazz.java).apply { .fromJson(metadata.extra, clazz.java).apply {
fillBaseFields(this@FlatMetadata) fillBaseFields(this@FlatMetadata)
} }

View File

@ -35,12 +35,12 @@ abstract class RaisedSearchMetadata {
abstract fun copyTo(manga: SManga) abstract fun copyTo(manga: SManga)
fun tagsToGenreString() fun tagsToGenreString() =
= tags.filter { it.type != TAG_TYPE_VIRTUAL } tags.filter { it.type != TAG_TYPE_VIRTUAL }
.joinToString { (if (it.namespace != null) "${it.namespace}: " else "") + it.name } .joinToString { (if (it.namespace != null) "${it.namespace}: " else "") + it.name }
fun tagsToDescription() fun tagsToDescription() =
= StringBuilder("Tags:\n").apply { StringBuilder("Tags:\n").apply {
// BiConsumer only available in Java 8, don't bother calling forEach directly on 'tags' // BiConsumer only available in Java 8, don't bother calling forEach directly on 'tags'
val groupedTags = tags.filter { it.type != TAG_TYPE_VIRTUAL }.groupBy { val groupedTags = tags.filter { it.type != TAG_TYPE_VIRTUAL }.groupBy {
it.namespace it.namespace
@ -125,8 +125,8 @@ abstract class RaisedSearchMetadata {
* @param property the metadata for the property. * @param property the metadata for the property.
* @return the property value. * @return the property value.
*/ */
override fun getValue(thisRef: RaisedSearchMetadata, property: KProperty<*>) override fun getValue(thisRef: RaisedSearchMetadata, property: KProperty<*>) =
= thisRef.getTitleOfType(type) thisRef.getTitleOfType(type)
/** /**
* Sets the value of the property for the given object. * Sets the value of the property for the given object.
@ -134,8 +134,8 @@ abstract class RaisedSearchMetadata {
* @param property the metadata for the property. * @param property the metadata for the property.
* @param value the value to set. * @param value the value to set.
*/ */
override fun setValue(thisRef: RaisedSearchMetadata, property: KProperty<*>, value: String?) override fun setValue(thisRef: RaisedSearchMetadata, property: KProperty<*>, value: String?) =
= thisRef.replaceTitleOfType(type, value) thisRef.replaceTitleOfType(type, value)
} }
} }
} }

View File

@ -1,5 +1,7 @@
package exh.metadata.metadata.base package exh.metadata.metadata.base
data class RaisedTag(val namespace: String?, data class RaisedTag(
val namespace: String?,
val name: String, val name: String,
val type: Int) val type: Int
)

View File

@ -3,7 +3,6 @@ package exh.metadata.sql.queries
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import com.pushtorefresh.storio.sqlite.queries.Query import com.pushtorefresh.storio.sqlite.queries.Query
import eu.kanade.tachiyomi.data.database.DbProvider import eu.kanade.tachiyomi.data.database.DbProvider
import eu.kanade.tachiyomi.data.database.models.Manga
import exh.metadata.sql.models.SearchMetadata import exh.metadata.sql.models.SearchMetadata
import exh.metadata.sql.tables.SearchMetadataTable import exh.metadata.sql.tables.SearchMetadataTable

View File

@ -4,8 +4,6 @@ import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import com.pushtorefresh.storio.sqlite.queries.Query import com.pushtorefresh.storio.sqlite.queries.Query
import eu.kanade.tachiyomi.data.database.DbProvider import eu.kanade.tachiyomi.data.database.DbProvider
import eu.kanade.tachiyomi.data.database.inTransaction import eu.kanade.tachiyomi.data.database.inTransaction
import eu.kanade.tachiyomi.data.database.models.Manga
import exh.metadata.sql.models.SearchMetadata
import exh.metadata.sql.models.SearchTitle import exh.metadata.sql.models.SearchTitle
import exh.metadata.sql.tables.SearchTitleTable import exh.metadata.sql.tables.SearchTitleTable

View File

@ -1,4 +1,6 @@
package exh.search package exh.search
class Namespace(var namespace: String, class Namespace(
var tag: Text? = null) : QueryComponent() var namespace: String,
var tag: Text? = null
) : QueryComponent()

View File

@ -7,8 +7,10 @@ import exh.metadata.sql.tables.SearchTitleTable
class SearchEngine { class SearchEngine {
private val queryCache = mutableMapOf<String, List<QueryComponent>>() private val queryCache = mutableMapOf<String, List<QueryComponent>>()
fun textToSubQueries(namespace: String?, fun textToSubQueries(
component: Text?): Pair<String, List<String>>? { namespace: String?,
component: Text?
): Pair<String, List<String>>? {
val maybeLenientComponent = component?.let { val maybeLenientComponent = component?.let {
if (!it.exact) if (!it.exact)
it.asLenientTagQueries() it.asLenientTagQueries()
@ -97,7 +99,6 @@ class SearchEngine {
completeParams += pair.second completeParams += pair.second
} }
exclude.forEach { exclude.forEach {
wheres += """ wheres += """
(meta.${SearchMetadataTable.COL_MANGA_ID} NOT IN ${it.first}) (meta.${SearchMetadataTable.COL_MANGA_ID} NOT IN ${it.first})
@ -197,7 +198,6 @@ class SearchEngine {
return string.replace("\\", "\\\\") return string.replace("\\", "\\\\")
.replace("_", "\\_") .replace("_", "\\_")
.replace("%", "\\%") .replace("%", "\\%")
} }
} }
} }

View File

@ -8,13 +8,19 @@ import eu.kanade.tachiyomi.source.model.SManga
import exh.ui.smartsearch.SmartSearchPresenter import exh.ui.smartsearch.SmartSearchPresenter
import exh.util.await import exh.util.await
import info.debatty.java.stringsimilarity.NormalizedLevenshtein import info.debatty.java.stringsimilarity.NormalizedLevenshtein
import kotlinx.coroutines.* import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.supervisorScope
import rx.schedulers.Schedulers import rx.schedulers.Schedulers
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import kotlin.coroutines.CoroutineContext
class SmartSearchEngine(parentContext: CoroutineContext, class SmartSearchEngine(
val extraSearchParams: String? = null): CoroutineScope { parentContext: CoroutineContext,
val extraSearchParams: String? = null
) : CoroutineScope {
override val coroutineContext: CoroutineContext = parentContext + Job() + Dispatchers.Default override val coroutineContext: CoroutineContext = parentContext + Job() + Dispatchers.Default
private val db: DatabaseHelper by injectLazy() private val db: DatabaseHelper by injectLazy()

View File

@ -1,12 +1,15 @@
package exh.source package exh.source
import eu.kanade.tachiyomi.source.model.* import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import rx.Observable import rx.Observable
import java.lang.RuntimeException
abstract class DelegatedHttpSource(val delegate: HttpSource) : HttpSource() { abstract class DelegatedHttpSource(val delegate: HttpSource) : HttpSource() {
/** /**
@ -14,16 +17,16 @@ abstract class DelegatedHttpSource(val delegate: HttpSource): HttpSource() {
* *
* @param page the page number to retrieve. * @param page the page number to retrieve.
*/ */
override fun popularMangaRequest(page: Int) override fun popularMangaRequest(page: Int) =
= throw UnsupportedOperationException("Should never be called!") throw UnsupportedOperationException("Should never be called!")
/** /**
* Parses the response from the site and returns a [MangasPage] object. * Parses the response from the site and returns a [MangasPage] object.
* *
* @param response the response from the site. * @param response the response from the site.
*/ */
override fun popularMangaParse(response: Response) override fun popularMangaParse(response: Response) =
= throw UnsupportedOperationException("Should never be called!") throw UnsupportedOperationException("Should never be called!")
/** /**
* Returns the request for the search manga given the page. * Returns the request for the search manga given the page.
@ -32,64 +35,64 @@ abstract class DelegatedHttpSource(val delegate: HttpSource): HttpSource() {
* @param query the search query. * @param query the search query.
* @param filters the list of filters to apply. * @param filters the list of filters to apply.
*/ */
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) override fun searchMangaRequest(page: Int, query: String, filters: FilterList) =
= throw UnsupportedOperationException("Should never be called!") throw UnsupportedOperationException("Should never be called!")
/** /**
* Parses the response from the site and returns a [MangasPage] object. * Parses the response from the site and returns a [MangasPage] object.
* *
* @param response the response from the site. * @param response the response from the site.
*/ */
override fun searchMangaParse(response: Response) override fun searchMangaParse(response: Response) =
= throw UnsupportedOperationException("Should never be called!") throw UnsupportedOperationException("Should never be called!")
/** /**
* Returns the request for latest manga given the page. * Returns the request for latest manga given the page.
* *
* @param page the page number to retrieve. * @param page the page number to retrieve.
*/ */
override fun latestUpdatesRequest(page: Int) override fun latestUpdatesRequest(page: Int) =
= throw UnsupportedOperationException("Should never be called!") throw UnsupportedOperationException("Should never be called!")
/** /**
* Parses the response from the site and returns a [MangasPage] object. * Parses the response from the site and returns a [MangasPage] object.
* *
* @param response the response from the site. * @param response the response from the site.
*/ */
override fun latestUpdatesParse(response: Response) override fun latestUpdatesParse(response: Response) =
= throw UnsupportedOperationException("Should never be called!") throw UnsupportedOperationException("Should never be called!")
/** /**
* Parses the response from the site and returns the details of a manga. * Parses the response from the site and returns the details of a manga.
* *
* @param response the response from the site. * @param response the response from the site.
*/ */
override fun mangaDetailsParse(response: Response) override fun mangaDetailsParse(response: Response) =
= throw UnsupportedOperationException("Should never be called!") throw UnsupportedOperationException("Should never be called!")
/** /**
* Parses the response from the site and returns a list of chapters. * Parses the response from the site and returns a list of chapters.
* *
* @param response the response from the site. * @param response the response from the site.
*/ */
override fun chapterListParse(response: Response) override fun chapterListParse(response: Response) =
= throw UnsupportedOperationException("Should never be called!") throw UnsupportedOperationException("Should never be called!")
/** /**
* Parses the response from the site and returns a list of pages. * Parses the response from the site and returns a list of pages.
* *
* @param response the response from the site. * @param response the response from the site.
*/ */
override fun pageListParse(response: Response) override fun pageListParse(response: Response) =
= throw UnsupportedOperationException("Should never be called!") throw UnsupportedOperationException("Should never be called!")
/** /**
* Parses the response from the site and returns the absolute url to the source image. * Parses the response from the site and returns the absolute url to the source image.
* *
* @param response the response from the site. * @param response the response from the site.
*/ */
override fun imageUrlParse(response: Response) override fun imageUrlParse(response: Response) =
= throw UnsupportedOperationException("Should never be called!") throw UnsupportedOperationException("Should never be called!")
/** /**
* Base url of the website without the trailing slash, like: http://mysite.com * Base url of the website without the trailing slash, like: http://mysite.com
@ -236,8 +239,8 @@ abstract class DelegatedHttpSource(val delegate: HttpSource): HttpSource() {
override fun getFilterList() = delegate.getFilterList() override fun getFilterList() = delegate.getFilterList()
private fun ensureDelegateCompatible() { private fun ensureDelegateCompatible() {
if(versionId != delegate.versionId if (versionId != delegate.versionId ||
|| lang != delegate.lang) { lang != delegate.lang) {
throw IncompatibleDelegateException("Delegate source is not compatible (versionId: $versionId <=> ${delegate.versionId}, lang: $lang <=> ${delegate.lang})!") throw IncompatibleDelegateException("Delegate source is not compatible (versionId: $versionId <=> ${delegate.versionId}, lang: $lang <=> ${delegate.lang})!")
} }
} }

View File

@ -2,13 +2,19 @@ package exh.source
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.source.model.* import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import okhttp3.Response import okhttp3.Response
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
class EnhancedHttpSource(val originalSource: HttpSource, class EnhancedHttpSource(
val enchancedSource: HttpSource): HttpSource() { val originalSource: HttpSource,
val enchancedSource: HttpSource
) : HttpSource() {
private val prefs: PreferencesHelper by injectLazy() private val prefs: PreferencesHelper by injectLazy()
/** /**
@ -16,16 +22,16 @@ class EnhancedHttpSource(val originalSource: HttpSource,
* *
* @param page the page number to retrieve. * @param page the page number to retrieve.
*/ */
override fun popularMangaRequest(page: Int) override fun popularMangaRequest(page: Int) =
= throw UnsupportedOperationException("Should never be called!") throw UnsupportedOperationException("Should never be called!")
/** /**
* Parses the response from the site and returns a [MangasPage] object. * Parses the response from the site and returns a [MangasPage] object.
* *
* @param response the response from the site. * @param response the response from the site.
*/ */
override fun popularMangaParse(response: Response) override fun popularMangaParse(response: Response) =
= throw UnsupportedOperationException("Should never be called!") throw UnsupportedOperationException("Should never be called!")
/** /**
* Returns the request for the search manga given the page. * Returns the request for the search manga given the page.
@ -34,64 +40,64 @@ class EnhancedHttpSource(val originalSource: HttpSource,
* @param query the search query. * @param query the search query.
* @param filters the list of filters to apply. * @param filters the list of filters to apply.
*/ */
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) override fun searchMangaRequest(page: Int, query: String, filters: FilterList) =
= throw UnsupportedOperationException("Should never be called!") throw UnsupportedOperationException("Should never be called!")
/** /**
* Parses the response from the site and returns a [MangasPage] object. * Parses the response from the site and returns a [MangasPage] object.
* *
* @param response the response from the site. * @param response the response from the site.
*/ */
override fun searchMangaParse(response: Response) override fun searchMangaParse(response: Response) =
= throw UnsupportedOperationException("Should never be called!") throw UnsupportedOperationException("Should never be called!")
/** /**
* Returns the request for latest manga given the page. * Returns the request for latest manga given the page.
* *
* @param page the page number to retrieve. * @param page the page number to retrieve.
*/ */
override fun latestUpdatesRequest(page: Int) override fun latestUpdatesRequest(page: Int) =
= throw UnsupportedOperationException("Should never be called!") throw UnsupportedOperationException("Should never be called!")
/** /**
* Parses the response from the site and returns a [MangasPage] object. * Parses the response from the site and returns a [MangasPage] object.
* *
* @param response the response from the site. * @param response the response from the site.
*/ */
override fun latestUpdatesParse(response: Response) override fun latestUpdatesParse(response: Response) =
= throw UnsupportedOperationException("Should never be called!") throw UnsupportedOperationException("Should never be called!")
/** /**
* Parses the response from the site and returns the details of a manga. * Parses the response from the site and returns the details of a manga.
* *
* @param response the response from the site. * @param response the response from the site.
*/ */
override fun mangaDetailsParse(response: Response) override fun mangaDetailsParse(response: Response) =
= throw UnsupportedOperationException("Should never be called!") throw UnsupportedOperationException("Should never be called!")
/** /**
* Parses the response from the site and returns a list of chapters. * Parses the response from the site and returns a list of chapters.
* *
* @param response the response from the site. * @param response the response from the site.
*/ */
override fun chapterListParse(response: Response) override fun chapterListParse(response: Response) =
= throw UnsupportedOperationException("Should never be called!") throw UnsupportedOperationException("Should never be called!")
/** /**
* Parses the response from the site and returns a list of pages. * Parses the response from the site and returns a list of pages.
* *
* @param response the response from the site. * @param response the response from the site.
*/ */
override fun pageListParse(response: Response) override fun pageListParse(response: Response) =
= throw UnsupportedOperationException("Should never be called!") throw UnsupportedOperationException("Should never be called!")
/** /**
* Parses the response from the site and returns the absolute url to the source image. * Parses the response from the site and returns the absolute url to the source image.
* *
* @param response the response from the site. * @param response the response from the site.
*/ */
override fun imageUrlParse(response: Response) override fun imageUrlParse(response: Response) =
= throw UnsupportedOperationException("Should never be called!") throw UnsupportedOperationException("Should never be called!")
/** /**
* Base url of the website without the trailing slash, like: http://mysite.com * Base url of the website without the trailing slash, like: http://mysite.com
@ -146,8 +152,8 @@ class EnhancedHttpSource(val originalSource: HttpSource,
* @param query the search query. * @param query the search query.
* @param filters the list of filters to apply. * @param filters the list of filters to apply.
*/ */
override fun fetchSearchManga(page: Int, query: String, filters: FilterList) override fun fetchSearchManga(page: Int, query: String, filters: FilterList) =
= source().fetchSearchManga(page, query, filters) source().fetchSearchManga(page, query, filters)
/** /**
* Returns an observable containing a page with a list of latest manga updates. * Returns an observable containing a page with a list of latest manga updates.
@ -202,8 +208,8 @@ class EnhancedHttpSource(val originalSource: HttpSource,
* @param chapter the chapter to be added. * @param chapter the chapter to be added.
* @param manga the manga of the chapter. * @param manga the manga of the chapter.
*/ */
override fun prepareNewChapter(chapter: SChapter, manga: SManga) override fun prepareNewChapter(chapter: SChapter, manga: SManga) =
= source().prepareNewChapter(chapter, manga) source().prepareNewChapter(chapter, manga)
/** /**
* Returns the list of filters for the source. * Returns the list of filters for the source.

View File

@ -7,8 +7,8 @@ import com.afollestad.materialdialogs.MaterialDialog
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 timber.log.Timber
import kotlin.concurrent.thread import kotlin.concurrent.thread
import timber.log.Timber
class ConfiguringDialogController : DialogController() { class ConfiguringDialogController : DialogController() {
private var materialDialog: MaterialDialog? = null private var materialDialog: MaterialDialog? = null
@ -62,4 +62,3 @@ class ConfiguringDialogController : DialogController() {
router.popController(this) router.popController(this)
} }
} }

View File

@ -24,11 +24,13 @@ class EHConfigurator {
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(action: String, private fun EHentai.execProfileActions(
action: String,
name: String, name: String,
set: String, set: String,
sp: Int) sp: Int
= configuratorClient.newCall(requestWithCreds(sp) ) =
configuratorClient.newCall(requestWithCreds(sp)
.url(uconfigUrl) .url(uconfigUrl)
.post(FormBody.Builder() .post(FormBody.Builder()
.add("profile_action", action) .add("profile_action", action)

View File

@ -9,7 +9,6 @@ class EHHathPerksResponse {
var pagingEnlargementII = false var pagingEnlargementII = false
var pagingEnlargementIII = false var pagingEnlargementIII = false
override fun toString() override fun toString() =
= "EHHathPerksResponse(moreThumbs=$moreThumbs, thumbsUp=$thumbsUp, allThumbs=$allThumbs, pagingEnlargementI=$pagingEnlargementI, pagingEnlargementII=$pagingEnlargementII, pagingEnlargementIII=$pagingEnlargementIII)" "EHHathPerksResponse(moreThumbs=$moreThumbs, thumbsUp=$thumbsUp, allThumbs=$allThumbs, pagingEnlargementI=$pagingEnlargementI, pagingEnlargementII=$pagingEnlargementII, pagingEnlargementIII=$pagingEnlargementIII)"
} }

View File

@ -1,11 +1,11 @@
package exh.ui package exh.ui
import java.util.UUID
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.*
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
typealias LoadingHandle = String typealias LoadingHandle = String

View File

@ -6,11 +6,11 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.annotation.LayoutRes import androidx.annotation.LayoutRes
import eu.kanade.tachiyomi.ui.base.controller.BaseController import eu.kanade.tachiyomi.ui.base.controller.BaseController
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlin.coroutines.CoroutineContext
abstract class BaseExhController(bundle: Bundle? = null) : BaseController(bundle), CoroutineScope { abstract class BaseExhController(bundle: Bundle? = null) : BaseController(bundle), CoroutineScope {
abstract val layoutId: Int abstract val layoutId: Int

View File

@ -1,6 +1,5 @@
package exh.ui.batchadd package exh.ui.batchadd
import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup

View File

@ -7,16 +7,18 @@ import android.webkit.WebView
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import eu.kanade.tachiyomi.util.system.asJsoup import eu.kanade.tachiyomi.util.system.asJsoup
import exh.ui.captcha.BrowserActionActivity.Companion.CROSS_WINDOW_SCRIPT_INNER import exh.ui.captcha.BrowserActionActivity.Companion.CROSS_WINDOW_SCRIPT_INNER
import java.nio.charset.Charset
import org.jsoup.nodes.DataNode import org.jsoup.nodes.DataNode
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import java.nio.charset.Charset
@RequiresApi(Build.VERSION_CODES.LOLLIPOP) @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
class AutoSolvingWebViewClient(activity: BrowserActionActivity, class AutoSolvingWebViewClient(
activity: BrowserActionActivity,
verifyComplete: (String) -> Boolean, verifyComplete: (String) -> Boolean,
injectScript: String?, injectScript: String?,
headers: Map<String, String>) headers: Map<String, String>
: HeadersInjectingWebViewClient(activity, verifyComplete, injectScript, headers) { ) :
HeadersInjectingWebViewClient(activity, verifyComplete, injectScript, headers) {
override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? { override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? {
// Inject our custom script into the recaptcha iframes // Inject our custom script into the recaptcha iframes

View File

@ -4,9 +4,11 @@ import android.os.Build
import android.webkit.WebView import android.webkit.WebView
import android.webkit.WebViewClient import android.webkit.WebViewClient
open class BasicWebViewClient(protected val activity: BrowserActionActivity, open class BasicWebViewClient(
protected val activity: BrowserActionActivity,
protected val verifyComplete: (String) -> Boolean, protected val verifyComplete: (String) -> Boolean,
private val injectScript: String?) : WebViewClient() { private val injectScript: String?
) : WebViewClient() {
override fun onPageFinished(view: WebView, url: String) { override fun onPageFinished(view: WebView, url: String) {
super.onPageFinished(view, url) super.onPageFinished(view, url)

View File

@ -6,7 +6,12 @@ import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.SystemClock import android.os.SystemClock
import android.view.MotionEvent import android.view.MotionEvent
import android.webkit.* import android.webkit.CookieManager
import android.webkit.CookieSyncManager
import android.webkit.JavascriptInterface
import android.webkit.JsResult
import android.webkit.WebChromeClient
import android.webkit.WebView
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
@ -22,6 +27,9 @@ import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import exh.source.DelegatedHttpSource import exh.source.DelegatedHttpSource
import exh.util.melt import exh.util.melt
import java.io.Serializable
import java.net.URL
import java.util.UUID
import kotlinx.android.synthetic.main.eh_activity_captcha.* import kotlinx.android.synthetic.main.eh_activity_captcha.*
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
@ -33,10 +41,6 @@ import rx.Single
import rx.schedulers.Schedulers import rx.schedulers.Schedulers
import timber.log.Timber import timber.log.Timber
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.io.Serializable
import java.net.URL
import java.util.*
import kotlin.collections.HashMap
class BrowserActionActivity : AppCompatActivity() { class BrowserActionActivity : AppCompatActivity() {
private val sourceManager: SourceManager by injectLazy() private val sourceManager: SourceManager by injectLazy()
@ -72,8 +76,8 @@ class BrowserActionActivity : AppCompatActivity() {
it.value.joinToString(",") it.value.joinToString(",")
} ?: emptyMap()) + (intent.getSerializableExtra(HEADERS_EXTRA) as? HashMap<String, String> ?: emptyMap()) } ?: emptyMap()) + (intent.getSerializableExtra(HEADERS_EXTRA) as? HashMap<String, String> ?: emptyMap())
val cookies: HashMap<String, String>? val cookies: HashMap<String, String>? =
= intent.getSerializableExtra(COOKIES_EXTRA) as? HashMap<String, String> intent.getSerializableExtra(COOKIES_EXTRA) as? HashMap<String, String>
val script: String? = intent.getStringExtra(SCRIPT_EXTRA) val script: String? = intent.getStringExtra(SCRIPT_EXTRA)
val url: String? = intent.getStringExtra(URL_EXTRA) val url: String? = intent.getStringExtra(URL_EXTRA)
val actionName = intent.getStringExtra(ACTION_NAME_EXTRA) val actionName = intent.getStringExtra(ACTION_NAME_EXTRA)
@ -120,8 +124,8 @@ class BrowserActionActivity : AppCompatActivity() {
// Wait for both inner scripts to be loaded // Wait for both inner scripts to be loaded
if (loadedInners >= 2) { if (loadedInners >= 2) {
// Attempt to autosolve captcha // Attempt to autosolve captcha
if(preferencesHelper.eh_autoSolveCaptchas().getOrDefault() if (preferencesHelper.eh_autoSolveCaptchas().getOrDefault() &&
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
webview.post { webview.post {
// 10 seconds to auto-solve captcha // 10 seconds to auto-solve captcha
strictValidationStartTime = System.currentTimeMillis() + 1000 * 10 strictValidationStartTime = System.currentTimeMillis() + 1000 * 10
@ -415,7 +419,6 @@ class BrowserActionActivity : AppCompatActivity() {
doStageCheckbox(loopId) doStageCheckbox(loopId)
} }
@RequiresApi(Build.VERSION_CODES.LOLLIPOP) @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
@JavascriptInterface @JavascriptInterface
fun validateCaptchaCallback(result: Boolean, loopId: String) { fun validateCaptchaCallback(result: Boolean, loopId: String) {
@ -434,8 +437,8 @@ class BrowserActionActivity : AppCompatActivity() {
} }
} else { } else {
val savedStrictValidationStartTime = strictValidationStartTime val savedStrictValidationStartTime = strictValidationStartTime
if(savedStrictValidationStartTime != null if (savedStrictValidationStartTime != null &&
&& System.currentTimeMillis() > savedStrictValidationStartTime) { System.currentTimeMillis() > savedStrictValidationStartTime) {
captchaSolveFail() captchaSolveFail()
} else { } else {
webview.postDelayed({ webview.postDelayed({
@ -624,12 +627,14 @@ class BrowserActionActivity : AppCompatActivity() {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
} }
fun launchCaptcha(context: Context, fun launchCaptcha(
context: Context,
source: ActionCompletionVerifier, source: ActionCompletionVerifier,
cookies: Map<String, String>, cookies: Map<String, String>,
script: String?, script: String?,
url: String, url: String,
autoSolveSubmitBtnSelector: String? = null) { autoSolveSubmitBtnSelector: String? = null
) {
val intent = baseIntent(context).apply { val intent = baseIntent(context).apply {
putExtra(SOURCE_ID_EXTRA, source.id) putExtra(SOURCE_ID_EXTRA, source.id)
putExtra(COOKIES_EXTRA, HashMap(cookies)) putExtra(COOKIES_EXTRA, HashMap(cookies))
@ -641,9 +646,11 @@ class BrowserActionActivity : AppCompatActivity() {
context.startActivity(intent) context.startActivity(intent)
} }
fun launchUniversal(context: Context, fun launchUniversal(
context: Context,
source: HttpSource, source: HttpSource,
url: String) { url: String
) {
val intent = baseIntent(context).apply { val intent = baseIntent(context).apply {
putExtra(SOURCE_ID_EXTRA, source.id) putExtra(SOURCE_ID_EXTRA, source.id)
putExtra(URL_EXTRA, url) putExtra(URL_EXTRA, url)
@ -652,9 +659,11 @@ class BrowserActionActivity : AppCompatActivity() {
context.startActivity(intent) context.startActivity(intent)
} }
fun launchUniversal(context: Context, fun launchUniversal(
context: Context,
sourceId: Long, sourceId: Long,
url: String) { url: String
) {
val intent = baseIntent(context).apply { val intent = baseIntent(context).apply {
putExtra(SOURCE_ID_EXTRA, sourceId) putExtra(SOURCE_ID_EXTRA, sourceId)
putExtra(URL_EXTRA, url) putExtra(URL_EXTRA, url)
@ -663,11 +672,13 @@ class BrowserActionActivity : AppCompatActivity() {
context.startActivity(intent) context.startActivity(intent)
} }
fun launchAction(context: Context, fun launchAction(
context: Context,
completionVerifier: ActionCompletionVerifier, completionVerifier: ActionCompletionVerifier,
script: String?, script: String?,
url: String, url: String,
actionName: String) { actionName: String
) {
val intent = baseIntent(context).apply { val intent = baseIntent(context).apply {
putExtra(SOURCE_ID_EXTRA, completionVerifier.id) putExtra(SOURCE_ID_EXTRA, completionVerifier.id)
putExtra(SCRIPT_EXTRA, script) putExtra(SCRIPT_EXTRA, script)
@ -678,12 +689,14 @@ class BrowserActionActivity : AppCompatActivity() {
context.startActivity(intent) context.startActivity(intent)
} }
fun launchAction(context: Context, fun launchAction(
context: Context,
completionVerifier: (String) -> Boolean, completionVerifier: (String) -> Boolean,
script: String?, script: String?,
url: String, url: String,
actionName: String, actionName: String,
headers: Map<String, String>? = emptyMap()) { headers: Map<String, String>? = emptyMap()
) {
val intent = baseIntent(context).apply { val intent = baseIntent(context).apply {
putExtra(HEADERS_EXTRA, HashMap(headers)) putExtra(HEADERS_EXTRA, HashMap(headers))
putExtra(VERIFY_LAMBDA_EXTRA, completionVerifier as Serializable) putExtra(VERIFY_LAMBDA_EXTRA, completionVerifier as Serializable)
@ -708,4 +721,3 @@ class NoopActionCompletionVerifier(private val source: HttpSource): DelegatedHtt
interface ActionCompletionVerifier : Source { interface ActionCompletionVerifier : Source {
fun verifyComplete(url: String): Boolean fun verifyComplete(url: String): Boolean
} }

View File

@ -7,11 +7,13 @@ import android.webkit.WebView
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
@RequiresApi(Build.VERSION_CODES.LOLLIPOP) @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
open class HeadersInjectingWebViewClient(activity: BrowserActionActivity, open class HeadersInjectingWebViewClient(
activity: BrowserActionActivity,
verifyComplete: (String) -> Boolean, verifyComplete: (String) -> Boolean,
injectScript: String?, injectScript: String?,
private val headers: Map<String, String>) private val headers: Map<String, String>
: BasicWebViewClient(activity, verifyComplete, injectScript) { ) :
BasicWebViewClient(activity, verifyComplete, injectScript) {
override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? { override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? {
// Temp disabled as it's unreliable // Temp disabled as it's unreliable

View File

@ -3,8 +3,8 @@ package exh.ui.intercept
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import exh.GalleryAddEvent import exh.GalleryAddEvent
import exh.GalleryAdder import exh.GalleryAdder
import rx.subjects.BehaviorSubject
import kotlin.concurrent.thread import kotlin.concurrent.thread
import rx.subjects.BehaviorSubject
class InterceptActivityPresenter : BasePresenter<InterceptActivity>() { class InterceptActivityPresenter : BasePresenter<InterceptActivity>() {
private val galleryAdder = GalleryAdder() private val galleryAdder = GalleryAdder()

View File

@ -28,13 +28,13 @@ class FingerLockPreference @JvmOverloads constructor(context: Context, attrs: At
val prefs: PreferencesHelper by injectLazy() val prefs: PreferencesHelper by injectLazy()
val fingerprintSupported val fingerprintSupported
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
&& Reprint.isHardwarePresent() Reprint.isHardwarePresent() &&
&& Reprint.hasFingerprintRegistered() Reprint.hasFingerprintRegistered()
val useFingerprint val useFingerprint
get() = fingerprintSupported get() = fingerprintSupported &&
&& prefs.eh_lockUseFingerprint().getOrDefault() prefs.eh_lockUseFingerprint().getOrDefault()
@SuppressLint("NewApi") @SuppressLint("NewApi")
override fun onAttached() { override fun onAttached() {

View File

@ -1,6 +1,5 @@
package exh.ui.lock package exh.ui.lock
import android.content.Intent
import android.view.WindowManager import android.view.WindowManager
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import com.bluelinelabs.conductor.Router import com.bluelinelabs.conductor.Router
@ -8,7 +7,6 @@ import com.bluelinelabs.conductor.RouterTransaction
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.data.preference.getOrDefault
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.util.Date
object LockActivityDelegate { object LockActivityDelegate {
private val preferences by injectLazy<PreferencesHelper>() private val preferences by injectLazy<PreferencesHelper>()
@ -20,7 +18,6 @@ object LockActivityDelegate {
.popChangeHandler(LockChangeHandler(animate))) .popChangeHandler(LockChangeHandler(animate)))
} }
fun onCreate(activity: FragmentActivity) { fun onCreate(activity: FragmentActivity) {
preferences.secureScreen().asObservable() preferences.secureScreen().asObservable()
.subscribe { .subscribe {
@ -42,5 +39,4 @@ object LockActivityDelegate {
private fun isAppLocked(router: Router): Boolean { private fun isAppLocked(router: Router): Boolean {
return router.backstack.lastOrNull()?.controller() is LockController return router.backstack.lastOrNull()?.controller() is LockController
} }
} }

View File

@ -7,7 +7,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.ControllerChangeHandler
import com.bluelinelabs.conductor.changehandler.AnimatorChangeHandler import com.bluelinelabs.conductor.changehandler.AnimatorChangeHandler
import java.util.* import java.util.ArrayList
class LockChangeHandler : AnimatorChangeHandler { class LockChangeHandler : AnimatorChangeHandler {
constructor() : super() constructor() : super()
@ -36,6 +36,4 @@ class LockChangeHandler : AnimatorChangeHandler {
override fun copy(): ControllerChangeHandler = override fun copy(): ControllerChangeHandler =
LockChangeHandler(animationDuration, removesFromViewOnPush()) LockChangeHandler(animationDuration, removesFromViewOnPush())
} }

View File

@ -22,8 +22,8 @@ class LockController : NucleusController<LockPresenter>() {
val prefs: PreferencesHelper by injectLazy() val prefs: PreferencesHelper by injectLazy()
override fun inflateView(inflater: LayoutInflater, container: ViewGroup) override fun inflateView(inflater: LayoutInflater, container: ViewGroup) =
= inflater.inflate(R.layout.activity_lock, container, false)!! inflater.inflate(R.layout.activity_lock, container, false)!!
override fun createPresenter() = LockPresenter() override fun createPresenter() = LockPresenter()

View File

@ -8,12 +8,12 @@ import com.afollestad.materialdialogs.MaterialDialog
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.util.preference.onChange import eu.kanade.tachiyomi.util.preference.onChange
import java.math.BigInteger
import java.security.SecureRandom
import rx.Observable import rx.Observable
import rx.android.schedulers.AndroidSchedulers import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers import rx.schedulers.Schedulers
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.math.BigInteger
import java.security.SecureRandom
class LockPreference @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : class LockPreference @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
SwitchPreferenceCompat(context, attrs) { SwitchPreferenceCompat(context, attrs) {

View File

@ -11,9 +11,8 @@ class LockPresenter: BasePresenter<LockController>() {
val prefs: PreferencesHelper by injectLazy() val prefs: PreferencesHelper by injectLazy()
val useFingerprint val useFingerprint
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
&& Reprint.isHardwarePresent() Reprint.isHardwarePresent() &&
&& Reprint.hasFingerprintRegistered() Reprint.hasFingerprintRegistered() &&
&& prefs.eh_lockUseFingerprint().getOrDefault() prefs.eh_lockUseFingerprint().getOrDefault()
} }

View File

@ -13,11 +13,10 @@ 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.data.preference.getOrDefault import eu.kanade.tachiyomi.data.preference.getOrDefault
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.security.MessageDigest import java.security.MessageDigest
import kotlin.experimental.and import kotlin.experimental.and
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
/** /**
* Password hashing utils * Password hashing utils
@ -40,22 +39,24 @@ fun sha512(passwordToHash: String, salt: String): String {
/** /**
* Check if lock is enabled * Check if lock is enabled
*/ */
fun lockEnabled(prefs: PreferencesHelper = Injekt.get()) fun lockEnabled(prefs: PreferencesHelper = Injekt.get()) =
= prefs.eh_lockHash().get() != null prefs.eh_lockHash().get() != null &&
&& prefs.eh_lockSalt().get() != null prefs.eh_lockSalt().get() != null &&
&& prefs.eh_lockLength().getOrDefault() != -1 prefs.eh_lockLength().getOrDefault() != -1
/** /**
* Check if the lock will function properly * Check if the lock will function properly
* *
* @return true if action is required, false if lock is working properly * @return true if action is required, false if lock is working properly
*/ */
fun notifyLockSecurity(context: Context, fun notifyLockSecurity(
prefs: PreferencesHelper = Injekt.get()): Boolean { context: Context,
prefs: PreferencesHelper = Injekt.get()
): Boolean {
return false return false
if (!prefs.eh_lockManually().getOrDefault() if (!prefs.eh_lockManually().getOrDefault() &&
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
&& !hasAccessToUsageStats(context)) { !hasAccessToUsageStats(context)) {
MaterialDialog.Builder(context) MaterialDialog.Builder(context)
.title("Permission required") .title("Permission required")
.content("${context.getString(R.string.app_name)} requires the usage stats permission to detect when you leave the app. " + .content("${context.getString(R.string.app_name)} requires the usage stats permission to detect when you leave the app. " +

View File

@ -12,14 +12,14 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.ui.base.controller.NucleusController import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.util.view.gone
import eu.kanade.tachiyomi.util.lang.launchUI import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.view.gone
import eu.kanade.tachiyomi.util.view.visible import eu.kanade.tachiyomi.util.view.visible
import exh.uconfig.WarnConfigureDialogController import exh.uconfig.WarnConfigureDialogController
import java.net.HttpCookie
import kotlinx.android.synthetic.main.eh_activity_login.view.* import kotlinx.android.synthetic.main.eh_activity_login.view.*
import timber.log.Timber import timber.log.Timber
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.net.HttpCookie
/** /**
* LoginController * LoginController
@ -105,8 +105,8 @@ class LoginController : NucleusController<LoginPresenter>() {
val parsedUrl = Uri.parse(url) val parsedUrl = Uri.parse(url)
if (parsedUrl.host.equals("forums.e-hentai.org", ignoreCase = true)) { if (parsedUrl.host.equals("forums.e-hentai.org", ignoreCase = true)) {
// Hide distracting content // Hide distracting content
if(!parsedUrl.queryParameterNames.contains(PARAM_SKIP_INJECT) if (!parsedUrl.queryParameterNames.contains(PARAM_SKIP_INJECT) &&
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
view.evaluateJavascript(HIDE_JS, null) view.evaluateJavascript(HIDE_JS, null)
// Check login result // Check login result
@ -138,9 +138,9 @@ class LoginController : NucleusController<LoginPresenter>() {
fun checkLoginCookies(url: String): Boolean { fun checkLoginCookies(url: String): Boolean {
getCookies(url)?.let { parsed -> getCookies(url)?.let { parsed ->
return parsed.filter { return parsed.filter {
(it.name.equals(MEMBER_ID_COOKIE, ignoreCase = true) (it.name.equals(MEMBER_ID_COOKIE, ignoreCase = true) ||
|| it.name.equals(PASS_HASH_COOKIE, ignoreCase = true)) it.name.equals(PASS_HASH_COOKIE, ignoreCase = true)) &&
&& it.value.isNotBlank() it.value.isNotBlank()
}.count() >= 2 }.count() >= 2
} }
return false return false
@ -177,8 +177,8 @@ class LoginController : NucleusController<LoginPresenter>() {
return false return false
} }
fun getCookies(url: String): List<HttpCookie>? fun getCookies(url: String): List<HttpCookie>? =
= CookieManager.getInstance().getCookie(url)?.let { CookieManager.getInstance().getCookie(url)?.let {
it.split("; ").flatMap { it.split("; ").flatMap {
HttpCookie.parse(it) HttpCookie.parse(it)
} }

View File

@ -2,6 +2,4 @@ package exh.ui.login
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
class LoginPresenter: BasePresenter<LoginController>() { class LoginPresenter : BasePresenter<LoginController>()
}

View File

@ -12,9 +12,9 @@ import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import exh.EXH_SOURCE_ID import exh.EXH_SOURCE_ID
import exh.isLewdSource import exh.isLewdSource
import kotlin.concurrent.thread
import timber.log.Timber import timber.log.Timber
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import kotlin.concurrent.thread
class MetadataFetchDialog { class MetadataFetchDialog {
@ -132,7 +132,6 @@ class MetadataFetchDialog {
} }
} }
} }
} }
fun adviseMigrationLater(activity: Activity) { fun adviseMigrationLater(activity: Activity) {

View File

@ -4,8 +4,10 @@ import android.os.Bundle
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import exh.debug.DebugFunctions.sourceManager import exh.debug.DebugFunctions.sourceManager
class MigrationSourceAdapter(val items: List<MigrationSourceItem>, class MigrationSourceAdapter(
val controller: MigrationDesignController): FlexibleAdapter<MigrationSourceItem>( val items: List<MigrationSourceItem>,
val controller: MigrationDesignController
) : FlexibleAdapter<MigrationSourceItem>(
items, items,
controller, controller,
true true

View File

@ -1,12 +1,12 @@
package exh.ui.migration.manga.design package exh.ui.migration.manga.design
import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG
import android.view.View import android.view.View
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
import eu.kanade.tachiyomi.util.view.getRound import eu.kanade.tachiyomi.util.view.getRound
import kotlinx.android.synthetic.main.eh_source_item.* import kotlinx.android.synthetic.main.eh_source_item.*
import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG
class MigrationSourceHolder(view: View, val adapter: FlexibleAdapter<MigrationSourceItem>) : class MigrationSourceHolder(view: View, val adapter: FlexibleAdapter<MigrationSourceItem>) :
BaseFlexibleViewHolder(view, adapter) { BaseFlexibleViewHolder(view, adapter) {

View File

@ -25,10 +25,12 @@ class MigrationSourceItem(val source: HttpSource, var sourceEnabled: Boolean): A
* @param position The position of this item in the adapter. * @param position The position of this item in the adapter.
* @param payloads List of partial changes. * @param payloads List of partial changes.
*/ */
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<androidx.recyclerview.widget.RecyclerView.ViewHolder>>, override fun bindViewHolder(
adapter: FlexibleAdapter<IFlexible<androidx.recyclerview.widget.RecyclerView.ViewHolder>>,
holder: MigrationSourceHolder, holder: MigrationSourceHolder,
position: Int, position: Int,
payloads: List<Any?>?) { payloads: List<Any?>?
) {
holder.bind(source, sourceEnabled) holder.bind(source, sourceEnabled)
} }

View File

@ -6,14 +6,17 @@ import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import exh.util.DeferredField import exh.util.DeferredField
import exh.util.await import exh.util.await
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.ConflatedBroadcastChannel
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.channels.ConflatedBroadcastChannel
class MigratingManga(private val db: DatabaseHelper, class MigratingManga(
private val db: DatabaseHelper,
private val sourceManager: SourceManager, private val sourceManager: SourceManager,
val mangaId: Long, val mangaId: Long,
parentContext: CoroutineContext) { parentContext: CoroutineContext
) {
val searchResult = DeferredField<Long?>() val searchResult = DeferredField<Long?>()
// <MAX, PROGRESS> // <MAX, PROGRESS>

View File

@ -22,20 +22,27 @@ import eu.kanade.tachiyomi.util.view.inflate
import eu.kanade.tachiyomi.util.view.visible import eu.kanade.tachiyomi.util.view.visible
import exh.MERGED_SOURCE_ID import exh.MERGED_SOURCE_ID
import exh.util.await import exh.util.await
import kotlinx.android.synthetic.main.eh_manga_card.view.*
import kotlinx.android.synthetic.main.eh_migration_process_item.view.*
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.collect
import uy.kohesive.injekt.injectLazy
import java.text.DateFormat import java.text.DateFormat
import java.text.DecimalFormat import java.text.DecimalFormat
import java.util.* import java.util.Date
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlinx.android.synthetic.main.eh_manga_card.view.*
import kotlinx.android.synthetic.main.eh_migration_process_item.view.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import uy.kohesive.injekt.injectLazy
class MigrationProcedureAdapter(val controller: MigrationProcedureController, class MigrationProcedureAdapter(
val controller: MigrationProcedureController,
val migratingManga: List<MigratingManga>, val migratingManga: List<MigratingManga>,
override val coroutineContext: CoroutineContext) : androidx.viewpager.widget.PagerAdapter(), CoroutineScope { override val coroutineContext: CoroutineContext
) : androidx.viewpager.widget.PagerAdapter(), CoroutineScope {
private val db: DatabaseHelper by injectLazy() private val db: DatabaseHelper by injectLazy()
private val gson: Gson by injectLazy() private val gson: Gson by injectLazy()
private val sourceManager: SourceManager by injectLazy() private val sourceManager: SourceManager by injectLazy()
@ -96,9 +103,11 @@ class MigrationProcedureAdapter(val controller: MigrationProcedureController,
} }
} }
private fun migrateMangaInternal(prevManga: Manga, private fun migrateMangaInternal(
prevManga: Manga,
manga: Manga, manga: Manga,
replace: Boolean) { replace: Boolean
) {
db.inTransaction { db.inTransaction {
// Update chapters read // Update chapters read
if (MigrationFlags.hasChapters(controller.config.migrationFlags)) { if (MigrationFlags.hasChapters(controller.config.migrationFlags)) {

View File

@ -15,13 +15,21 @@ import eu.kanade.tachiyomi.util.system.toast
import exh.smartsearch.SmartSearchEngine import exh.smartsearch.SmartSearchEngine
import exh.ui.base.BaseExhController import exh.ui.base.BaseExhController
import exh.util.await import exh.util.await
import java.util.concurrent.atomic.AtomicInteger
import kotlinx.android.synthetic.main.eh_migration_process.* import kotlinx.android.synthetic.main.eh_migration_process.*
import kotlinx.coroutines.* import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.cancel
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit import kotlinx.coroutines.sync.withPermit
import kotlinx.coroutines.withContext
import rx.schedulers.Schedulers import rx.schedulers.Schedulers
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.util.concurrent.atomic.AtomicInteger
// TODO Will probably implode if activity is fully destroyed // TODO Will probably implode if activity is fully destroyed
class MigrationProcedureController(bundle: Bundle? = null) : BaseExhController(bundle), CoroutineScope { class MigrationProcedureController(bundle: Bundle? = null) : BaseExhController(bundle), CoroutineScope {

View File

@ -14,7 +14,13 @@ import eu.kanade.tachiyomi.ui.catalogue.browse.BrowseCatalogueController
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import kotlinx.android.synthetic.main.eh_smart_search.* import kotlinx.android.synthetic.main.eh_smart_search.*
import kotlinx.coroutines.* import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
class SmartSearchController(bundle: Bundle? = null) : NucleusController<SmartSearchPresenter>(), CoroutineScope { class SmartSearchController(bundle: Bundle? = null) : NucleusController<SmartSearchPresenter>(), CoroutineScope {

View File

@ -8,8 +8,13 @@ import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.ui.catalogue.CatalogueController import eu.kanade.tachiyomi.ui.catalogue.CatalogueController
import exh.smartsearch.SmartSearchEngine import exh.smartsearch.SmartSearchEngine
import kotlinx.coroutines.* import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
class SmartSearchPresenter(private val source: CatalogueSource?, private val config: CatalogueController.SmartSearchConfig?) : class SmartSearchPresenter(private val source: CatalogueSource?, private val config: CatalogueController.SmartSearchConfig?) :
BasePresenter<SmartSearchController>(), CoroutineScope { BasePresenter<SmartSearchController>(), CoroutineScope {
@ -48,7 +53,6 @@ class SmartSearchPresenter(private val source: CatalogueSource?, private val con
} }
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()

View File

@ -1,10 +1,10 @@
package exh.util package exh.util
import kotlin.coroutines.coroutineContext
import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.ensureActive import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlin.coroutines.coroutineContext
@FlowPreview @FlowPreview
fun <T> Flow<T>.cancellable() = onEach { fun <T> Flow<T>.cancellable() = onEach {

View File

@ -112,7 +112,6 @@ private inline class IteratorShim<E>(private val iterator: Iterator<E>) : FakeMu
override fun next() = iterator.next() override fun next() = iterator.next()
} }
interface FakeMutableIterator<E> : MutableIterator<E> { interface FakeMutableIterator<E> : MutableIterator<E> {
/** /**
* Removes from the underlying collection the last element returned by this iterator. * Removes from the underlying collection the last element returned by this iterator.

View File

@ -1,7 +1,10 @@
package exh.util package exh.util
import io.realm.* import io.realm.Case
import java.util.* import io.realm.RealmModel
import io.realm.RealmQuery
import io.realm.RealmResults
import java.util.Date
/** /**
* Realm query with logging * Realm query with logging
@ -9,14 +12,16 @@ import java.util.*
* @author nulldev * @author nulldev
*/ */
inline fun <reified E : RealmModel> RealmQuery<out E>.beginLog(clazz: Class<out E>? = inline fun <reified E : RealmModel> RealmQuery<out E>.beginLog(
E::class.java): LoggingRealmQuery<out E> clazz: Class<out E>? =
= LoggingRealmQuery.fromQuery(this, clazz) E::class.java
): LoggingRealmQuery<out E> =
LoggingRealmQuery.fromQuery(this, clazz)
class LoggingRealmQuery<E : RealmModel>(val query: RealmQuery<E>) { class LoggingRealmQuery<E : RealmModel>(val query: RealmQuery<E>) {
companion object { companion object {
fun <E : RealmModel> fromQuery(q: RealmQuery<out E>, clazz: Class<out E>?) fun <E : RealmModel> fromQuery(q: RealmQuery<out E>, clazz: Class<out E>?) =
= LoggingRealmQuery(q).apply { LoggingRealmQuery(q).apply {
log += "SELECT * FROM ${clazz?.name ?: "???"} WHERE" log += "SELECT * FROM ${clazz?.name ?: "???"} WHERE"
} }
} }

View File

@ -1,7 +1,8 @@
package exh.util package exh.util
import android.util.SparseArray import android.util.SparseArray
import java.util.* import java.util.AbstractMap
import java.util.LinkedList
class NakedTrieNode<T>(val key: Int, var parent: NakedTrieNode<T>?) { class NakedTrieNode<T>(val key: Int, var parent: NakedTrieNode<T>?) {
val children = SparseArray<NakedTrieNode<T>>(1) val children = SparseArray<NakedTrieNode<T>>(1)

View File

@ -8,8 +8,8 @@ import org.jsoup.nodes.Document
fun Response.interceptAsHtml(block: (Document) -> Unit): Response { fun Response.interceptAsHtml(block: (Document) -> Unit): Response {
val body = body val body = body
if (body?.contentType()?.type == "text" if (body?.contentType()?.type == "text" &&
&& body.contentType()?.subtype == "html") { body.contentType()?.subtype == "html") {
val bodyString = body.string() val bodyString = body.string()
val rebuiltResponse = newBuilder() val rebuiltResponse = newBuilder()
.body(ResponseBody.create(body.contentType(), bodyString)) .body(ResponseBody.create(body.contentType(), bodyString))

View File

@ -3,7 +3,7 @@ package exh.util
import io.realm.Realm import io.realm.Realm
import io.realm.RealmModel import io.realm.RealmModel
import io.realm.log.RealmLog import io.realm.log.RealmLog
import java.util.* import java.util.UUID
inline fun <T> realmTrans(block: (Realm) -> T): T { inline fun <T> realmTrans(block: (Realm) -> T): T {
return defRealm { return defRealm {
@ -49,9 +49,8 @@ inline fun <T> Realm.useTrans(block: (Realm) -> T): T {
} }
} }
fun <T : RealmModel> Realm.createUUIDObj(clazz: Class<T>) fun <T : RealmModel> Realm.createUUIDObj(clazz: Class<T>) =
= createObject(clazz, UUID.randomUUID().toString())!! createObject(clazz, UUID.randomUUID().toString())!!
inline fun <reified T : RealmModel> Realm.createUUIDObj()
= createUUIDObj(T::class.java)
inline fun <reified T : RealmModel> Realm.createUUIDObj() =
createUUIDObj(T::class.java)

View File

@ -2,10 +2,14 @@ package exh.util
import com.pushtorefresh.storio.operations.PreparedOperation import com.pushtorefresh.storio.operations.PreparedOperation
import com.pushtorefresh.storio.sqlite.operations.get.PreparedGetObject import com.pushtorefresh.storio.sqlite.operations.get.PreparedGetObject
import kotlinx.coroutines.suspendCancellableCoroutine
import rx.*
import rx.subjects.ReplaySubject
import kotlin.coroutines.resumeWithException import kotlin.coroutines.resumeWithException
import kotlinx.coroutines.suspendCancellableCoroutine
import rx.Completable
import rx.Observable
import rx.Scheduler
import rx.Single
import rx.Subscription
import rx.subjects.ReplaySubject
/** /**
* Transform a cold single to a hot single * Transform a cold single to a hot single

View File

@ -1,6 +1,17 @@
package xyz.nulldev.ts.api.http.serializer package xyz.nulldev.ts.api.http.serializer
import com.github.salomonbrys.kotson.* import com.github.salomonbrys.kotson.bool
import com.github.salomonbrys.kotson.byte
import com.github.salomonbrys.kotson.char
import com.github.salomonbrys.kotson.double
import com.github.salomonbrys.kotson.float
import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.int
import com.github.salomonbrys.kotson.long
import com.github.salomonbrys.kotson.obj
import com.github.salomonbrys.kotson.set
import com.github.salomonbrys.kotson.short
import com.github.salomonbrys.kotson.string
import com.google.gson.JsonArray import com.google.gson.JsonArray
import com.google.gson.JsonObject import com.google.gson.JsonObject
import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.Filter