Convert SY specific usages of Gson to Kotlin Serialization

Cleanup saved searches a bit
Cleanup json parsing
This commit is contained in:
Jobobby04 2020-10-12 14:20:54 -04:00
parent bbfce97125
commit f3365cef67
46 changed files with 490 additions and 366 deletions

View File

@ -2,20 +2,16 @@ package eu.kanade.tachiyomi.data.backup
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import com.github.salomonbrys.kotson.array
import com.github.salomonbrys.kotson.fromJson import com.github.salomonbrys.kotson.fromJson
import com.github.salomonbrys.kotson.jsonObject import com.github.salomonbrys.kotson.jsonObject
import com.github.salomonbrys.kotson.obj
import com.github.salomonbrys.kotson.registerTypeAdapter import com.github.salomonbrys.kotson.registerTypeAdapter
import com.github.salomonbrys.kotson.registerTypeHierarchyAdapter import com.github.salomonbrys.kotson.registerTypeHierarchyAdapter
import com.github.salomonbrys.kotson.set import com.github.salomonbrys.kotson.set
import com.github.salomonbrys.kotson.string
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.GsonBuilder import com.google.gson.GsonBuilder
import com.google.gson.JsonArray import com.google.gson.JsonArray
import com.google.gson.JsonElement import com.google.gson.JsonElement
import com.google.gson.JsonObject import com.google.gson.JsonObject
import com.google.gson.JsonParser
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.backup.BackupCreateService.Companion.BACKUP_CATEGORY import eu.kanade.tachiyomi.data.backup.BackupCreateService.Companion.BACKUP_CATEGORY
@ -62,13 +58,16 @@ 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.source.online.all.MergedSource import eu.kanade.tachiyomi.source.online.all.MergedSource
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import exh.EXHSavedSearch
import exh.MERGED_SOURCE_ID import exh.MERGED_SOURCE_ID
import exh.eh.EHentaiThrottleManager import exh.eh.EHentaiThrottleManager
import exh.merged.sql.models.MergedMangaReference import exh.merged.sql.models.MergedMangaReference
import exh.savedsearches.EXHSavedSearch
import exh.savedsearches.JsonSavedSearch
import exh.util.asObservable import exh.util.asObservable
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import rx.Observable import rx.Observable
import timber.log.Timber import timber.log.Timber
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
@ -553,17 +552,17 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) {
val newSavedSearches = backupSavedSearches.mapNotNull { val newSavedSearches = backupSavedSearches.mapNotNull {
try { try {
val id = it.substringBefore(':').toLong() val id = it.substringBefore(':').toLong()
val content = JsonParser.parseString(it.substringAfter(':')).obj val content = Json.decodeFromString<JsonSavedSearch>(it.substringAfter(':'))
val source = sourceManager.getOrStub(id) val source = sourceManager.getOrStub(id)
if (source !is CatalogueSource) return@mapNotNull null if (source !is CatalogueSource) return@mapNotNull null
val originalFilters = source.getFilterList() val originalFilters = source.getFilterList()
filterSerializer.deserialize(originalFilters, content["filters"].array) filterSerializer.deserialize(originalFilters, content.filters)
Pair( Pair(
id, id,
EXHSavedSearch( EXHSavedSearch(
content["name"].string, content.name,
content["query"].string, content.query,
originalFilters originalFilters
) )
) )
@ -580,18 +579,18 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) {
newSavedSearches += preferences.eh_savedSearches().get().mapNotNull { newSavedSearches += preferences.eh_savedSearches().get().mapNotNull {
try { try {
val id = it.substringBefore(':').toLong() val id = it.substringBefore(':').toLong()
val content = JsonParser.parseString(it.substringAfter(':')).obj val content = Json.decodeFromString<JsonSavedSearch>(it.substringAfter(':'))
if (id !in currentSources) return@mapNotNull null if (id !in currentSources) return@mapNotNull null
val source = sourceManager.getOrStub(id) val source = sourceManager.getOrStub(id)
if (source !is CatalogueSource) return@mapNotNull null if (source !is CatalogueSource) return@mapNotNull null
val originalFilters = source.getFilterList() val originalFilters = source.getFilterList()
filterSerializer.deserialize(originalFilters, content["filters"].array) filterSerializer.deserialize(originalFilters, content.filters)
Pair( Pair(
id, id,
EXHSavedSearch( EXHSavedSearch(
content["name"].string, content.name,
content["query"].string, content.query,
originalFilters originalFilters
) )
) )

View File

@ -1,14 +1,19 @@
package eu.kanade.tachiyomi.data.library package eu.kanade.tachiyomi.data.library
import android.content.Context import android.content.Context
import com.github.salomonbrys.kotson.nullLong
import com.github.salomonbrys.kotson.nullString
import com.github.salomonbrys.kotson.set
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.JsonObject
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaImpl import eu.kanade.tachiyomi.data.database.models.MangaImpl
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.encodeToJsonElement
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.longOrNull
import java.io.File import java.io.File
import java.util.Scanner import java.util.Scanner
@ -28,25 +33,24 @@ class CustomMangaManager(val context: Context) {
if (!editJson.exists() || !editJson.isFile) return if (!editJson.exists() || !editJson.isFile) return
val json = try { val json = try {
Gson().fromJson( Json.decodeFromString<JsonObject>(
Scanner(editJson).useDelimiter("\\Z").next(), Scanner(editJson).useDelimiter("\\Z").next()
JsonObject::class.java
) )
} catch (e: Exception) { } catch (e: Exception) {
null null
} ?: return } ?: return
val mangasJson = json.get("mangas").asJsonArray ?: return val mangasJson = json["mangas"] as? JsonArray ?: return
customMangaMap = mangasJson.mapNotNull { element -> customMangaMap = mangasJson.mapNotNull { element ->
val mangaObject = element.asJsonObject ?: return@mapNotNull null val mangaObject = element as? JsonObject ?: return@mapNotNull null
val id = mangaObject["id"]?.nullLong ?: return@mapNotNull null val id = mangaObject["id"]?.jsonPrimitive?.longOrNull ?: return@mapNotNull null
val manga = MangaImpl().apply { val manga = MangaImpl().apply {
this.id = id this.id = id
title = mangaObject["title"]?.nullString ?: "" title = mangaObject["title"]?.jsonPrimitive?.contentOrNull ?: ""
author = mangaObject["author"]?.nullString author = mangaObject["author"]?.jsonPrimitive?.contentOrNull
artist = mangaObject["artist"]?.nullString artist = mangaObject["artist"]?.jsonPrimitive?.contentOrNull
description = mangaObject["description"]?.nullString description = mangaObject["description"]?.jsonPrimitive?.contentOrNull
genre = mangaObject["genre"]?.asJsonArray?.mapNotNull { it.nullString } genre = (mangaObject["genre"] as? JsonArray)?.mapNotNull { it.jsonPrimitive.contentOrNull }
?.joinToString(", ") ?.joinToString(", ")
} }
id to manga id to manga
@ -72,17 +76,16 @@ class CustomMangaManager(val context: Context) {
private fun saveCustomInfo() { private fun saveCustomInfo() {
val jsonElements = customMangaMap.values.map { it.toJson() } val jsonElements = customMangaMap.values.map { it.toJson() }
if (jsonElements.isNotEmpty()) { if (jsonElements.isNotEmpty()) {
val gson = GsonBuilder().create() val mangaEntries = Json.encodeToJsonElement(jsonElements)
val root = JsonObject() val root = buildJsonObject {
val mangaEntries = gson.toJsonTree(jsonElements) put("mangas", mangaEntries)
}
root["mangas"] = mangaEntries
editJson.delete() editJson.delete()
editJson.writeText(gson.toJson(root)) editJson.writeText(Json.encodeToString(root))
} }
} }
fun Manga.toJson(): MangaJson { private fun Manga.toJson(): MangaJson {
return MangaJson( return MangaJson(
id!!, id!!,
title, title,
@ -93,6 +96,7 @@ class CustomMangaManager(val context: Context) {
) )
} }
@Serializable
data class MangaJson( data class MangaJson(
val id: Long, val id: Long,
val title: String? = null, val title: String? = null,

View File

@ -1,8 +1,6 @@
package eu.kanade.tachiyomi.extension.api package eu.kanade.tachiyomi.extension.api
import android.content.Context import android.content.Context
import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.int
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.extension.model.Extension import eu.kanade.tachiyomi.extension.model.Extension
import eu.kanade.tachiyomi.extension.model.LoadResult import eu.kanade.tachiyomi.extension.model.LoadResult

View File

@ -1,7 +1,6 @@
package eu.kanade.tachiyomi.source package eu.kanade.tachiyomi.source
import android.content.Context import android.content.Context
import com.google.gson.GsonBuilder
import com.google.gson.JsonParser import com.google.gson.JsonParser
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
@ -18,6 +17,9 @@ import eu.kanade.tachiyomi.util.storage.EpubFile
import eu.kanade.tachiyomi.util.system.ImageUtil import eu.kanade.tachiyomi.util.system.ImageUtil
import junrar.Archive import junrar.Archive
import junrar.rarfile.FileHeader import junrar.rarfile.FileHeader
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import rx.Observable import rx.Observable
import timber.log.Timber import timber.log.Timber
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
@ -154,16 +156,19 @@ class LocalSource(private val context: Context) : CatalogueSource {
val directory = getBaseDirectories(context).mapNotNull { File(it, manga.url) }.find { val directory = getBaseDirectories(context).mapNotNull { File(it, manga.url) }.find {
it.exists() it.exists()
} ?: return } ?: return
val gson = GsonBuilder().setPrettyPrinting().create() val json = Json {
prettyPrint = true
}
val existingFileName = directory.listFiles()?.find { it.extension == "json" }?.name val existingFileName = directory.listFiles()?.find { it.extension == "json" }?.name
val file = File(directory, existingFileName ?: "info.json") val file = File(directory, existingFileName ?: "info.json")
file.writeText(gson.toJson(manga.toJson())) file.writeText(json.encodeToString(manga.toJson()))
} }
fun SManga.toJson(): MangaJson { private fun SManga.toJson(): MangaJson {
return MangaJson(title, author, artist, description, genre?.split(", ")?.toTypedArray()) return MangaJson(title, author, artist, description, genre?.split(", ")?.toTypedArray())
} }
@Serializable
data class MangaJson( data class MangaJson(
val title: String, val title: String,
val author: String?, val author: String?,

View File

@ -3,15 +3,6 @@ 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.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.JsonObject
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.annoations.Nsfw import eu.kanade.tachiyomi.annoations.Nsfw
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
@ -57,6 +48,17 @@ import exh.util.trimAll
import exh.util.trimOrNull import exh.util.trimOrNull
import exh.util.urlImportFetchSearchManga import exh.util.urlImportFetchSearchManga
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.add
import kotlinx.serialization.json.buildJsonArray
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.put
import okhttp3.CacheControl import okhttp3.CacheControl
import okhttp3.CookieJar import okhttp3.CookieJar
import okhttp3.Headers import okhttp3.Headers
@ -938,29 +940,33 @@ class EHentai(
val gallery = lastSplit.first() val gallery = lastSplit.first()
val pageToken = uri.pathSegments.elementAt(1) val pageToken = uri.pathSegments.elementAt(1)
val json = JsonObject() val json = buildJsonObject {
json["method"] = "gtoken" put("method", "gtoken")
json["pagelist"] = JsonArray().apply { put(
add( "pagelist",
JsonArray().apply { buildJsonArray {
add(gallery.toInt()) add(
add(pageToken) buildJsonArray {
add(pageNum.toInt()) add(gallery.toInt())
add(pageToken)
add(pageNum.toInt())
}
)
} }
) )
} }
val outJson = JsonParser.parseString( val outJson = Json.decodeFromString<JsonObject>(
client.newCall( client.newCall(
Request.Builder() Request.Builder()
.url(EH_API_BASE) .url(EH_API_BASE)
.post(json.toString().toRequestBody(JSON)) .post(json.toString().toRequestBody(JSON))
.build() .build()
).execute().body!!.string() ).execute().body!!.string()
).obj )
val obj = outJson["tokenlist"].array.first() val obj = outJson["tokenlist"]!!.jsonArray.first().jsonObject
return "${uri.scheme}://${uri.host}/g/${obj["gid"].int}/${obj["token"].string}/" return "${uri.scheme}://${uri.host}/g/${obj["gid"]!!.jsonPrimitive.int}/${obj["token"]!!.jsonPrimitive.content}/"
} }
override fun getDescriptionAdapter(controller: MangaController): EHentaiDescriptionAdapter { override fun getDescriptionAdapter(controller: MangaController): EHentaiDescriptionAdapter {

View File

@ -68,8 +68,8 @@ class Hitomi(delegate: HttpSource, val context: Context) :
tags += RaisedTag("group", group!!, RaisedSearchMetadata.TAG_TYPE_VIRTUAL) tags += RaisedTag("group", group!!, RaisedSearchMetadata.TAG_TYPE_VIRTUAL)
} }
"type" -> { "type" -> {
type = content.text() genre = content.text()
tags += RaisedTag("type", type!!, RaisedSearchMetadata.TAG_TYPE_VIRTUAL) tags += RaisedTag("type", genre!!, RaisedSearchMetadata.TAG_TYPE_VIRTUAL)
} }
"series" -> { "series" -> {
series = content.select("a").map { it.text() } series = content.select("a").map { it.text() }

View File

@ -2,12 +2,7 @@ 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.get import com.elvishew.xlog.XLog
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 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.MangasPage
@ -23,6 +18,10 @@ import exh.metadata.metadata.base.RaisedTag
import exh.source.DelegatedHttpSource import exh.source.DelegatedHttpSource
import exh.ui.metadata.adapters.NHentaiDescriptionAdapter import exh.ui.metadata.adapters.NHentaiDescriptionAdapter
import exh.util.urlImportFetchSearchManga import exh.util.urlImportFetchSearchManga
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.Response import okhttp3.Response
import rx.Observable import rx.Observable
@ -58,41 +57,40 @@ class NHentai(delegate: HttpSource, val context: Context) :
val json = GALLERY_JSON_REGEX.find(input.body!!.string())!!.groupValues[1].replace( val json = GALLERY_JSON_REGEX.find(input.body!!.string())!!.groupValues[1].replace(
UNICODE_ESCAPE_REGEX UNICODE_ESCAPE_REGEX
) { it.groupValues[1].toInt(radix = 16).toChar().toString() } ) { it.groupValues[1].toInt(radix = 16).toChar().toString() }
val obj = JsonParser.parseString(json).asJsonObject val jsonResponse = jsonParser.decodeFromString<JsonResponse>(json)
with(metadata) { with(metadata) {
nhId = obj["id"].asLong nhId = jsonResponse.id
uploadDate = obj["upload_date"].nullLong uploadDate = jsonResponse.uploadDate
favoritesCount = obj["num_favorites"].nullLong favoritesCount = jsonResponse.numFavorites
mediaId = obj["media_id"].nullString mediaId = jsonResponse.mediaId
obj["title"].nullObj?.let { title -> jsonResponse.title?.let { title ->
japaneseTitle = title["japanese"].nullString japaneseTitle = title.japanese
shortTitle = title["pretty"].nullString shortTitle = title.pretty
englishTitle = title["english"].nullString englishTitle = title.english
} }
obj["images"].nullObj?.let { images -> jsonResponse.images?.let { images ->
coverImageType = images["cover"]?.get("t").nullString coverImageType = images.cover?.type
images["pages"].nullArray?.mapNotNull { images.pages.mapNotNull {
it?.asJsonObject?.get("t").nullString it.type
}?.let { }.let {
pageImageTypes = it pageImageTypes = it
} }
thumbnailImageType = images["thumbnail"]?.get("t").nullString thumbnailImageType = images.thumbnail?.type
} }
scanlator = obj["scanlator"].nullString scanlator = jsonResponse.scanlator
obj["tags"]?.asJsonArray?.map { jsonResponse.tags.map {
val asObj = it.asJsonObject it.type to it.name
Pair(asObj["type"].nullString, asObj["name"].nullString) }.apply {
}?.apply {
tags.clear() tags.clear()
}?.forEach { }.forEach {
if (it.first != null && it.second != null) { if (it.first != null && it.second != null) {
tags.add(RaisedTag(it.first!!, it.second!!, if (it.first == "category") RaisedSearchMetadata.TAG_TYPE_VIRTUAL else NHentaiSearchMetadata.TAG_TYPE_DEFAULT)) tags.add(RaisedTag(it.first!!, it.second!!, if (it.first == "category") RaisedSearchMetadata.TAG_TYPE_VIRTUAL else NHentaiSearchMetadata.TAG_TYPE_DEFAULT))
} }
@ -100,6 +98,49 @@ class NHentai(delegate: HttpSource, val context: Context) :
} }
} }
@Serializable
data class JsonResponse(
val id: Long,
@SerialName("media_id") val mediaId: String? = null,
val title: JsonTitle? = null,
val images: JsonImages? = null,
val scanlator: String? = null,
@SerialName("upload_date") val uploadDate: Long? = null,
val tags: List<JsonTag> = emptyList(),
@SerialName("num_pages") val numPages: Int? = null,
@SerialName("num_favorites") val numFavorites: Long? = null
)
@Serializable
data class JsonTitle(
val english: String? = null,
val japanese: String? = null,
val pretty: String? = null
)
@Serializable
data class JsonImages(
val pages: List<JsonPage> = emptyList(),
val cover: JsonPage? = null,
val thumbnail: JsonPage? = null
)
@Serializable
data class JsonPage(
@SerialName("t") val type: String? = null,
@SerialName("w") val width: String? = null,
@SerialName("h") val height: String? = null
)
@Serializable
data class JsonTag(
val id: Long? = null,
val type: String? = null,
val name: String? = null,
val url: String? = null,
val count: Long? = null
)
override fun toString() = "$name (${lang.toUpperCase()})" override fun toString() = "$name (${lang.toUpperCase()})"
override fun ensureDelegateCompatible() { override fun ensureDelegateCompatible() {
@ -127,6 +168,10 @@ class NHentai(delegate: HttpSource, val context: Context) :
companion object { companion object {
const val otherId = 7309872737163460316L const val otherId = 7309872737163460316L
private val jsonParser = Json {
ignoreUnknownKeys = true
}
private val GALLERY_JSON_REGEX = Regex(".parse\\(\"(.*)\"\\);") private val GALLERY_JSON_REGEX = Regex(".parse\\(\"(.*)\"\\);")
private val UNICODE_ESCAPE_REGEX = Regex("\\\\u([0-9a-fA-F]{4})") private val UNICODE_ESCAPE_REGEX = Regex("\\\\u([0-9a-fA-F]{4})")
} }

View File

@ -96,7 +96,7 @@ class PervEden(delegate: HttpSource, val context: Context) :
if (it is TextNode) { if (it is TextNode) {
val text = it.text().trim() val text = it.text().trim()
if (!text.isBlank()) { if (!text.isBlank()) {
type = text genre = text
} }
} }
} }

View File

@ -53,8 +53,8 @@ import eu.kanade.tachiyomi.util.view.shrinkOnScroll
import eu.kanade.tachiyomi.util.view.snack import eu.kanade.tachiyomi.util.view.snack
import eu.kanade.tachiyomi.widget.AutofitRecyclerView import eu.kanade.tachiyomi.widget.AutofitRecyclerView
import eu.kanade.tachiyomi.widget.EmptyView import eu.kanade.tachiyomi.widget.EmptyView
import exh.EXHSavedSearch
import exh.isEhBasedSource import exh.isEhBasedSource
import exh.savedsearches.EXHSavedSearch
import exh.source.EnhancedHttpSource.Companion.getMainSource import exh.source.EnhancedHttpSource.Companion.getMainSource
import kotlinx.android.parcel.Parcelize import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.main_activity.root_coordinator import kotlinx.android.synthetic.main.main_activity.root_coordinator

View File

@ -1,11 +1,6 @@
package eu.kanade.tachiyomi.ui.browse.source.browse package eu.kanade.tachiyomi.ui.browse.source.browse
import android.os.Bundle import android.os.Bundle
import com.github.salomonbrys.kotson.array
import com.github.salomonbrys.kotson.jsonObject
import com.github.salomonbrys.kotson.obj
import com.github.salomonbrys.kotson.string
import com.google.gson.JsonParser
import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.flexibleadapter.items.IFlexible
import eu.davidea.flexibleadapter.items.ISectionable import eu.davidea.flexibleadapter.items.ISectionable
import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.cache.CoverCache
@ -38,7 +33,14 @@ import eu.kanade.tachiyomi.ui.browse.source.filter.TriStateItem
import eu.kanade.tachiyomi.ui.browse.source.filter.TriStateSectionItem import eu.kanade.tachiyomi.ui.browse.source.filter.TriStateSectionItem
import eu.kanade.tachiyomi.util.chapter.ChapterSettingsHelper import eu.kanade.tachiyomi.util.chapter.ChapterSettingsHelper
import eu.kanade.tachiyomi.util.removeCovers import eu.kanade.tachiyomi.util.removeCovers
import exh.EXHSavedSearch import exh.savedsearches.EXHSavedSearch
import exh.savedsearches.JsonSavedSearch
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.put
import rx.Observable import rx.Observable
import rx.Subscription import rx.Subscription
import rx.android.schedulers.AndroidSchedulers import rx.android.schedulers.AndroidSchedulers
@ -134,8 +136,8 @@ open class BrowseSourcePresenter(
// SY --> // SY -->
if (filters != null) { if (filters != null) {
val filters = JsonParser.parseString(filters).obj val filters = Json.decodeFromString<JsonObject>(filters)
filterSerializer.deserialize(sourceFilters, filters["filters"].array) filterSerializer.deserialize(sourceFilters, filters["filters"]!!.jsonArray)
} }
val allDefault = sourceFilters == source.getFilterList() val allDefault = sourceFilters == source.getFilterList()
// SY <-- // SY <--
@ -446,11 +448,11 @@ open class BrowseSourcePresenter(
!it.startsWith("${source.id}:") !it.startsWith("${source.id}:")
} }
val newSerialized = searches.map { val newSerialized = searches.map {
"${source.id}:" + jsonObject( "${source.id}:" + buildJsonObject {
"name" to it.name, put("name", it.name)
"query" to it.query, put("query", it.query)
"filters" to filterSerializer.serialize(it.filterList) put("filters", filterSerializer.serialize(it.filterList))
).toString() }.toString()
} }
prefs.eh_savedSearches().set((otherSerialized + newSerialized).toSet()) prefs.eh_savedSearches().set((otherSerialized + newSerialized).toSet())
} }
@ -461,12 +463,12 @@ open class BrowseSourcePresenter(
try { try {
val id = it.substringBefore(':').toLong() val id = it.substringBefore(':').toLong()
if (id != source.id) return@map null if (id != source.id) return@map null
val content = JsonParser.parseString(it.substringAfter(':')).obj val content = Json.decodeFromString<JsonSavedSearch>(it.substringAfter(':'))
val originalFilters = source.getFilterList() val originalFilters = source.getFilterList()
filterSerializer.deserialize(originalFilters, content["filters"].array) filterSerializer.deserialize(originalFilters, content.filters)
EXHSavedSearch( EXHSavedSearch(
content["name"].string, content.name,
content["query"].string, content.query,
originalFilters originalFilters
) )
} catch (t: RuntimeException) { } catch (t: RuntimeException) {

View File

@ -1,16 +1,5 @@
package eu.kanade.tachiyomi.ui.browse.source.browse package eu.kanade.tachiyomi.ui.browse.source.browse
import com.github.salomonbrys.kotson.array
import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.jsonObject
import com.github.salomonbrys.kotson.nullArray
import com.github.salomonbrys.kotson.nullObj
import com.github.salomonbrys.kotson.nullString
import com.github.salomonbrys.kotson.obj
import com.github.salomonbrys.kotson.string
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.MangasPage
@ -22,6 +11,15 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.put
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
@ -73,17 +71,18 @@ class MyAnimeList() : API("https://api.jikan.moe/v3/") {
if (body.isEmpty()) { if (body.isEmpty()) {
throw Exception("Null Response") throw Exception("Null Response")
} }
val data = JsonParser.parseString(body).obj val data = Json.decodeFromString<JsonObject>(body)
val recommendations = data["recommendations"].nullArray val recommendations = data["recommendations"] as? JsonArray
?: throw Exception("Unexpected response") ?: throw Exception("Unexpected response")
val recs = recommendations.map { rec -> val recs = recommendations.map { rec ->
rec as? JsonObject ?: throw Exception("Invalid json")
Timber.tag("RECOMMENDATIONS") Timber.tag("RECOMMENDATIONS")
.d("MYANIMELIST > FOUND RECOMMENDATION > %s", rec["title"].string) .d("MYANIMELIST > FOUND RECOMMENDATION > %s", rec["title"]!!.jsonPrimitive.content)
SMangaImpl().apply { SMangaImpl().apply {
this.title = rec["title"].string this.title = rec["title"]!!.jsonPrimitive.content
this.thumbnail_url = rec["image_url"].string this.thumbnail_url = rec["image_url"]!!.jsonPrimitive.content
this.initialized = true this.initialized = true
this.url = rec["url"].string this.url = rec["url"]!!.jsonPrimitive.content
} }
} }
callback.invoke(recs, null) callback.invoke(recs, null)
@ -121,15 +120,15 @@ class MyAnimeList() : API("https://api.jikan.moe/v3/") {
if (body.isEmpty()) { if (body.isEmpty()) {
throw Exception("Null Response") throw Exception("Null Response")
} }
val data = JsonParser.parseString(body).obj val data = Json.decodeFromString<JsonObject>(body)
val results = data["results"].nullArray ?: throw Exception("Unexpected response") val results = data["results"] as? JsonArray ?: throw Exception("Unexpected response")
if (results.size() <= 0) { if (results.size <= 0) {
throw Exception("'$search' not found") throw Exception("'$search' not found")
} }
val result = results.first().obj val result = results.first().jsonObject
Timber.tag("RECOMMENDATIONS") Timber.tag("RECOMMENDATIONS")
.d("MYANIMELIST > FOUND TITLE > %s", result["title"].string) .d("MYANIMELIST > FOUND TITLE > %s", result["title"]!!.jsonPrimitive.content)
val id = result["mal_id"].string val id = result["mal_id"]!!.jsonPrimitive.content
getRecsById(id, callback) getRecsById(id, callback)
} }
} }
@ -138,19 +137,21 @@ class MyAnimeList() : API("https://api.jikan.moe/v3/") {
class Anilist() : API("https://graphql.anilist.co/") { class Anilist() : API("https://graphql.anilist.co/") {
private fun countOccurrence(arr: JsonArray, search: String): Int { private fun countOccurrence(arr: JsonArray, search: String): Int {
return arr.count { return arr.count {
val synonym = it.string val synonym = it.jsonPrimitive.content
synonym.contains(search, true) synonym.contains(search, true)
} }
} }
private fun languageContains(obj: JsonObject, language: String, search: String): Boolean { private fun languageContains(obj: JsonObject, language: String, search: String): Boolean {
return obj["title"].obj[language].nullString?.contains(search, true) == true return obj["title"]?.jsonObject?.get(language)?.jsonPrimitive?.content?.contains(search, true) == true
} }
private fun getTitle(obj: JsonObject): String { private fun getTitle(obj: JsonObject): String {
return obj["title"].obj["romaji"].nullString return obj["title"]!!.jsonObject.let {
?: obj["title"].obj["english"].nullString it["romaji"]?.jsonPrimitive?.content
?: obj["title"].obj["native"].string ?: it["english"]?.jsonPrimitive?.content
?: it["native"]!!.jsonPrimitive.content
}
} }
override fun getRecsBySearch( override fun getRecsBySearch(
@ -189,11 +190,13 @@ class Anilist() : API("https://graphql.anilist.co/") {
|} |}
|} |}
|""".trimMargin() |""".trimMargin()
val variables = jsonObject("search" to search) val variables = buildJsonObject {
val payload = jsonObject( put("search", search)
"query" to query, }
"variables" to variables val payload = buildJsonObject {
) put("query", query)
put("variables", variables)
}
val payloadBody = val payloadBody =
payload.toString().toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull()) payload.toString().toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull())
val request = Request.Builder() val request = Request.Builder()
@ -211,33 +214,33 @@ class Anilist() : API("https://graphql.anilist.co/") {
if (body.isEmpty()) { if (body.isEmpty()) {
throw Exception("Null Response") throw Exception("Null Response")
} }
val data = JsonParser.parseString(body).obj["data"].nullObj val data = Json.decodeFromString<JsonObject>(body)["data"] as? JsonObject
?: throw Exception("Unexpected response") ?: throw Exception("Unexpected response")
val page = data["Page"].obj val page = data["Page"]!!.jsonObject
val media = page["media"].array val media = page["media"]!!.jsonArray
if (media.size() <= 0) { if (media.size <= 0) {
throw Exception("'$search' not found") throw Exception("'$search' not found")
} }
val result = media.sortedWith( val result = media.sortedWith(
compareBy( compareBy(
{ languageContains(it.obj, "romaji", search) }, { languageContains(it.jsonObject, "romaji", search) },
{ languageContains(it.obj, "english", search) }, { languageContains(it.jsonObject, "english", search) },
{ languageContains(it.obj, "native", search) }, { languageContains(it.jsonObject, "native", search) },
{ countOccurrence(it.obj["synonyms"].array, search) > 0 } { countOccurrence(it.jsonObject["synonyms"]!!.jsonArray, search) > 0 }
) )
).last().obj ).last().jsonObject
Timber.tag("RECOMMENDATIONS") Timber.tag("RECOMMENDATIONS")
.d("ANILIST > FOUND TITLE > %s", getTitle(result)) .d("ANILIST > FOUND TITLE > %s", getTitle(result))
val recommendations = result["recommendations"].obj["edges"].array val recommendations = result["recommendations"]!!.jsonObject["edges"]!!.jsonArray
val recs = recommendations.map { val recs = recommendations.map {
val rec = it["node"]["mediaRecommendation"].obj val rec = it.jsonObject["node"]!!.jsonObject["mediaRecommendation"]!!.jsonObject
Timber.tag("RECOMMENDATIONS") Timber.tag("RECOMMENDATIONS")
.d("ANILIST: FOUND RECOMMENDATION: %s", getTitle(rec)) .d("ANILIST: FOUND RECOMMENDATION: %s", getTitle(rec))
SMangaImpl().apply { SMangaImpl().apply {
this.title = getTitle(rec) this.title = getTitle(rec)
this.thumbnail_url = rec["coverImage"].obj["large"].string this.thumbnail_url = rec["coverImage"]!!.jsonObject["large"]!!.jsonPrimitive.content
this.initialized = true this.initialized = true
this.url = rec["siteUrl"].string this.url = rec["siteUrl"]!!.jsonPrimitive.content
} }
} }
callback.invoke(recs, null) callback.invoke(recs, null)

View File

@ -18,7 +18,7 @@ import eu.kanade.tachiyomi.databinding.SourceFilterSheetBinding
import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.online.BrowseSourceFilterHeader import eu.kanade.tachiyomi.source.online.BrowseSourceFilterHeader
import eu.kanade.tachiyomi.widget.SimpleNavigationView import eu.kanade.tachiyomi.widget.SimpleNavigationView
import exh.EXHSavedSearch import exh.savedsearches.EXHSavedSearch
import exh.source.EnhancedHttpSource.Companion.getMainSource import exh.source.EnhancedHttpSource.Companion.getMainSource
class SourceFilterSheet( class SourceFilterSheet(

View File

@ -11,7 +11,6 @@ import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.github.salomonbrys.kotson.jsonObject
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
@ -30,6 +29,7 @@ import exh.util.nullIfBlank
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.serialization.json.buildJsonObject
import reactivecircus.flowbinding.android.view.clicks import reactivecircus.flowbinding.android.view.clicks
import reactivecircus.flowbinding.appcompat.QueryTextEvent import reactivecircus.flowbinding.appcompat.QueryTextEvent
import reactivecircus.flowbinding.appcompat.queryTextEvents import reactivecircus.flowbinding.appcompat.queryTextEvents
@ -203,7 +203,7 @@ open class IndexController :
val allDefault = presenter.sourceFilters == presenter.source.getFilterList() val allDefault = presenter.sourceFilters == presenter.source.getFilterList()
filterSheet?.dismiss() filterSheet?.dismiss()
if (!allDefault) { if (!allDefault) {
val json = jsonObject("filters" to filterSerializer.serialize(presenter.sourceFilters)) val json = buildJsonObject { put("filters", filterSerializer.serialize(presenter.sourceFilters)) }
onBrowseClick(presenter.query.nullIfBlank(), json.toString()) onBrowseClick(presenter.query.nullIfBlank(), json.toString())
} }
}, },
@ -232,7 +232,7 @@ open class IndexController :
filterSheet?.dismiss() filterSheet?.dismiss()
if (!allDefault) { if (!allDefault) {
val json = jsonObject("filters" to filterSerializer.serialize(presenter.sourceFilters)) val json = buildJsonObject { put("filters", filterSerializer.serialize(presenter.sourceFilters)) }
onBrowseClick(presenter.query.nullIfBlank(), json.toString()) onBrowseClick(presenter.query.nullIfBlank(), json.toString())
} }
}, },

View File

@ -1,10 +1,6 @@
package eu.kanade.tachiyomi.ui.browse.source.index package eu.kanade.tachiyomi.ui.browse.source.index
import android.os.Bundle import android.os.Bundle
import com.github.salomonbrys.kotson.array
import com.github.salomonbrys.kotson.obj
import com.github.salomonbrys.kotson.string
import com.google.gson.JsonParser
import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.flexibleadapter.items.IFlexible
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
@ -15,7 +11,8 @@ import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.SManga 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.browse.source.browse.BrowseSourcePresenter.Companion.toItems import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourcePresenter.Companion.toItems
import exh.EXHSavedSearch import exh.savedsearches.EXHSavedSearch
import exh.savedsearches.JsonSavedSearch
import exh.util.asFlow import exh.util.asFlow
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -23,6 +20,8 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.singleOrNull import kotlinx.coroutines.flow.singleOrNull
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import rx.Observable import rx.Observable
import rx.Subscription import rx.Subscription
import rx.android.schedulers.AndroidSchedulers import rx.android.schedulers.AndroidSchedulers
@ -232,12 +231,12 @@ open class IndexPresenter(
try { try {
val id = it.substringBefore(':').toLong() val id = it.substringBefore(':').toLong()
if (id != source.id) return@map null if (id != source.id) return@map null
val content = JsonParser.parseString(it.substringAfter(':')).obj val content = Json.decodeFromString<JsonSavedSearch>(it.substringAfter(':'))
val originalFilters = source.getFilterList() val originalFilters = source.getFilterList()
filterSerializer.deserialize(originalFilters, content["filters"].array) filterSerializer.deserialize(originalFilters, content.filters)
EXHSavedSearch( EXHSavedSearch(
content["name"].string, content.name,
content["query"].string, content.query,
originalFilters originalFilters
) )
} catch (t: RuntimeException) { } catch (t: RuntimeException) {

View File

@ -12,8 +12,6 @@ import com.afollestad.materialdialogs.input.getInputField
import com.afollestad.materialdialogs.input.input import com.afollestad.materialdialogs.input.input
import com.bluelinelabs.conductor.RouterTransaction import com.bluelinelabs.conductor.RouterTransaction
import com.bluelinelabs.conductor.changehandler.FadeChangeHandler import com.bluelinelabs.conductor.changehandler.FadeChangeHandler
import com.github.salomonbrys.kotson.fromJson
import com.google.gson.Gson
import com.tfcporciuncula.flow.Preference import com.tfcporciuncula.flow.Preference
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.DatabaseHelper
@ -116,6 +114,8 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.take import kotlinx.coroutines.flow.take
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.util.Date import java.util.Date
import kotlin.time.Duration import kotlin.time.Duration
@ -128,7 +128,6 @@ import kotlin.time.hours
*/ */
class SettingsEhController : SettingsController() { class SettingsEhController : SettingsController() {
private val gson: Gson by injectLazy()
private val db: DatabaseHelper by injectLazy() private val db: DatabaseHelper by injectLazy()
private fun Preference<*>.reconfigure(): Boolean { private fun Preference<*>.reconfigure(): Boolean {
@ -687,7 +686,7 @@ class SettingsEhController : SettingsController() {
val updateInfo = try { val updateInfo = try {
val stats = val stats =
preferences.eh_autoUpdateStats().get().nullIfBlank()?.let { preferences.eh_autoUpdateStats().get().nullIfBlank()?.let {
gson.fromJson<EHentaiUpdaterStats>(it) Json.decodeFromString<EHentaiUpdaterStats>(it)
} }
val statsText = if (stats != null) { val statsText = if (stats != null) {

View File

@ -2,9 +2,6 @@ package exh
import android.content.Context import android.content.Context
import com.elvishew.xlog.XLog import com.elvishew.xlog.XLog
import com.github.salomonbrys.kotson.fromJson
import com.google.gson.Gson
import com.google.gson.annotations.SerializedName
import com.pushtorefresh.storio.sqlite.queries.Query import com.pushtorefresh.storio.sqlite.queries.Query
import com.pushtorefresh.storio.sqlite.queries.RawQuery import com.pushtorefresh.storio.sqlite.queries.RawQuery
import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.BuildConfig
@ -28,6 +25,10 @@ import eu.kanade.tachiyomi.source.online.all.Hitomi
import eu.kanade.tachiyomi.source.online.all.NHentai import eu.kanade.tachiyomi.source.online.all.NHentai
import exh.merged.sql.models.MergedMangaReference import exh.merged.sql.models.MergedMangaReference
import exh.source.BlacklistedSources import exh.source.BlacklistedSources
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.io.File import java.io.File
import java.net.URI import java.net.URI
@ -36,7 +37,6 @@ import java.net.URISyntaxException
object EXHMigrations { object EXHMigrations {
private val db: DatabaseHelper by injectLazy() private val db: DatabaseHelper by injectLazy()
private val sourceManager: SourceManager by injectLazy() private val sourceManager: SourceManager by injectLazy()
private val gson: Gson by injectLazy()
private val logger = XLog.tag("EXHMigrations") private val logger = XLog.tag("EXHMigrations")
@ -167,7 +167,7 @@ object EXHMigrations {
.executeAsBlocking() .executeAsBlocking()
if (mergedMangas.isNotEmpty()) { if (mergedMangas.isNotEmpty()) {
val mangaConfigs = mergedMangas.mapNotNull { mergedManga -> readMangaConfig(mergedManga, gson)?.let { mergedManga to it } } val mangaConfigs = mergedMangas.mapNotNull { mergedManga -> readMangaConfig(mergedManga)?.let { mergedManga to it } }
if (mangaConfigs.isNotEmpty()) { if (mangaConfigs.isNotEmpty()) {
val mangaToUpdate = mutableListOf<Manga>() val mangaToUpdate = mutableListOf<Manga>()
val mergedMangaReferences = mutableListOf<MergedMangaReference>() val mergedMangaReferences = mutableListOf<MergedMangaReference>()
@ -237,7 +237,7 @@ object EXHMigrations {
.prepare() .prepare()
.executeAsBlocking() .executeAsBlocking()
val mergedMangaChaptersMatched = mergedMangaChapters.mapNotNull { chapter -> loadedMangaList.firstOrNull { it.manga.id == chapter.id }?.let { it to chapter } } val mergedMangaChaptersMatched = mergedMangaChapters.mapNotNull { chapter -> loadedMangaList.firstOrNull { it.manga.id == chapter.id }?.let { it to chapter } }
val parsedChapters = chapters.filter { it.read || it.last_page_read != 0 }.mapNotNull { chapter -> readUrlConfig(chapter.url, gson)?.let { chapter to it } } val parsedChapters = chapters.filter { it.read || it.last_page_read != 0 }.mapNotNull { chapter -> readUrlConfig(chapter.url)?.let { chapter to it } }
val chaptersToUpdate = mutableListOf<Chapter>() val chaptersToUpdate = mutableListOf<Chapter>()
parsedChapters.forEach { parsedChapter -> parsedChapters.forEach { parsedChapter ->
mergedMangaChaptersMatched.firstOrNull { it.second.url == parsedChapter.second.url && it.first.source.id == parsedChapter.second.source && it.first.manga.url == parsedChapter.second.mangaUrl }?.let { mergedMangaChaptersMatched.firstOrNull { it.second.url == parsedChapter.second.url && it.first.source.id == parsedChapter.second.source && it.first.manga.url == parsedChapter.second.mangaUrl }?.let {
@ -339,23 +339,25 @@ object EXHMigrations {
} }
} }
@Serializable
private data class UrlConfig( private data class UrlConfig(
@SerializedName("s") @SerialName("s")
val source: Long, val source: Long,
@SerializedName("u") @SerialName("u")
val url: String, val url: String,
@SerializedName("m") @SerialName("m")
val mangaUrl: String val mangaUrl: String
) )
@Serializable
private data class MangaConfig( private data class MangaConfig(
@SerializedName("c") @SerialName("c")
val children: List<MangaSource> val children: List<MangaSource>
) { ) {
companion object { companion object {
fun readFromUrl(gson: Gson, url: String): MangaConfig? { fun readFromUrl(url: String): MangaConfig? {
return try { return try {
gson.fromJson(url) Json.decodeFromString(url)
} catch (e: Exception) { } catch (e: Exception) {
null null
} }
@ -363,14 +365,15 @@ object EXHMigrations {
} }
} }
private fun readMangaConfig(manga: SManga, gson: Gson): MangaConfig? { private fun readMangaConfig(manga: SManga): MangaConfig? {
return MangaConfig.readFromUrl(gson, manga.url) return MangaConfig.readFromUrl(manga.url)
} }
@Serializable
private data class MangaSource( private data class MangaSource(
@SerializedName("s") @SerialName("s")
val source: Long, val source: Long,
@SerializedName("u") @SerialName("u")
val url: String val url: String
) { ) {
fun load(db: DatabaseHelper, sourceManager: SourceManager): LoadedMangaSource? { fun load(db: DatabaseHelper, sourceManager: SourceManager): LoadedMangaSource? {
@ -380,9 +383,9 @@ object EXHMigrations {
} }
} }
private fun readUrlConfig(url: String, gson: Gson): UrlConfig? { private fun readUrlConfig(url: String): UrlConfig? {
return try { return try {
gson.fromJson(url) Json.decodeFromString(url)
} catch (e: Exception) { } catch (e: Exception) {
null null
} }

View File

@ -2,11 +2,6 @@ package exh.debug
import android.app.Application import android.app.Application
import com.elvishew.xlog.XLog import com.elvishew.xlog.XLog
import com.github.salomonbrys.kotson.array
import com.github.salomonbrys.kotson.jsonObject
import com.github.salomonbrys.kotson.obj
import com.github.salomonbrys.kotson.string
import com.google.gson.JsonParser
import com.pushtorefresh.storio.sqlite.queries.RawQuery import com.pushtorefresh.storio.sqlite.queries.RawQuery
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.tables.MangaTable import eu.kanade.tachiyomi.data.database.tables.MangaTable
@ -16,13 +11,13 @@ import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.SourceManager.Companion.currentDelegatedSources import eu.kanade.tachiyomi.source.SourceManager.Companion.currentDelegatedSources
import exh.EH_SOURCE_ID import exh.EH_SOURCE_ID
import exh.EXHMigrations import exh.EXHMigrations
import exh.EXHSavedSearch
import exh.EXH_SOURCE_ID import exh.EXH_SOURCE_ID
import exh.eh.EHentaiThrottleManager import exh.eh.EHentaiThrottleManager
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.savedsearches.JsonSavedSearch
import exh.util.await import exh.util.await
import exh.util.cancellable import exh.util.cancellable
import exh.util.jobScheduler import exh.util.jobScheduler
@ -31,8 +26,10 @@ 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.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import xyz.nulldev.ts.api.http.serializer.FilterSerializer
import java.lang.RuntimeException import java.lang.RuntimeException
@OptIn(FlowPreview::class) @OptIn(FlowPreview::class)
@ -236,22 +233,13 @@ object DebugFunctions {
fun copyEHentaiSavedSearchesToExhentai() { fun copyEHentaiSavedSearchesToExhentai() {
runBlocking { runBlocking {
val filterSerializer = FilterSerializer()
val source = sourceManager.get(EH_SOURCE_ID) as? CatalogueSource ?: return@runBlocking val source = sourceManager.get(EH_SOURCE_ID) as? CatalogueSource ?: return@runBlocking
val newSource = sourceManager.get(EXH_SOURCE_ID) as? CatalogueSource ?: return@runBlocking val newSource = sourceManager.get(EXH_SOURCE_ID) as? CatalogueSource ?: return@runBlocking
val savedSearches = prefs.eh_savedSearches().get().mapNotNull { val savedSearches = prefs.eh_savedSearches().get().mapNotNull {
try { try {
val id = it.substringBefore(':').toLong() val id = it.substringBefore(':').toLong()
if (id != source.id) return@mapNotNull null if (id != source.id) return@mapNotNull null
val content = JsonParser.parseString(it.substringAfter(':')).obj Json.decodeFromString<JsonSavedSearch>(it.substringAfter(':'))
val originalFilters = source.getFilterList()
filterSerializer.deserialize(originalFilters, content["filters"].array)
EXHSavedSearch(
content["name"].string,
content["query"].string,
originalFilters
)
} catch (t: RuntimeException) { } catch (t: RuntimeException) {
// Load failed // Load failed
XLog.e("Failed to load saved search!", t) XLog.e("Failed to load saved search!", t)
@ -263,15 +251,7 @@ object DebugFunctions {
try { try {
val id = it.substringBefore(':').toLong() val id = it.substringBefore(':').toLong()
if (id != newSource.id) return@mapNotNull null if (id != newSource.id) return@mapNotNull null
val content = JsonParser.parseString(it.substringAfter(':')).obj Json.decodeFromString<JsonSavedSearch>(it.substringAfter(':'))
val originalFilters = source.getFilterList()
filterSerializer.deserialize(originalFilters, content["filters"].array)
EXHSavedSearch(
content["name"].string,
content["query"].string,
originalFilters
)
} catch (t: RuntimeException) { } catch (t: RuntimeException) {
// Load failed // Load failed
XLog.e("Failed to load saved search!", t) XLog.e("Failed to load saved search!", t)
@ -284,11 +264,7 @@ object DebugFunctions {
!it.startsWith("${newSource.id}:") !it.startsWith("${newSource.id}:")
} }
val newSerialized = savedSearches.map { val newSerialized = savedSearches.map {
"${newSource.id}:" + jsonObject( "${newSource.id}:" + Json.encodeToString(it)
"name" to it.name,
"query" to it.query,
"filters" to filterSerializer.serialize(it.filterList)
).toString()
} }
prefs.eh_savedSearches().set((otherSerialized + newSerialized).toSet()) prefs.eh_savedSearches().set((otherSerialized + newSerialized).toSet())
} }
@ -296,22 +272,13 @@ object DebugFunctions {
fun copyExhentaiSavedSearchesToEHentai() { fun copyExhentaiSavedSearchesToEHentai() {
runBlocking { runBlocking {
val filterSerializer = FilterSerializer()
val source = sourceManager.get(EXH_SOURCE_ID) as? CatalogueSource ?: return@runBlocking val source = sourceManager.get(EXH_SOURCE_ID) as? CatalogueSource ?: return@runBlocking
val newSource = sourceManager.get(EH_SOURCE_ID) as? CatalogueSource ?: return@runBlocking val newSource = sourceManager.get(EH_SOURCE_ID) as? CatalogueSource ?: return@runBlocking
val savedSearches = prefs.eh_savedSearches().get().mapNotNull { val savedSearches = prefs.eh_savedSearches().get().mapNotNull {
try { try {
val id = it.substringBefore(':').toLong() val id = it.substringBefore(':').toLong()
if (id != source.id) return@mapNotNull null if (id != source.id) return@mapNotNull null
val content = JsonParser.parseString(it.substringAfter(':')).obj Json.decodeFromString<JsonSavedSearch>(it.substringAfter(':'))
val originalFilters = source.getFilterList()
filterSerializer.deserialize(originalFilters, content["filters"].array)
EXHSavedSearch(
content["name"].string,
content["query"].string,
originalFilters
)
} catch (t: RuntimeException) { } catch (t: RuntimeException) {
// Load failed // Load failed
XLog.e("Failed to load saved search!", t) XLog.e("Failed to load saved search!", t)
@ -323,15 +290,7 @@ object DebugFunctions {
try { try {
val id = it.substringBefore(':').toLong() val id = it.substringBefore(':').toLong()
if (id != newSource.id) return@mapNotNull null if (id != newSource.id) return@mapNotNull null
val content = JsonParser.parseString(it.substringAfter(':')).obj Json.decodeFromString<JsonSavedSearch>(it.substringAfter(':'))
val originalFilters = source.getFilterList()
filterSerializer.deserialize(originalFilters, content["filters"].array)
EXHSavedSearch(
content["name"].string,
content["query"].string,
originalFilters
)
} catch (t: RuntimeException) { } catch (t: RuntimeException) {
// Load failed // Load failed
XLog.e("Failed to load saved search!", t) XLog.e("Failed to load saved search!", t)
@ -344,11 +303,7 @@ object DebugFunctions {
!it.startsWith("${newSource.id}:") !it.startsWith("${newSource.id}:")
} }
val newSerialized = savedSearches.map { val newSerialized = savedSearches.map {
"${newSource.id}:" + jsonObject( "${newSource.id}:" + Json.encodeToString(it)
"name" to it.name,
"query" to it.query,
"filters" to filterSerializer.serialize(it.filterList)
).toString()
} }
prefs.eh_savedSearches().set((otherSerialized + newSerialized).toSet()) prefs.eh_savedSearches().set((otherSerialized + newSerialized).toSet())
} }

View File

@ -8,7 +8,6 @@ import android.content.ComponentName
import android.content.Context import android.content.Context
import android.os.Build import android.os.Build
import com.elvishew.xlog.XLog import com.elvishew.xlog.XLog
import com.google.gson.Gson
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Chapter import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
@ -38,6 +37,8 @@ import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.toList import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
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
@ -54,7 +55,6 @@ class EHentaiUpdateWorker : JobService(), CoroutineScope {
private val db: DatabaseHelper by injectLazy() private val db: DatabaseHelper by injectLazy()
private val prefs: PreferencesHelper by injectLazy() private val prefs: PreferencesHelper by injectLazy()
private val gson: Gson by injectLazy()
private val sourceManager: SourceManager by injectLazy() private val sourceManager: SourceManager by injectLazy()
private val updateHelper: EHentaiUpdateHelper by injectLazy() private val updateHelper: EHentaiUpdateHelper by injectLazy()
private val logger = XLog.tag("EHUpdater") private val logger = XLog.tag("EHUpdater")
@ -242,7 +242,7 @@ class EHentaiUpdateWorker : JobService(), CoroutineScope {
} }
} finally { } finally {
prefs.eh_autoUpdateStats().set( prefs.eh_autoUpdateStats().set(
gson.toJson( Json.encodeToString(
EHentaiUpdaterStats( EHentaiUpdaterStats(
startTime, startTime,
allMeta.size, allMeta.size,

View File

@ -1,5 +1,8 @@
package exh.eh package exh.eh
import kotlinx.serialization.Serializable
@Serializable
data class EHentaiUpdaterStats( data class EHentaiUpdaterStats(
val startTime: Long, val startTime: Long,
val possibleUpdates: Int, val possibleUpdates: Int,

View File

@ -1,20 +1,19 @@
package exh.log package exh.log
import com.elvishew.xlog.XLog import com.elvishew.xlog.XLog
import com.google.gson.Gson import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor import okhttp3.logging.HttpLoggingInterceptor
fun OkHttpClient.Builder.maybeInjectEHLogger(): OkHttpClient.Builder { fun OkHttpClient.Builder.maybeInjectEHLogger(): OkHttpClient.Builder {
if (EHLogLevel.shouldLog(EHLogLevel.EXTREME)) { if (EHLogLevel.shouldLog(EHLogLevel.EXTREME)) {
val logger: HttpLoggingInterceptor.Logger = object : HttpLoggingInterceptor.Logger { val logger: HttpLoggingInterceptor.Logger = HttpLoggingInterceptor.Logger { message ->
override fun log(message: String) { try {
try { Json.decodeFromString<Any>(message)
Gson().fromJson(message, Any::class.java) XLog.tag("||EH-NETWORK-JSON").nst().json(message)
XLog.tag("||EH-NETWORK-JSON").nst().json(message) } catch (ex: Exception) {
} catch (ex: Exception) { XLog.tag("||EH-NETWORK").nb().nst().d(message)
XLog.tag("||EH-NETWORK").nb().nst().d(message)
}
} }
} }
return addInterceptor(HttpLoggingInterceptor(logger).apply { level = HttpLoggingInterceptor.Level.BODY }) return addInterceptor(HttpLoggingInterceptor(logger).apply { level = HttpLoggingInterceptor.Level.BODY })

View File

@ -1,24 +1,27 @@
package exh.md.handlers package exh.md.handlers
import com.github.salomonbrys.kotson.string
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.Response import okhttp3.Response
import java.util.Date import java.util.Date
class ApiChapterParser { class ApiChapterParser {
fun pageListParse(response: Response): List<Page> { fun pageListParse(response: Response): List<Page> {
val jsonData = response.body!!.string() val jsonData = response.body!!.string()
val json = JsonParser.parseString(jsonData).asJsonObject val json = Json.decodeFromString<JsonObject>(jsonData)
val pages = mutableListOf<Page>() val pages = mutableListOf<Page>()
val hash = json.get("hash").string val hash = json["hash"]!!.jsonPrimitive.content
val pageArray = json.getAsJsonArray("page_array") val pageArray = json["page_array"]!!.jsonArray
val server = json.get("server").string val server = json["server"]!!.jsonPrimitive.content
pageArray.forEach { pageArray.forEach {
val url = "$hash/${it.asString}" val url = "$hash/${it.jsonPrimitive.content}"
pages.add(Page(pages.size, "$server,${response.request.url},${Date().time}", url)) pages.add(Page(pages.size, "$server,${response.request.url},${Date().time}", url))
} }
@ -27,8 +30,8 @@ class ApiChapterParser {
fun externalParse(response: Response): String { fun externalParse(response: Response): String {
val jsonData = response.body!!.string() val jsonData = response.body!!.string()
val json = JsonParser.parseString(jsonData).asJsonObject val json = Json.decodeFromString<JsonObject>(jsonData)
val external = json.get("external").string val external = json["external"]!!.jsonPrimitive.content
return external.substringAfterLast("/") return external.substringAfterLast("/")
} }
} }

View File

@ -1,9 +1,6 @@
package exh.md.handlers package exh.md.handlers
import com.elvishew.xlog.XLog import com.elvishew.xlog.XLog
import com.github.salomonbrys.kotson.nullInt
import com.github.salomonbrys.kotson.obj
import com.google.gson.JsonParser
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.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
@ -18,6 +15,11 @@ import exh.metadata.metadata.base.RaisedTag
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.floor import exh.util.floor
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.intOrNull
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.Response import okhttp3.Response
import rx.Completable import rx.Completable
import rx.Single import rx.Single
@ -232,8 +234,8 @@ class ApiMangaParser(private val langs: List<String>) {
throw Exception("Null Response") throw Exception("Null Response")
} }
val jsonObject = JsonParser.parseString(body).obj val jsonObject = Json.decodeFromString<JsonObject>(body)
return jsonObject["manga_id"]?.nullInt ?: throw Exception("No manga associated with chapter") return jsonObject["manga_id"]?.jsonPrimitive?.intOrNull ?: throw Exception("No manga associated with chapter")
} catch (e: Exception) { } catch (e: Exception) {
XLog.e(e) XLog.e(e)
throw e throw e

View File

@ -9,10 +9,12 @@ import exh.metadata.EX_DATE_FORMAT
import exh.metadata.ONGOING_SUFFIX import exh.metadata.ONGOING_SUFFIX
import exh.metadata.humanReadableByteCount import exh.metadata.humanReadableByteCount
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import kotlinx.serialization.Serializable
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.Date import java.util.Date
@Serializable
class EHentaiSearchMetadata : RaisedSearchMetadata() { class EHentaiSearchMetadata : RaisedSearchMetadata() {
var gId: String? var gId: String?
get() = indexedExtra get() = indexedExtra

View File

@ -4,7 +4,9 @@ import android.content.Context
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import kotlinx.serialization.Serializable
@Serializable
class EightMusesSearchMetadata : RaisedSearchMetadata() { class EightMusesSearchMetadata : RaisedSearchMetadata() {
var path: List<String> = emptyList() var path: List<String> = emptyList()

View File

@ -5,7 +5,9 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.metadata.EightMusesSearchMetadata.Companion.ARTIST_NAMESPACE import exh.metadata.metadata.EightMusesSearchMetadata.Companion.ARTIST_NAMESPACE
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import kotlinx.serialization.Serializable
@Serializable
class HBrowseSearchMetadata : RaisedSearchMetadata() { class HBrowseSearchMetadata : RaisedSearchMetadata() {
var hbId: Long? = null var hbId: Long? = null

View File

@ -4,7 +4,9 @@ import android.content.Context
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import kotlinx.serialization.Serializable
@Serializable
class HentaiCafeSearchMetadata : RaisedSearchMetadata() { class HentaiCafeSearchMetadata : RaisedSearchMetadata() {
var hcId: String? = null var hcId: String? = null
var readerId: String? = null var readerId: String? = null

View File

@ -5,9 +5,10 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.model.SManga 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 kotlinx.serialization.Serializable
import java.util.Date import java.util.Date
@Serializable
class HitomiSearchMetadata : RaisedSearchMetadata() { class HitomiSearchMetadata : RaisedSearchMetadata() {
var url get() = hlId?.let { urlFromHlId(it) } var url get() = hlId?.let { urlFromHlId(it) }
set(a) { set(a) {
@ -26,7 +27,7 @@ class HitomiSearchMetadata : RaisedSearchMetadata() {
var group: String? = null var group: String? = null
var type: String? = null var genre: String? = null
var language: String? = null var language: String? = null
@ -101,7 +102,7 @@ class HitomiSearchMetadata : RaisedSearchMetadata() {
pairs += Pair(context.getString(R.string.artist), artists) pairs += Pair(context.getString(R.string.artist), artists)
} }
group?.let { pairs += Pair(context.getString(R.string.group), it) } group?.let { pairs += Pair(context.getString(R.string.group), it) }
type?.let { pairs += Pair(context.getString(R.string.genre), it) } genre?.let { pairs += Pair(context.getString(R.string.genre), it) }
language?.let { pairs += Pair(context.getString(R.string.language), it) } language?.let { pairs += Pair(context.getString(R.string.language), it) }
val series = series.joinToString() val series = series.joinToString()
if (series.isNotBlank()) { if (series.isNotBlank()) {

View File

@ -5,7 +5,9 @@ import androidx.core.net.toUri
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import kotlinx.serialization.Serializable
@Serializable
class MangaDexSearchMetadata : RaisedSearchMetadata() { class MangaDexSearchMetadata : RaisedSearchMetadata() {
var mdId: String? = null var mdId: String? = null

View File

@ -6,9 +6,11 @@ import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.EX_DATE_FORMAT import exh.metadata.EX_DATE_FORMAT
import exh.metadata.ONGOING_SUFFIX import exh.metadata.ONGOING_SUFFIX
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import kotlinx.serialization.Serializable
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.util.Date import java.util.Date
@Serializable
class NHentaiSearchMetadata : RaisedSearchMetadata() { class NHentaiSearchMetadata : RaisedSearchMetadata() {
var url get() = nhId?.let { BASE_URL + nhIdToPath(it) } var url get() = nhId?.let { BASE_URL + nhIdToPath(it) }
set(a) { set(a) {

View File

@ -6,7 +6,9 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.metadata.metadata.base.RaisedTitle import exh.metadata.metadata.base.RaisedTitle
import kotlinx.serialization.Serializable
@Serializable
class PervEdenSearchMetadata : RaisedSearchMetadata() { class PervEdenSearchMetadata : RaisedSearchMetadata() {
var pvId: String? = null var pvId: String? = null
@ -23,7 +25,7 @@ class PervEdenSearchMetadata : RaisedSearchMetadata() {
var artist: String? = null var artist: String? = null
var type: String? = null var genre: String? = null
var rating: Float? = null var rating: Float? = null
@ -102,7 +104,7 @@ class PervEdenSearchMetadata : RaisedSearchMetadata() {
pairs += Pair(context.getString(R.string.alt_titles), altTitles) pairs += Pair(context.getString(R.string.alt_titles), altTitles)
} }
artist?.let { pairs += Pair(context.getString(R.string.artist), it) } artist?.let { pairs += Pair(context.getString(R.string.artist), it) }
type?.let { pairs += Pair(context.getString(R.string.genre), it) } genre?.let { pairs += Pair(context.getString(R.string.genre), it) }
rating?.let { pairs += Pair(context.getString(R.string.average_rating), it.toString()) } rating?.let { pairs += Pair(context.getString(R.string.average_rating), it.toString()) }
status?.let { pairs += Pair(context.getString(R.string.status), it) } status?.let { pairs += Pair(context.getString(R.string.status), it) }
lang?.let { pairs += Pair(context.getString(R.string.language), it) } lang?.let { pairs += Pair(context.getString(R.string.language), it) }

View File

@ -4,7 +4,9 @@ import android.content.Context
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import kotlinx.serialization.Serializable
@Serializable
class PururinSearchMetadata : RaisedSearchMetadata() { class PururinSearchMetadata : RaisedSearchMetadata() {
var prId: Int? = null var prId: Int? = null

View File

@ -6,10 +6,12 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.model.SManga 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 kotlinx.serialization.Serializable
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
@Serializable
class TsuminoSearchMetadata : RaisedSearchMetadata() { class TsuminoSearchMetadata : RaisedSearchMetadata() {
var tmId: Int? = null var tmId: Int? = null

View File

@ -5,10 +5,15 @@ 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 kotlinx.serialization.InternalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.serializer
import rx.Completable import rx.Completable
import rx.Single import rx.Single
import kotlin.reflect.KClass import kotlin.reflect.KClass
@Serializable
data class FlatMetadata( data class FlatMetadata(
val metadata: SearchMetadata, val metadata: SearchMetadata,
val tags: List<SearchTag>, val tags: List<SearchTag>,
@ -16,9 +21,10 @@ data class FlatMetadata(
) { ) {
inline fun <reified T : RaisedSearchMetadata> raise(): T = raise(T::class) inline fun <reified T : RaisedSearchMetadata> raise(): T = raise(T::class)
@OptIn(InternalSerializationApi::class)
fun <T : RaisedSearchMetadata> raise(clazz: KClass<T>): T = fun <T : RaisedSearchMetadata> raise(clazz: KClass<T>): T =
RaisedSearchMetadata.raiseFlattenGson RaisedSearchMetadata.raiseFlattenJson
.fromJson(metadata.extra, clazz.java).apply { .decodeFromString(clazz.serializer(), metadata.extra).apply {
fillBaseFields(this@FlatMetadata) fillBaseFields(this@FlatMetadata)
} }
} }

View File

@ -1,17 +1,35 @@
package exh.metadata.metadata.base package exh.metadata.metadata.base
import android.content.Context import android.content.Context
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.forEach import exh.metadata.forEach
import exh.metadata.metadata.EHentaiSearchMetadata
import exh.metadata.metadata.EightMusesSearchMetadata
import exh.metadata.metadata.HBrowseSearchMetadata
import exh.metadata.metadata.HentaiCafeSearchMetadata
import exh.metadata.metadata.HitomiSearchMetadata
import exh.metadata.metadata.MangaDexSearchMetadata
import exh.metadata.metadata.NHentaiSearchMetadata
import exh.metadata.metadata.PervEdenSearchMetadata
import exh.metadata.metadata.PururinSearchMetadata
import exh.metadata.metadata.TsuminoSearchMetadata
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 exh.plusAssign import exh.plusAssign
import kotlinx.serialization.Polymorphic
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.modules.polymorphic
import kotlinx.serialization.modules.subclass
import kotlin.properties.ReadWriteProperty import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
@Polymorphic
@Serializable
abstract class RaisedSearchMetadata { abstract class RaisedSearchMetadata {
@Transient @Transient
var mangaId: Long = -1 var mangaId: Long = -1
@ -67,7 +85,7 @@ abstract class RaisedSearchMetadata {
fun flatten(): FlatMetadata { fun flatten(): FlatMetadata {
require(mangaId != -1L) require(mangaId != -1L)
val extra = raiseFlattenGson.toJson(this) val extra = raiseFlattenJson.encodeToString(this)
return FlatMetadata( return FlatMetadata(
SearchMetadata( SearchMetadata(
mangaId, mangaId,
@ -122,7 +140,25 @@ abstract class RaisedSearchMetadata {
(this).filter { it.type != TAG_TYPE_VIRTUAL } (this).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 }
val raiseFlattenGson: Gson = GsonBuilder().create() private val module = SerializersModule {
polymorphic(RaisedSearchMetadata::class) {
subclass(EHentaiSearchMetadata::class)
subclass(EightMusesSearchMetadata::class)
subclass(HBrowseSearchMetadata::class)
subclass(HentaiCafeSearchMetadata::class)
subclass(HitomiSearchMetadata::class)
subclass(MangaDexSearchMetadata::class)
subclass(NHentaiSearchMetadata::class)
subclass(PervEdenSearchMetadata::class)
subclass(PururinSearchMetadata::class)
subclass(TsuminoSearchMetadata::class)
}
}
val raiseFlattenJson = Json {
ignoreUnknownKeys = true
serializersModule = module
}
fun titleDelegate(type: Int) = object : ReadWriteProperty<RaisedSearchMetadata, String?> { fun titleDelegate(type: Int) = object : ReadWriteProperty<RaisedSearchMetadata, String?> {
/** /**

View File

@ -1,5 +1,8 @@
package exh.metadata.metadata.base package exh.metadata.metadata.base
import kotlinx.serialization.Serializable
@Serializable
data class RaisedTag( data class RaisedTag(
val namespace: String?, val namespace: String?,
val name: String, val name: String,

View File

@ -1,5 +1,8 @@
package exh.metadata.metadata.base package exh.metadata.metadata.base
import kotlinx.serialization.Serializable
@Serializable
data class RaisedTitle( data class RaisedTitle(
val title: String, val title: String,
val type: Int = 0 val type: Int = 0

View File

@ -1,5 +1,9 @@
package exh.metadata.sql.models package exh.metadata.sql.models
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable
@Serializable
data class SearchMetadata( data class SearchMetadata(
// Manga ID this gallery is linked to // Manga ID this gallery is linked to
val mangaId: Long, val mangaId: Long,
@ -17,5 +21,6 @@ data class SearchMetadata(
val extraVersion: Int val extraVersion: Int
) { ) {
// Transient information attached to this piece of metadata, useful for caching // Transient information attached to this piece of metadata, useful for caching
var transientCache: Map<String, Any>? = null
var transientCache: Map<String, @Contextual Any>? = null
} }

View File

@ -1,5 +1,8 @@
package exh.metadata.sql.models package exh.metadata.sql.models
import kotlinx.serialization.Serializable
@Serializable
data class SearchTag( data class SearchTag(
// Tag identifier, unique // Tag identifier, unique
val id: Long?, val id: Long?,

View File

@ -1,5 +1,8 @@
package exh.metadata.sql.models package exh.metadata.sql.models
import kotlinx.serialization.Serializable
@Serializable
data class SearchTitle( data class SearchTitle(
// Title identifier, unique // Title identifier, unique
val id: Long?, val id: Long?,

View File

@ -1,4 +1,4 @@
package exh package exh.savedsearches
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList

View File

@ -0,0 +1,11 @@
package exh.savedsearches
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonArray
@Serializable
data class JsonSavedSearch(
val name: String,
val query: String,
val filters: JsonArray
)

View File

@ -13,9 +13,6 @@ import android.webkit.WebView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.elvishew.xlog.XLog import com.elvishew.xlog.XLog
import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.string
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.NetworkHelper
@ -28,6 +25,12 @@ import exh.source.DelegatedHttpSource
import exh.util.melt import exh.util.melt
import kotlinx.android.synthetic.main.eh_activity_captcha.toolbar import kotlinx.android.synthetic.main.eh_activity_captcha.toolbar
import kotlinx.android.synthetic.main.eh_activity_captcha.webview import kotlinx.android.synthetic.main.eh_activity_captcha.webview
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody import okhttp3.MultipartBody
@ -153,9 +156,9 @@ class BrowserActionActivity : AppCompatActivity() {
.asObservableSuccess() .asObservableSuccess()
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.map { .map {
val json = JsonParser.parseString(it.body!!.string()) val json = Json.decodeFromString<JsonObject>(it.body!!.string())
it.close() it.close()
json["token"].string json["token"]!!.jsonPrimitive.content
}.melt() }.melt()
webview.addJavascriptInterface(this@BrowserActionActivity, "exh") webview.addJavascriptInterface(this@BrowserActionActivity, "exh")
@ -317,7 +320,7 @@ class BrowserActionActivity : AppCompatActivity() {
.build() .build()
).asObservableSuccess() ).asObservableSuccess()
}.map { response -> }.map { response ->
JsonParser.parseString(response.body!!.string())["results"][0]["alternatives"][0]["transcript"].string.trim() Json.decodeFromString<JsonObject>(response.body!!.string())["results"]!!.jsonArray[0].jsonObject["alternatives"]!!.jsonArray[0].jsonObject["transcript"]!!.jsonPrimitive.content.trim()
}.toSingle() }.toSingle()
} }

View File

@ -47,7 +47,7 @@ class HitomiDescriptionAdapter(
val meta = controller.presenter.meta val meta = controller.presenter.meta
if (meta == null || meta !is HitomiSearchMetadata) return if (meta == null || meta !is HitomiSearchMetadata) return
val genre = meta.type val genre = meta.genre
if (genre != null) { if (genre != null) {
val pair = when (genre) { val pair = when (genre) {
"doujinshi" -> Pair(SourceTagsUtil.DOUJINSHI_COLOR, R.string.doujinshi) "doujinshi" -> Pair(SourceTagsUtil.DOUJINSHI_COLOR, R.string.doujinshi)

View File

@ -47,7 +47,7 @@ class PervEdenDescriptionAdapter(
val meta = controller.presenter.meta val meta = controller.presenter.meta
if (meta == null || meta !is PervEdenSearchMetadata) return if (meta == null || meta !is PervEdenSearchMetadata) return
val genre = meta.type val genre = meta.genre
if (genre != null) { if (genre != null) {
val pair = when (genre) { val pair = when (genre) {
"Doujinshi" -> Pair(SourceTagsUtil.DOUJINSHI_COLOR, R.string.doujinshi) "Doujinshi" -> Pair(SourceTagsUtil.DOUJINSHI_COLOR, R.string.doujinshi)

View File

@ -1,21 +1,20 @@
package xyz.nulldev.ts.api.http.serializer package xyz.nulldev.ts.api.http.serializer
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.JsonObject
import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.boolean
import kotlinx.serialization.json.buildJsonArray
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.double
import kotlinx.serialization.json.float
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.long
import kotlinx.serialization.json.put
import kotlinx.serialization.json.putJsonObject
import kotlin.reflect.KMutableProperty1 import kotlin.reflect.KMutableProperty1
import kotlin.reflect.full.isSubclassOf import kotlin.reflect.full.isSubclassOf
@ -35,7 +34,7 @@ class FilterSerializer {
SortSerializer(this) SortSerializer(this)
) )
fun serialize(filters: FilterList) = JsonArray().apply { fun serialize(filters: FilterList) = buildJsonArray {
filters.forEach { filters.forEach {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
add(serialize(it as Filter<Any?>)) add(serialize(it as Filter<Any?>))
@ -43,26 +42,31 @@ class FilterSerializer {
} }
fun serialize(filter: Filter<Any?>): JsonObject { fun serialize(filter: Filter<Any?>): JsonObject {
val out = JsonObject()
for (serializer in serializers) { for (serializer in serializers) {
if (filter::class.isSubclassOf(serializer.clazz)) { if (filter::class.isSubclassOf(serializer.clazz)) {
// TODO Not sure how to deal with the mess of types here // TODO Not sure how to deal with the mess of types here
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
serializer as Serializer<Filter<Any?>> serializer as Serializer<Filter<Any?>>
serializer.serialize(out, filter) return buildJsonObject {
with(serializer) { serialize(filter) }
out[CLASS_MAPPINGS] = JsonObject() val classMappings = mutableListOf<Pair<String, Any>>()
serializer.mappings().forEach { serializer.mappings().forEach {
val res = it.second.get(filter) val res = it.second.get(filter)
out[it.first] = res put(it.first, res.toString())
out[CLASS_MAPPINGS][it.first] = res?.javaClass?.name ?: "null" classMappings += it.first to (res?.javaClass?.name ?: "null")
}
putJsonObject(CLASS_MAPPINGS) {
classMappings.forEach { (t, u) ->
put(t, u.toString())
}
}
put(TYPE, serializer.type)
} }
out[TYPE] = serializer.type
return out
} }
} }
throw IllegalArgumentException("Cannot serialize this Filter object!") throw IllegalArgumentException("Cannot serialize this Filter object!")
@ -71,13 +75,13 @@ class FilterSerializer {
fun deserialize(filters: FilterList, json: JsonArray) { fun deserialize(filters: FilterList, json: JsonArray) {
filters.zip(json).forEach { (filter, obj) -> filters.zip(json).forEach { (filter, obj) ->
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
deserialize(filter as Filter<Any?>, obj.obj) deserialize(filter as Filter<Any?>, obj.jsonObject)
} }
} }
fun deserialize(filter: Filter<Any?>, json: JsonObject) { fun deserialize(filter: Filter<Any?>, json: JsonObject) {
val serializer = serializers.find { val serializer = serializers.find {
it.type == json[TYPE].string it.type == json[TYPE]!!.jsonPrimitive.content
} ?: throw IllegalArgumentException("Cannot deserialize this type!") } ?: throw IllegalArgumentException("Cannot deserialize this type!")
// TODO Not sure how to deal with the mess of types here // TODO Not sure how to deal with the mess of types here
@ -88,17 +92,17 @@ class FilterSerializer {
serializer.mappings().forEach { serializer.mappings().forEach {
if (it.second is KMutableProperty1) { if (it.second is KMutableProperty1) {
val obj = json[it.first] val obj = json[it.first]!!.jsonPrimitive
val res: Any? = when (json[CLASS_MAPPINGS][it.first].string) { val res: Any? = when (json[CLASS_MAPPINGS]!!.jsonObject[it.first]!!.jsonPrimitive.content) {
java.lang.Integer::class.java.name -> obj.int java.lang.Integer::class.java.name -> obj.int
java.lang.Long::class.java.name -> obj.long java.lang.Long::class.java.name -> obj.long
java.lang.Float::class.java.name -> obj.float java.lang.Float::class.java.name -> obj.float
java.lang.Double::class.java.name -> obj.double java.lang.Double::class.java.name -> obj.double
java.lang.String::class.java.name -> obj.string java.lang.String::class.java.name -> obj.content
java.lang.Boolean::class.java.name -> obj.bool java.lang.Boolean::class.java.name -> obj.boolean
java.lang.Byte::class.java.name -> obj.byte java.lang.Byte::class.java.name -> obj.content.toByte()
java.lang.Short::class.java.name -> obj.short java.lang.Short::class.java.name -> obj.content.toShort()
java.lang.Character::class.java.name -> obj.char java.lang.Character::class.java.name -> obj.content[0]
"null" -> null "null" -> null
else -> throw IllegalArgumentException("Cannot deserialize this type!") else -> throw IllegalArgumentException("Cannot deserialize this type!")
} }

View File

@ -1,20 +1,23 @@
package xyz.nulldev.ts.api.http.serializer package xyz.nulldev.ts.api.http.serializer
import com.github.salomonbrys.kotson.bool
import com.github.salomonbrys.kotson.int
import com.github.salomonbrys.kotson.nullArray
import com.github.salomonbrys.kotson.nullObj
import com.github.salomonbrys.kotson.set
import com.github.salomonbrys.kotson.string
import com.google.gson.JsonArray
import com.google.gson.JsonNull
import com.google.gson.JsonObject
import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.Filter
import kotlinx.serialization.json.JsonNull
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonObjectBuilder
import kotlinx.serialization.json.add
import kotlinx.serialization.json.boolean
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.put
import kotlinx.serialization.json.putJsonArray
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.reflect.KProperty1 import kotlin.reflect.KProperty1
interface Serializer<in T : Filter<out Any?>> { interface Serializer<in T : Filter<out Any?>> {
fun serialize(json: JsonObject, filter: T) {} fun JsonObjectBuilder.serialize(filter: T) {}
fun deserialize(json: JsonObject, filter: T) {} fun deserialize(json: JsonObject, filter: T) {}
/** /**
@ -76,9 +79,9 @@ class SelectSerializer(override val serializer: FilterSerializer) : Serializer<F
override val type = "SELECT" override val type = "SELECT"
override val clazz = Filter.Select::class override val clazz = Filter.Select::class
override fun serialize(json: JsonObject, filter: Filter.Select<Any>) { override fun JsonObjectBuilder.serialize(filter: Filter.Select<Any>) {
// Serialize values to JSON // Serialize values to JSON
json[VALUES] = JsonArray().apply { putJsonArray(VALUES) {
filter.values.map { filter.values.map {
it.toString() it.toString()
}.forEach { add(it) } }.forEach { add(it) }
@ -146,15 +149,15 @@ class GroupSerializer(override val serializer: FilterSerializer) : Serializer<Fi
override val type = "GROUP" override val type = "GROUP"
override val clazz = Filter.Group::class override val clazz = Filter.Group::class
override fun serialize(json: JsonObject, filter: Filter.Group<Any?>) { override fun JsonObjectBuilder.serialize(filter: Filter.Group<Any?>) {
json[STATE] = JsonArray().apply { putJsonArray(STATE) {
filter.state.forEach { filter.state.forEach {
add( add(
if (it is Filter<*>) { if (it is Filter<*>) {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
serializer.serialize(it as Filter<Any?>) serializer.serialize(it as Filter<Any?>)
} else { } else {
JsonNull.INSTANCE JsonNull
} }
) )
} }
@ -162,10 +165,10 @@ class GroupSerializer(override val serializer: FilterSerializer) : Serializer<Fi
} }
override fun deserialize(json: JsonObject, filter: Filter.Group<Any?>) { override fun deserialize(json: JsonObject, filter: Filter.Group<Any?>) {
json[STATE].asJsonArray.forEachIndexed { index, jsonElement -> json[STATE]!!.jsonArray.forEachIndexed { index, jsonElement ->
if (!jsonElement.isJsonNull) { if (jsonElement !is JsonNull) {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
serializer.deserialize(filter.state[index] as Filter<Any?>, jsonElement.asJsonObject) serializer.deserialize(filter.state[index] as Filter<Any?>, jsonElement.jsonObject)
} }
} }
} }
@ -184,27 +187,29 @@ class SortSerializer(override val serializer: FilterSerializer) : Serializer<Fil
override val type = "SORT" override val type = "SORT"
override val clazz = Filter.Sort::class override val clazz = Filter.Sort::class
override fun serialize(json: JsonObject, filter: Filter.Sort) { override fun JsonObjectBuilder.serialize(filter: Filter.Sort) {
// Serialize values // Serialize values
json[VALUES] = JsonArray().apply { putJsonArray(VALUES) {
filter.values.forEach { add(it) } filter.values.forEach { add(it) }
} }
// Serialize state // Serialize state
json[STATE] = filter.state?.let { (index, ascending) -> put(
JsonObject().apply { STATE,
this[STATE_INDEX] = index filter.state?.let { (index, ascending) ->
this[STATE_ASCENDING] = ascending buildJsonObject {
} put(STATE_INDEX, index)
} ?: JsonNull.INSTANCE put(STATE_ASCENDING, ascending)
}
} ?: JsonNull
)
} }
override fun deserialize(json: JsonObject, filter: Filter.Sort) { override fun deserialize(json: JsonObject, filter: Filter.Sort) {
// Deserialize state // Deserialize state
filter.state = json[STATE].nullObj?.let { filter.state = (json[STATE] as? JsonObject)?.let {
Filter.Sort.Selection( Filter.Sort.Selection(
it[STATE_INDEX].int, it[STATE_INDEX]!!.jsonPrimitive.int,
it[STATE_ASCENDING].bool it[STATE_ASCENDING]!!.jsonPrimitive.boolean
) )
} }
} }
@ -227,19 +232,17 @@ class AutoCompleteSerializer(override val serializer: FilterSerializer) : Serial
override val type = "AUTOCOMPLETE" override val type = "AUTOCOMPLETE"
override val clazz = Filter.AutoComplete::class override val clazz = Filter.AutoComplete::class
override fun serialize(json: JsonObject, filter: Filter.AutoComplete) { override fun JsonObjectBuilder.serialize(filter: Filter.AutoComplete) {
// Serialize values to JSON // Serialize values to JSON
json[STATE] = JsonArray().apply { putJsonArray(STATE) {
filter.state.forEach { add(it) } filter.state.forEach { add(it) }
} }
} }
override fun deserialize(json: JsonObject, filter: Filter.AutoComplete) { override fun deserialize(json: JsonObject, filter: Filter.AutoComplete) {
// Deserialize state // Deserialize state
json[STATE].nullArray?.let { array -> filter.state = json[STATE]!!.jsonArray.map {
filter.state = array.map { it.jsonPrimitive.content
it.string
}
} }
} }