mirror of
https://github.com/mihonapp/mihon.git
synced 2025-11-15 13:37:29 +01:00
Migrate to realm for metadata
This commit is contained in:
@@ -10,11 +10,14 @@ import eu.kanade.tachiyomi.data.backup.BackupCreatorJob
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
||||
import eu.kanade.tachiyomi.data.updater.UpdateCheckerJob
|
||||
import eu.kanade.tachiyomi.util.LocaleHelper
|
||||
import io.paperdb.Paper
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmConfiguration
|
||||
import timber.log.Timber
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.InjektScope
|
||||
import uy.kohesive.injekt.registry.default.DefaultRegistrar
|
||||
import java.io.File
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
open class App : Application() {
|
||||
|
||||
@@ -26,7 +29,7 @@ open class App : Application() {
|
||||
if (BuildConfig.DEBUG) Timber.plant(Timber.DebugTree())
|
||||
|
||||
setupJobManager()
|
||||
Paper.init(this) //Setup metadata DB (EH)
|
||||
setupRealm() //Setup metadata DB (EH)
|
||||
Reprint.initialize(this) //Setup fingerprint (EH)
|
||||
|
||||
LocaleHelper.updateConfiguration(this, resources.configuration)
|
||||
@@ -55,4 +58,25 @@ open class App : Application() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupRealm() {
|
||||
Realm.init(this)
|
||||
val config = RealmConfiguration.Builder()
|
||||
.name("gallery-metadata.realm")
|
||||
.schemaVersion(1)
|
||||
.build()
|
||||
Realm.setDefaultConfiguration(config)
|
||||
|
||||
//Delete old paper db files
|
||||
listOf(
|
||||
File(filesDir, "gallery-ex"),
|
||||
File(filesDir, "gallery-perveden"),
|
||||
File(filesDir, "gallery-nhentai")
|
||||
).forEach {
|
||||
if(it.exists()) {
|
||||
thread {
|
||||
it.deleteRecursively()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,14 +180,12 @@ class PreferencesHelper(val context: Context) {
|
||||
|
||||
fun thumbnailRows() = rxPrefs.getString("ex_thumb_rows", "tr_2")
|
||||
|
||||
fun migrateLibraryAsked() = rxPrefs.getBoolean("ex_migrate_library", false)
|
||||
fun migrateLibraryAsked2() = rxPrefs.getBoolean("ex_migrate_library2", false)
|
||||
|
||||
fun migrationStatus() = rxPrefs.getInteger("migration_status", MigrationStatus.NOT_INITIALIZED)
|
||||
|
||||
fun hasPerformedURLMigration() = rxPrefs.getBoolean("performed_url_migration", false)
|
||||
|
||||
fun hasPerformedSourceMigration() = rxPrefs.getBoolean("performed_source_migration", false)
|
||||
|
||||
//EH Cookies
|
||||
fun memberIdVal() = rxPrefs.getString("eh_ipb_member_id", null)
|
||||
fun passHashVal() = rxPrefs.getString("eh_ipb_pass_hash", null)
|
||||
|
||||
@@ -10,7 +10,6 @@ import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import eu.kanade.tachiyomi.source.online.all.EHentai
|
||||
import eu.kanade.tachiyomi.source.online.all.EHentaiMetadata
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.source.online.YamlHttpSource
|
||||
import eu.kanade.tachiyomi.source.online.all.NHentai
|
||||
@@ -88,13 +87,11 @@ open class SourceManager(private val context: Context) {
|
||||
)
|
||||
|
||||
private fun createEHSources(): List<Source> {
|
||||
val exSrcs = mutableListOf(
|
||||
EHentai(EH_SOURCE_ID, false, context),
|
||||
EHentaiMetadata(EH_METADATA_SOURCE_ID, false, context)
|
||||
val exSrcs = mutableListOf<HttpSource>(
|
||||
EHentai(EH_SOURCE_ID, false, context)
|
||||
)
|
||||
if(prefs.enableExhentai().getOrDefault()) {
|
||||
exSrcs += EHentai(EXH_SOURCE_ID, true, context)
|
||||
exSrcs += EHentaiMetadata(EXH_METADATA_SOURCE_ID, true, context)
|
||||
}
|
||||
exSrcs += PervEden(PERV_EDEN_EN_SOURCE_ID, "en")
|
||||
exSrcs += PervEden(PERV_EDEN_IT_SOURCE_ID, "it")
|
||||
|
||||
@@ -20,14 +20,13 @@ import uy.kohesive.injekt.injectLazy
|
||||
import java.net.URLEncoder
|
||||
import java.util.*
|
||||
import exh.ui.login.LoginController
|
||||
import exh.util.UriFilter
|
||||
import exh.util.UriGroup
|
||||
import okhttp3.CacheControl
|
||||
import okhttp3.Headers
|
||||
import okhttp3.Request
|
||||
import org.jsoup.nodes.Document
|
||||
import exh.GalleryAdder
|
||||
import exh.util.urlImportFetchSearchManga
|
||||
import exh.util.*
|
||||
import io.realm.Realm
|
||||
|
||||
class EHentai(override val id: Long,
|
||||
val exh: Boolean,
|
||||
@@ -50,8 +49,6 @@ class EHentai(override val id: Long,
|
||||
|
||||
val prefs: PreferencesHelper by injectLazy()
|
||||
|
||||
val metadataHelper = MetadataHelper()
|
||||
|
||||
val galleryAdder = GalleryAdder()
|
||||
|
||||
/**
|
||||
@@ -168,10 +165,10 @@ class EHentai(override val id: Long,
|
||||
override fun latestUpdatesParse(response: Response) = genericMangaParse(response)
|
||||
|
||||
fun exGet(url: String, page: Int? = null, additionalHeaders: Headers? = null, cache: Boolean = true)
|
||||
= GET(page?.let {
|
||||
addParam(url, "page", Integer.toString(page - 1))
|
||||
} ?: url, additionalHeaders?.let {
|
||||
val headers = headers.newBuilder()
|
||||
= GET(page?.let {
|
||||
addParam(url, "page", Integer.toString(page - 1))
|
||||
} ?: url, additionalHeaders?.let {
|
||||
val headers = headers.newBuilder()
|
||||
it.toMultimap().forEach { (t, u) ->
|
||||
u.forEach {
|
||||
headers.add(t, it)
|
||||
@@ -188,86 +185,90 @@ class EHentai(override val id: Long,
|
||||
/**
|
||||
* Parse gallery page to metadata model
|
||||
*/
|
||||
override fun mangaDetailsParse(response: Response) = with(response.asJsoup()) {
|
||||
val metdata = ExGalleryMetadata()
|
||||
with(metdata) {
|
||||
url = response.request().url().encodedPath()
|
||||
exh = this@EHentai.exh
|
||||
title = select("#gn").text().nullIfBlank()?.trim()
|
||||
override fun mangaDetailsParse(response: Response)
|
||||
= with(response.asJsoup()) {
|
||||
realmTrans { realm ->
|
||||
val url = response.request().url().encodedPath()!!
|
||||
val gId = ExGalleryMetadata.galleryId(url)
|
||||
val gToken = ExGalleryMetadata.galleryToken(url)
|
||||
|
||||
altTitle = select("#gj").text().nullIfBlank()?.trim()
|
||||
val metdata = (realm.loadEh(gId, gToken, exh)
|
||||
?: realm.createUUIDObj(ExGalleryMetadata::class.java))
|
||||
with(metdata) {
|
||||
this.url = url
|
||||
this.gId = gId
|
||||
this.gToken = gToken
|
||||
|
||||
thumbnailUrl = select("#gd1 div").attr("style").nullIfBlank()?.let {
|
||||
it.substring(it.indexOf('(') + 1 until it.lastIndexOf(')'))
|
||||
}
|
||||
exh = this@EHentai.exh
|
||||
title = select("#gn").text().nullIfBlank()?.trim()
|
||||
|
||||
genre = select(".ic").parents().attr("href").nullIfBlank()?.trim()?.substringAfterLast('/')
|
||||
altTitle = select("#gj").text().nullIfBlank()?.trim()
|
||||
|
||||
uploader = select("#gdn").text().nullIfBlank()?.trim()
|
||||
thumbnailUrl = select("#gd1 div").attr("style").nullIfBlank()?.let {
|
||||
it.substring(it.indexOf('(') + 1 until it.lastIndexOf(')'))
|
||||
}
|
||||
genre = select(".ic").parents().attr("href").nullIfBlank()?.trim()?.substringAfterLast('/')
|
||||
|
||||
//Parse the table
|
||||
select("#gdd tr").forEach {
|
||||
it.select(".gdt1")
|
||||
.text()
|
||||
.nullIfBlank()
|
||||
?.trim()
|
||||
?.let { left ->
|
||||
it.select(".gdt2")
|
||||
.text()
|
||||
.nullIfBlank()
|
||||
?.trim()
|
||||
?.let { right ->
|
||||
ignore {
|
||||
when (left.removeSuffix(":")
|
||||
.toLowerCase()) {
|
||||
"posted" -> datePosted = EX_DATE_FORMAT.parse(right).time
|
||||
"visible" -> visible = right.nullIfBlank()
|
||||
"language" -> {
|
||||
language = right.removeSuffix(TR_SUFFIX).trim().nullIfBlank()
|
||||
translated = right.endsWith(TR_SUFFIX, true)
|
||||
uploader = select("#gdn").text().nullIfBlank()?.trim()
|
||||
|
||||
//Parse the table
|
||||
select("#gdd tr").forEach {
|
||||
it.select(".gdt1")
|
||||
.text()
|
||||
.nullIfBlank()
|
||||
?.trim()
|
||||
?.let { left ->
|
||||
it.select(".gdt2")
|
||||
.text()
|
||||
.nullIfBlank()
|
||||
?.trim()
|
||||
?.let { right ->
|
||||
ignore {
|
||||
when (left.removeSuffix(":")
|
||||
.toLowerCase()) {
|
||||
"posted" -> datePosted = EX_DATE_FORMAT.parse(right).time
|
||||
"visible" -> visible = right.nullIfBlank()
|
||||
"language" -> {
|
||||
language = right.removeSuffix(TR_SUFFIX).trim().nullIfBlank()
|
||||
translated = right.endsWith(TR_SUFFIX, true)
|
||||
}
|
||||
"file size" -> size = parseHumanReadableByteCount(right)?.toLong()
|
||||
"length" -> length = right.removeSuffix("pages").trim().nullIfBlank()?.toInt()
|
||||
"favorited" -> favorites = right.removeSuffix("times").trim().nullIfBlank()?.toInt()
|
||||
}
|
||||
"file size" -> size = parseHumanReadableByteCount(right)?.toLong()
|
||||
"length" -> length = right.removeSuffix("pages").trim().nullIfBlank()?.toInt()
|
||||
"favorited" -> favorites = right.removeSuffix("times").trim().nullIfBlank()?.toInt()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Parse ratings
|
||||
ignore {
|
||||
averageRating = select("#rating_label")
|
||||
.text()
|
||||
.removePrefix("Average:")
|
||||
.trim()
|
||||
.nullIfBlank()
|
||||
?.toDouble()
|
||||
ratingCount = select("#rating_count")
|
||||
.text()
|
||||
.trim()
|
||||
.nullIfBlank()
|
||||
?.toInt()
|
||||
}
|
||||
|
||||
//Parse tags
|
||||
tags.clear()
|
||||
select("#taglist tr").forEach {
|
||||
val namespace = it.select(".tc").text().removeSuffix(":")
|
||||
val currentTags = it.select("div").map {
|
||||
Tag(it.text().trim(),
|
||||
it.hasClass("gtl"))
|
||||
}
|
||||
}
|
||||
tags.put(namespace, ArrayList(currentTags))
|
||||
}
|
||||
|
||||
//Save metadata
|
||||
metadataHelper.writeGallery(this, id)
|
||||
//Parse ratings
|
||||
ignore {
|
||||
averageRating = select("#rating_label")
|
||||
.text()
|
||||
.removePrefix("Average:")
|
||||
.trim()
|
||||
.nullIfBlank()
|
||||
?.toDouble()
|
||||
ratingCount = select("#rating_count")
|
||||
.text()
|
||||
.trim()
|
||||
.nullIfBlank()
|
||||
?.toInt()
|
||||
}
|
||||
|
||||
//Copy metadata to manga
|
||||
SManga.create().let {
|
||||
copyTo(it)
|
||||
it
|
||||
//Parse tags
|
||||
tags.clear()
|
||||
select("#taglist tr").forEach {
|
||||
val namespace = it.select(".tc").text().removeSuffix(":")
|
||||
tags.addAll(it.select("div").map {
|
||||
Tag(namespace, it.text().trim(), it.hasClass("gtl"))
|
||||
})
|
||||
}
|
||||
|
||||
//Copy metadata to manga
|
||||
SManga.create().apply {
|
||||
copyTo(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
package eu.kanade.tachiyomi.source.online.all
|
||||
|
||||
import android.content.Context
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.source.model.*
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import exh.metadata.MetadataHelper
|
||||
import exh.metadata.copyTo
|
||||
import exh.metadata.models.ExGalleryMetadata
|
||||
import exh.search.SearchEngine
|
||||
import okhttp3.Response
|
||||
import rx.Observable
|
||||
|
||||
/**
|
||||
* Offline metadata store source
|
||||
*
|
||||
* TODO This no longer fakes an online source because of technical reasons.
|
||||
* If we still want offline search, we must find out a way to rearchitecture the source system so it supports
|
||||
* online source faking again.
|
||||
*/
|
||||
|
||||
class EHentaiMetadata(override val id: Long,
|
||||
val exh: Boolean,
|
||||
val context: Context) : HttpSource() {
|
||||
override fun popularMangaRequest(page: Int)
|
||||
= throw UnsupportedOperationException("Unused method called!")
|
||||
override fun popularMangaParse(response: Response)
|
||||
= throw UnsupportedOperationException("Unused method called!")
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList)
|
||||
= throw UnsupportedOperationException("Unused method called!")
|
||||
override fun searchMangaParse(response: Response)
|
||||
= throw UnsupportedOperationException("Unused method called!")
|
||||
override fun latestUpdatesRequest(page: Int)
|
||||
= throw UnsupportedOperationException("Unused method called!")
|
||||
override fun latestUpdatesParse(response: Response)
|
||||
= throw UnsupportedOperationException("Unused method called!")
|
||||
override fun mangaDetailsParse(response: Response)
|
||||
= throw UnsupportedOperationException("Unused method called!")
|
||||
override fun chapterListParse(response: Response)
|
||||
= throw UnsupportedOperationException("Unused method called!")
|
||||
override fun pageListParse(response: Response)
|
||||
= throw UnsupportedOperationException("Unused method called!")
|
||||
override fun imageUrlParse(response: Response)
|
||||
= throw UnsupportedOperationException("Unused method called!")
|
||||
|
||||
val metadataHelper = MetadataHelper()
|
||||
|
||||
val internalEx = EHentai(id - 2, exh, context)
|
||||
|
||||
val searchEngine = SearchEngine()
|
||||
|
||||
override val baseUrl: String
|
||||
get() = throw UnsupportedOperationException()
|
||||
override val lang: String
|
||||
get() = "advanced"
|
||||
override val supportsLatest: Boolean
|
||||
get() = true
|
||||
|
||||
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>>
|
||||
= Observable.just(listOf(Chapter.create().apply {
|
||||
url = manga.url
|
||||
name = "ONLINE - Chapter"
|
||||
chapter_number = 1f
|
||||
}))
|
||||
|
||||
override fun fetchPageList(chapter: SChapter) = internalEx.fetchPageList(chapter)
|
||||
|
||||
override fun fetchImageUrl(page: Page) = internalEx.fetchImageUrl(page)
|
||||
|
||||
fun List<ExGalleryMetadata>.mapToManga() = filter { it.exh == exh }
|
||||
.map {
|
||||
Manga.create(id).apply {
|
||||
it.copyTo(this)
|
||||
source = this@EHentaiMetadata.id
|
||||
}
|
||||
}
|
||||
|
||||
fun sortedByTimeGalleries() = metadataHelper.getAllGalleries().sortedByDescending {
|
||||
it.datePosted ?: 0
|
||||
}
|
||||
|
||||
override fun fetchPopularManga(page: Int)
|
||||
= Observable.fromCallable {
|
||||
MangasPage(metadataHelper.getAllGalleries().sortedByDescending {
|
||||
it.ratingCount ?: 0
|
||||
}.mapToManga(), false)
|
||||
}!!
|
||||
|
||||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList)
|
||||
= Observable.fromCallable {
|
||||
val genreGroup = filters.find {
|
||||
it is EHentai.GenreGroup
|
||||
}!! as EHentai.GenreGroup
|
||||
val disableGenreFilter = genreGroup.state.find(EHentai.GenreOption::state) == null
|
||||
|
||||
val parsed = searchEngine.parseQuery(query)
|
||||
MangasPage(sortedByTimeGalleries().filter { manga ->
|
||||
disableGenreFilter || genreGroup.state.find {
|
||||
it.state && it.genreId == manga.genre
|
||||
} != null
|
||||
}.filter {
|
||||
searchEngine.matches(it, parsed)
|
||||
}.mapToManga(), false)
|
||||
}!!
|
||||
|
||||
override fun fetchLatestUpdates(page: Int)
|
||||
= Observable.fromCallable {
|
||||
MangasPage(sortedByTimeGalleries().mapToManga(), false)
|
||||
}!!
|
||||
|
||||
override fun fetchMangaDetails(manga: SManga) = Observable.fromCallable {
|
||||
//Hack to convert the gallery into an online gallery when favoriting it or reading it
|
||||
metadataHelper.fetchEhMetadata(manga.url, exh)?.copyTo(manga)
|
||||
manga
|
||||
}!!
|
||||
|
||||
override fun getFilterList() = FilterList(EHentai.GenreGroup())
|
||||
|
||||
override val name: String
|
||||
get() = if(exh) {
|
||||
"ExHentai"
|
||||
} else {
|
||||
"E-Hentai"
|
||||
} + " - METADATA"
|
||||
|
||||
}
|
||||
@@ -17,10 +17,15 @@ import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||
import eu.kanade.tachiyomi.source.model.*
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import exh.NHENTAI_SOURCE_ID
|
||||
import exh.metadata.MetadataHelper
|
||||
import exh.metadata.copyTo
|
||||
import exh.metadata.loadNhentai
|
||||
import exh.metadata.loadNhentaiAsync
|
||||
import exh.metadata.models.NHentaiMetadata
|
||||
import exh.metadata.models.PageImageType
|
||||
import exh.metadata.models.Tag
|
||||
import exh.util.createUUIDObj
|
||||
import exh.util.defRealm
|
||||
import exh.util.realmTrans
|
||||
import exh.util.urlImportFetchSearchManga
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
@@ -108,62 +113,73 @@ class NHentai(context: Context) : HttpSource() {
|
||||
return MangasPage(emptyList(), false)
|
||||
}
|
||||
|
||||
fun rawParseGallery(obj: JsonObject) = NHentaiMetadata().apply {
|
||||
uploadDate = obj.get("upload_date")?.notNull()?.long
|
||||
fun rawParseGallery(obj: JsonObject) = realmTrans { realm ->
|
||||
val nhId = obj.get("id").asLong
|
||||
|
||||
favoritesCount = obj.get("num_favorites")?.notNull()?.long
|
||||
(realm.loadNhentai(nhId)
|
||||
?: realm.createUUIDObj(NHentaiMetadata::class.java)).apply {
|
||||
this.nhId = nhId
|
||||
|
||||
mediaId = obj.get("media_id")?.notNull()?.string
|
||||
uploadDate = obj.get("upload_date")?.notNull()?.long
|
||||
|
||||
obj.get("title")?.asJsonObject?.let {
|
||||
japaneseTitle = it.get("japanese")?.notNull()?.string
|
||||
shortTitle = it.get("pretty")?.notNull()?.string
|
||||
englishTitle = it.get("english")?.notNull()?.string
|
||||
}
|
||||
favoritesCount = obj.get("num_favorites")?.notNull()?.long
|
||||
|
||||
obj.get("images")?.asJsonObject?.let {
|
||||
coverImageType = it.get("cover")?.get("t")?.notNull()?.asString
|
||||
it.get("pages")?.asJsonArray?.map {
|
||||
it?.asJsonObject?.get("t")?.notNull()?.asString
|
||||
}?.filterNotNull()?.let {
|
||||
pageImageTypes.clear()
|
||||
pageImageTypes.addAll(it)
|
||||
mediaId = obj.get("media_id")?.notNull()?.string
|
||||
|
||||
obj.get("title")?.asJsonObject?.let {
|
||||
japaneseTitle = it.get("japanese")?.notNull()?.string
|
||||
shortTitle = it.get("pretty")?.notNull()?.string
|
||||
englishTitle = it.get("english")?.notNull()?.string
|
||||
}
|
||||
thumbnailImageType = it.get("thumbnail")?.get("t")?.notNull()?.asString
|
||||
}
|
||||
|
||||
scanlator = obj.get("scanlator")?.notNull()?.asString
|
||||
obj.get("images")?.asJsonObject?.let {
|
||||
coverImageType = it.get("cover")?.get("t")?.notNull()?.asString
|
||||
it.get("pages")?.asJsonArray?.map {
|
||||
it?.asJsonObject?.get("t")?.notNull()?.asString
|
||||
}?.filterNotNull()?.map {
|
||||
PageImageType(it)
|
||||
}?.let {
|
||||
pageImageTypes.clear()
|
||||
pageImageTypes.addAll(it)
|
||||
}
|
||||
thumbnailImageType = it.get("thumbnail")?.get("t")?.notNull()?.asString
|
||||
}
|
||||
|
||||
id = obj.get("id")?.asLong
|
||||
scanlator = obj.get("scanlator")?.notNull()?.asString
|
||||
|
||||
obj.get("tags")?.asJsonArray?.map {
|
||||
val asObj = it.asJsonObject
|
||||
Pair(asObj.get("type")?.string, asObj.get("name")?.string)
|
||||
}?.apply {
|
||||
tags.clear()
|
||||
}?.forEach {
|
||||
if(it.first != null && it.second != null)
|
||||
tags.getOrPut(it.first!!, { ArrayList() }).add(Tag(it.second!!, false))
|
||||
obj.get("tags")?.asJsonArray?.map {
|
||||
val asObj = it.asJsonObject
|
||||
Pair(asObj.get("type")?.string, asObj.get("name")?.string)
|
||||
}?.apply {
|
||||
tags.clear()
|
||||
}?.forEach {
|
||||
if(it.first != null && it.second != null)
|
||||
tags.add(Tag(it.first!!, it.second!!, false))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun parseGallery(obj: JsonObject) = rawParseGallery(obj).let {
|
||||
metadataHelper.writeGallery(it, id)
|
||||
|
||||
SManga.create().apply {
|
||||
it.copyTo(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun lazyLoadMetadata(url: String) =
|
||||
Observable.fromCallable {
|
||||
metadataHelper.fetchNhentaiMetadata(url)
|
||||
?: client.newCall(urlToDetailsRequest(url))
|
||||
.asObservableSuccess()
|
||||
.map {
|
||||
rawParseGallery(jsonParser.parse(it.body()!!.string()).asJsonObject)
|
||||
}.toBlocking().first()
|
||||
}!!
|
||||
defRealm { realm ->
|
||||
realm.loadNhentaiAsync(NHentaiMetadata.nhIdFromUrl(url))
|
||||
.flatMap {
|
||||
if(it == null)
|
||||
client.newCall(urlToDetailsRequest(url))
|
||||
.asObservableSuccess()
|
||||
.map {
|
||||
rawParseGallery(jsonParser.parse(it.body()!!.string())
|
||||
.asJsonObject)
|
||||
}.first()
|
||||
else
|
||||
Observable.just(it)
|
||||
}.map { realm.copyFromRealm(it) }
|
||||
}
|
||||
|
||||
override fun fetchChapterList(manga: SManga)
|
||||
= lazyLoadMetadata(manga.url).map {
|
||||
@@ -181,7 +197,7 @@ class NHentai(context: Context) : HttpSource() {
|
||||
if(metadata.mediaId == null) emptyList()
|
||||
else
|
||||
metadata.pageImageTypes.mapIndexed { index, s ->
|
||||
val imageUrl = imageUrlFromType(metadata.mediaId!!, index + 1, s)
|
||||
val imageUrl = imageUrlFromType(metadata.mediaId!!, index + 1, s.type!!)
|
||||
Page(index, imageUrl!!, imageUrl)
|
||||
}
|
||||
}!!
|
||||
@@ -231,14 +247,10 @@ class NHentai(context: Context) : HttpSource() {
|
||||
val jsonParser by lazy {
|
||||
JsonParser()
|
||||
}
|
||||
|
||||
val metadataHelper by lazy {
|
||||
MetadataHelper()
|
||||
}
|
||||
}
|
||||
|
||||
fun JsonElement.notNull() =
|
||||
if(this is JsonNull)
|
||||
null
|
||||
else this
|
||||
if(this is JsonNull)
|
||||
null
|
||||
else this
|
||||
}
|
||||
|
||||
@@ -6,12 +6,15 @@ import eu.kanade.tachiyomi.source.model.*
|
||||
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
|
||||
import eu.kanade.tachiyomi.util.ChapterRecognition
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import exh.metadata.MetadataHelper
|
||||
import exh.metadata.copyTo
|
||||
import exh.metadata.loadPervEden
|
||||
import exh.metadata.models.PervEdenGalleryMetadata
|
||||
import exh.metadata.models.PervEdenTitle
|
||||
import exh.metadata.models.Tag
|
||||
import exh.util.UriFilter
|
||||
import exh.util.UriGroup
|
||||
import exh.util.createUUIDObj
|
||||
import exh.util.realmTrans
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.jsoup.nodes.Document
|
||||
@@ -27,8 +30,6 @@ class PervEden(override val id: Long, override val lang: String) : ParsedHttpSou
|
||||
override val name = "Perv Eden"
|
||||
override val baseUrl = "http://www.perveden.com"
|
||||
|
||||
val metadataHelper by lazy { MetadataHelper() }
|
||||
|
||||
override fun popularMangaSelector() = "#topManga > ul > li"
|
||||
|
||||
override fun popularMangaFromElement(element: Element): SManga {
|
||||
@@ -99,72 +100,68 @@ class PervEden(override val id: Long, override val lang: String) : ParsedHttpSou
|
||||
}
|
||||
|
||||
override fun mangaDetailsParse(document: Document): SManga {
|
||||
val metadata = PervEdenGalleryMetadata()
|
||||
with(metadata) {
|
||||
url = document.location()
|
||||
realmTrans { realm ->
|
||||
val url = document.location()
|
||||
val metadata = (realm.loadPervEden(PervEdenGalleryMetadata.pvIdFromUrl(url), id)
|
||||
?: realm.createUUIDObj(PervEdenGalleryMetadata::class.java))
|
||||
with(metadata) {
|
||||
this.url = url
|
||||
|
||||
lang = this@PervEden.lang
|
||||
lang = this@PervEden.lang
|
||||
|
||||
title = document.getElementsByClass("manga-title").first()?.text()
|
||||
title = document.getElementsByClass("manga-title").first()?.text()
|
||||
|
||||
thumbnailUrl = "http:" + document.getElementsByClass("mangaImage2").first()?.child(0)?.attr("src")
|
||||
thumbnailUrl = "http:" + document.getElementsByClass("mangaImage2").first()?.child(0)?.attr("src")
|
||||
|
||||
val rightBoxElement = document.select(".rightBox:not(.info)").first()
|
||||
val rightBoxElement = document.select(".rightBox:not(.info)").first()
|
||||
|
||||
tags.clear()
|
||||
var inStatus: String? = null
|
||||
rightBoxElement.childNodes().forEach {
|
||||
if(it is Element && it.tagName().toLowerCase() == "h4") {
|
||||
inStatus = it.text().trim()
|
||||
} else {
|
||||
when(inStatus) {
|
||||
"Alternative name(s)" -> {
|
||||
if(it is TextNode) {
|
||||
val text = it.text().trim()
|
||||
if(!text.isBlank())
|
||||
altTitles.add(text)
|
||||
tags.clear()
|
||||
var inStatus: String? = null
|
||||
rightBoxElement.childNodes().forEach {
|
||||
if(it is Element && it.tagName().toLowerCase() == "h4") {
|
||||
inStatus = it.text().trim()
|
||||
} else {
|
||||
when(inStatus) {
|
||||
"Alternative name(s)" -> {
|
||||
if(it is TextNode) {
|
||||
val text = it.text().trim()
|
||||
if(!text.isBlank())
|
||||
altTitles.add(PervEdenTitle(this, text))
|
||||
}
|
||||
}
|
||||
}
|
||||
"Artist" -> {
|
||||
if(it is Element && it.tagName() == "a") {
|
||||
artist = it.text()
|
||||
tags.getOrPut("artist", {
|
||||
ArrayList()
|
||||
}).add(Tag(it.text().toLowerCase(), false))
|
||||
"Artist" -> {
|
||||
if(it is Element && it.tagName() == "a") {
|
||||
artist = it.text()
|
||||
tags.add(Tag("artist", it.text().toLowerCase(), false))
|
||||
}
|
||||
}
|
||||
}
|
||||
"Genres" -> {
|
||||
if(it is Element && it.tagName() == "a")
|
||||
tags.getOrPut("genre", {
|
||||
ArrayList()
|
||||
}).add(Tag(it.text().toLowerCase(), false))
|
||||
}
|
||||
"Type" -> {
|
||||
if(it is TextNode) {
|
||||
val text = it.text().trim()
|
||||
if(!text.isBlank())
|
||||
type = text
|
||||
"Genres" -> {
|
||||
if(it is Element && it.tagName() == "a")
|
||||
tags.add(Tag("genre", it.text().toLowerCase(), false))
|
||||
}
|
||||
}
|
||||
"Status" -> {
|
||||
if(it is TextNode) {
|
||||
val text = it.text().trim()
|
||||
if(!text.isBlank())
|
||||
status = text
|
||||
"Type" -> {
|
||||
if(it is TextNode) {
|
||||
val text = it.text().trim()
|
||||
if(!text.isBlank())
|
||||
type = text
|
||||
}
|
||||
}
|
||||
"Status" -> {
|
||||
if(it is TextNode) {
|
||||
val text = it.text().trim()
|
||||
if(!text.isBlank())
|
||||
status = text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rating = document.getElementById("rating-score")?.attr("value")?.toFloat()
|
||||
rating = document.getElementById("rating-score")?.attr("value")?.toFloat()
|
||||
|
||||
//Save metadata
|
||||
Timber.d("LNG: " + metadata.lang)
|
||||
metadataHelper.writeGallery(this, id)
|
||||
|
||||
return SManga.create().apply {
|
||||
copyTo(this)
|
||||
return SManga.create().apply {
|
||||
copyTo(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -197,12 +194,12 @@ class PervEden(override val id: Long, override val lang: String) : ParsedHttpSou
|
||||
}
|
||||
|
||||
override fun pageListParse(document: Document)
|
||||
= document.getElementById("pageSelect").getElementsByTag("option").map {
|
||||
Page(it.attr("data-page").toInt() - 1, baseUrl + it.attr("value"))
|
||||
}
|
||||
= document.getElementById("pageSelect").getElementsByTag("option").map {
|
||||
Page(it.attr("data-page").toInt() - 1, baseUrl + it.attr("value"))
|
||||
}
|
||||
|
||||
override fun imageUrlParse(document: Document)
|
||||
= "http:" + document.getElementById("mainImg").attr("src")!!
|
||||
= "http:" + document.getElementById("mainImg").attr("src")!!
|
||||
|
||||
override fun getFilterList() = FilterList (
|
||||
AuthorFilter(),
|
||||
|
||||
@@ -2,6 +2,18 @@ package eu.kanade.tachiyomi.ui.library
|
||||
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import exh.*
|
||||
import exh.metadata.loadAllMetadata
|
||||
import exh.metadata.models.ExGalleryMetadata
|
||||
import exh.metadata.models.NHentaiMetadata
|
||||
import exh.metadata.models.PervEdenGalleryMetadata
|
||||
import exh.metadata.queryMetadataFromManga
|
||||
import exh.search.SearchEngine
|
||||
import exh.util.defRealm
|
||||
import rx.Observable
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import rx.schedulers.Schedulers
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
/**
|
||||
* Adapter storing a list of manga in a certain category.
|
||||
@@ -16,7 +28,9 @@ class LibraryCategoryAdapter(view: LibraryCategoryView) :
|
||||
*/
|
||||
private var mangas: List<LibraryItem> = emptyList()
|
||||
|
||||
var asyncSearchText: String? = null
|
||||
// --> EH
|
||||
private val searchEngine = SearchEngine()
|
||||
// <-- EH
|
||||
|
||||
/**
|
||||
* Sets a list of manga in the adapter.
|
||||
@@ -40,9 +54,42 @@ class LibraryCategoryAdapter(view: LibraryCategoryView) :
|
||||
}
|
||||
|
||||
fun performFilter() {
|
||||
updateDataSet(mangas.filter {
|
||||
it.filter(searchText)
|
||||
})
|
||||
Observable.fromCallable {
|
||||
defRealm { realm ->
|
||||
val parsedQuery = searchEngine.parseQuery(searchText)
|
||||
val metadata = realm.loadAllMetadata().map {
|
||||
Pair(it.key, searchEngine.filterResults(it.value, parsedQuery))
|
||||
}
|
||||
mangas.filter { manga ->
|
||||
// --> EH
|
||||
if (isLewdSource(manga.manga.source)) {
|
||||
metadata.any {
|
||||
when (manga.manga.source) {
|
||||
EH_SOURCE_ID,
|
||||
EXH_SOURCE_ID ->
|
||||
if (it.first != ExGalleryMetadata::class)
|
||||
return@any false
|
||||
PERV_EDEN_IT_SOURCE_ID,
|
||||
PERV_EDEN_EN_SOURCE_ID ->
|
||||
if (it.first != PervEdenGalleryMetadata::class)
|
||||
return@any false
|
||||
NHENTAI_SOURCE_ID ->
|
||||
if (it.first != NHentaiMetadata::class)
|
||||
return@any false
|
||||
}
|
||||
realm.queryMetadataFromManga(manga.manga, it.second.where()).count() > 0
|
||||
}
|
||||
} else {
|
||||
manga.filter(searchText)
|
||||
}
|
||||
// <-- EH
|
||||
}
|
||||
}
|
||||
}.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
updateDataSet(it)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -39,10 +39,12 @@ import eu.kanade.tachiyomi.widget.DrawerSwipeCloseListener
|
||||
import kotlinx.android.synthetic.main.main_activity.*
|
||||
import kotlinx.android.synthetic.main.library_controller.view.*
|
||||
import rx.Subscription
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import timber.log.Timber
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.io.IOException
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
||||
class LibraryController(
|
||||
@@ -342,7 +344,11 @@ class LibraryController(
|
||||
// Mutate the filter icon because it needs to be tinted and the resource is shared.
|
||||
menu.findItem(R.id.action_filter).icon.mutate()
|
||||
|
||||
searchView.queryTextChanges().subscribeUntilDestroy {
|
||||
// Debounce search (EH)
|
||||
searchView.queryTextChanges()
|
||||
.debounce(200, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeUntilDestroy {
|
||||
query = it.toString()
|
||||
searchRelay.call(query)
|
||||
}
|
||||
|
||||
@@ -12,18 +12,10 @@ import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.util.inflate
|
||||
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
|
||||
import exh.isLewdSource
|
||||
import exh.metadata.MetadataHelper
|
||||
import exh.search.SearchEngine
|
||||
import kotlinx.android.synthetic.main.catalogue_grid_item.view.*
|
||||
|
||||
class LibraryItem(val manga: Manga) : AbstractFlexibleItem<LibraryHolder>(), IFilterable {
|
||||
|
||||
// --> EH
|
||||
private val searchEngine = SearchEngine()
|
||||
private val metadataHelper = MetadataHelper()
|
||||
// <-- EH
|
||||
|
||||
override fun getLayoutRes(): Int {
|
||||
return R.layout.catalogue_grid_item
|
||||
}
|
||||
@@ -61,15 +53,6 @@ class LibraryItem(val manga: Manga) : AbstractFlexibleItem<LibraryHolder>(), IFi
|
||||
* @return true if the manga should be included, false otherwise.
|
||||
*/
|
||||
override fun filter(constraint: String): Boolean {
|
||||
// --> EH
|
||||
if(!isLewdSource(manga.source)) {
|
||||
//Use gallery search engine for EH manga
|
||||
metadataHelper.fetchMetadata(manga.url, manga.source)?.let {
|
||||
return searchEngine.matches(it, searchEngine.parseQuery(constraint))
|
||||
}
|
||||
}
|
||||
// <-- EH
|
||||
|
||||
return manga.title.contains(constraint, true) ||
|
||||
(manga.author?.contains(constraint, true) ?: false)
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import com.bluelinelabs.conductor.changehandler.FadeChangeHandler
|
||||
import eu.kanade.tachiyomi.Migrations
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.NoToolbarElevationController
|
||||
@@ -35,6 +36,7 @@ import exh.ui.lock.LockChangeHandler
|
||||
import exh.ui.lock.LockController
|
||||
import exh.ui.lock.lockEnabled
|
||||
import exh.ui.lock.notifyLockSecurity
|
||||
import exh.ui.migration.MetadataFetchDialog
|
||||
import kotlinx.android.synthetic.main.main_activity.*
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
@@ -165,6 +167,10 @@ class MainActivity : BaseActivity() {
|
||||
if (Migrations.upgrade(preferences)) {
|
||||
ChangelogDialogController().showDialog(router)
|
||||
}
|
||||
|
||||
// Migrate metadata to Realm (EH)
|
||||
if(!preferences.migrateLibraryAsked2().getOrDefault())
|
||||
MetadataFetchDialog().askMigration(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user