From f3365cef675b31e8be423590ea073b03d210bda6 Mon Sep 17 00:00:00 2001 From: Jobobby04 Date: Mon, 12 Oct 2020 14:20:54 -0400 Subject: [PATCH] Convert SY specific usages of Gson to Kotlin Serialization Cleanup saved searches a bit Cleanup json parsing --- .../tachiyomi/data/backup/BackupManager.kt | 25 +++-- .../data/library/CustomMangaManager.kt | 52 +++++----- .../extension/api/ExtensionGithubApi.kt | 2 - .../eu/kanade/tachiyomi/source/LocalSource.kt | 13 ++- .../tachiyomi/source/online/all/EHentai.kt | 48 +++++---- .../tachiyomi/source/online/all/Hitomi.kt | 4 +- .../tachiyomi/source/online/all/NHentai.kt | 99 ++++++++++++++----- .../tachiyomi/source/online/all/PervEden.kt | 2 +- .../source/browse/BrowseSourceController.kt | 2 +- .../source/browse/BrowseSourcePresenter.kt | 36 +++---- .../browse/source/browse/RecommendsPager.kt | 95 +++++++++--------- .../browse/source/browse/SourceFilterSheet.kt | 2 +- .../ui/browse/source/index/IndexController.kt | 6 +- .../ui/browse/source/index/IndexPresenter.kt | 17 ++-- .../ui/setting/SettingsEhController.kt | 7 +- app/src/main/java/exh/EXHMigrations.kt | 39 ++++---- app/src/main/java/exh/debug/DebugFunctions.kt | 65 ++---------- .../main/java/exh/eh/EHentaiUpdateWorker.kt | 6 +- .../main/java/exh/eh/EHentaiUpdaterStats.kt | 3 + app/src/main/java/exh/log/EHNetworkLogging.kt | 17 ++-- .../java/exh/md/handlers/ApiChapterParser.kt | 21 ++-- .../java/exh/md/handlers/ApiMangaParser.kt | 12 ++- .../metadata/EHentaiSearchMetadata.kt | 2 + .../metadata/EightMusesSearchMetadata.kt | 2 + .../metadata/HBrowseSearchMetadata.kt | 2 + .../metadata/HentaiCafeSearchMetadata.kt | 2 + .../metadata/metadata/HitomiSearchMetadata.kt | 7 +- .../metadata/MangaDexSearchMetadata.kt | 2 + .../metadata/NHentaiSearchMetadata.kt | 2 + .../metadata/PervEdenSearchMetadata.kt | 6 +- .../metadata/PururinSearchMetadata.kt | 2 + .../metadata/TsuminoSearchMetadata.kt | 2 + .../metadata/metadata/base/FlatMetadata.kt | 10 +- .../metadata/base/RaisedSearchMetadata.kt | 44 ++++++++- .../exh/metadata/metadata/base/RaisedTag.kt | 3 + .../exh/metadata/metadata/base/RaisedTitle.kt | 3 + .../exh/metadata/sql/models/SearchMetadata.kt | 7 +- .../java/exh/metadata/sql/models/SearchTag.kt | 3 + .../exh/metadata/sql/models/SearchTitle.kt | 3 + .../exh/{ => savedsearches}/EXHSavedSearch.kt | 2 +- .../java/exh/savedsearches/JsonSavedSearch.kt | 11 +++ .../exh/ui/captcha/BrowserActionActivity.kt | 15 +-- .../adapters/HitomiDescriptionAdapter.kt | 2 +- .../adapters/PervEdenDescriptionAdapter.kt | 2 +- .../api/http/serializer/FilterSerializer.kt | 74 +++++++------- .../http/serializer/FilterSerializerModels.kt | 75 +++++++------- 46 files changed, 490 insertions(+), 366 deletions(-) rename app/src/main/java/exh/{ => savedsearches}/EXHSavedSearch.kt (85%) create mode 100644 app/src/main/java/exh/savedsearches/JsonSavedSearch.kt diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupManager.kt index f6d770361..0cf6357c7 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupManager.kt @@ -2,20 +2,16 @@ package eu.kanade.tachiyomi.data.backup import android.content.Context import android.net.Uri -import com.github.salomonbrys.kotson.array import com.github.salomonbrys.kotson.fromJson import com.github.salomonbrys.kotson.jsonObject -import com.github.salomonbrys.kotson.obj import com.github.salomonbrys.kotson.registerTypeAdapter import com.github.salomonbrys.kotson.registerTypeHierarchyAdapter import com.github.salomonbrys.kotson.set -import com.github.salomonbrys.kotson.string import com.google.gson.Gson import com.google.gson.GsonBuilder import com.google.gson.JsonArray import com.google.gson.JsonElement import com.google.gson.JsonObject -import com.google.gson.JsonParser import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.R 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.MergedSource import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource -import exh.EXHSavedSearch import exh.MERGED_SOURCE_ID import exh.eh.EHentaiThrottleManager import exh.merged.sql.models.MergedMangaReference +import exh.savedsearches.EXHSavedSearch +import exh.savedsearches.JsonSavedSearch import exh.util.asObservable import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.runBlocking +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json import rx.Observable import timber.log.Timber import uy.kohesive.injekt.Injekt @@ -553,17 +552,17 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) { val newSavedSearches = backupSavedSearches.mapNotNull { try { val id = it.substringBefore(':').toLong() - val content = JsonParser.parseString(it.substringAfter(':')).obj + val content = Json.decodeFromString(it.substringAfter(':')) val source = sourceManager.getOrStub(id) if (source !is CatalogueSource) return@mapNotNull null val originalFilters = source.getFilterList() - filterSerializer.deserialize(originalFilters, content["filters"].array) + filterSerializer.deserialize(originalFilters, content.filters) Pair( id, EXHSavedSearch( - content["name"].string, - content["query"].string, + content.name, + content.query, originalFilters ) ) @@ -580,18 +579,18 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) { newSavedSearches += preferences.eh_savedSearches().get().mapNotNull { try { val id = it.substringBefore(':').toLong() - val content = JsonParser.parseString(it.substringAfter(':')).obj + val content = Json.decodeFromString(it.substringAfter(':')) if (id !in currentSources) return@mapNotNull null val source = sourceManager.getOrStub(id) if (source !is CatalogueSource) return@mapNotNull null val originalFilters = source.getFilterList() - filterSerializer.deserialize(originalFilters, content["filters"].array) + filterSerializer.deserialize(originalFilters, content.filters) Pair( id, EXHSavedSearch( - content["name"].string, - content["query"].string, + content.name, + content.query, originalFilters ) ) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/CustomMangaManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/CustomMangaManager.kt index 743bada22..1b3384c0f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/CustomMangaManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/CustomMangaManager.kt @@ -1,14 +1,19 @@ package eu.kanade.tachiyomi.data.library 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.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.util.Scanner @@ -28,25 +33,24 @@ class CustomMangaManager(val context: Context) { if (!editJson.exists() || !editJson.isFile) return val json = try { - Gson().fromJson( - Scanner(editJson).useDelimiter("\\Z").next(), - JsonObject::class.java + Json.decodeFromString( + Scanner(editJson).useDelimiter("\\Z").next() ) } catch (e: Exception) { null } ?: return - val mangasJson = json.get("mangas").asJsonArray ?: return + val mangasJson = json["mangas"] as? JsonArray ?: return customMangaMap = mangasJson.mapNotNull { element -> - val mangaObject = element.asJsonObject ?: return@mapNotNull null - val id = mangaObject["id"]?.nullLong ?: return@mapNotNull null + val mangaObject = element as? JsonObject ?: return@mapNotNull null + val id = mangaObject["id"]?.jsonPrimitive?.longOrNull ?: return@mapNotNull null val manga = MangaImpl().apply { this.id = id - title = mangaObject["title"]?.nullString ?: "" - author = mangaObject["author"]?.nullString - artist = mangaObject["artist"]?.nullString - description = mangaObject["description"]?.nullString - genre = mangaObject["genre"]?.asJsonArray?.mapNotNull { it.nullString } + title = mangaObject["title"]?.jsonPrimitive?.contentOrNull ?: "" + author = mangaObject["author"]?.jsonPrimitive?.contentOrNull + artist = mangaObject["artist"]?.jsonPrimitive?.contentOrNull + description = mangaObject["description"]?.jsonPrimitive?.contentOrNull + genre = (mangaObject["genre"] as? JsonArray)?.mapNotNull { it.jsonPrimitive.contentOrNull } ?.joinToString(", ") } id to manga @@ -72,17 +76,16 @@ class CustomMangaManager(val context: Context) { private fun saveCustomInfo() { val jsonElements = customMangaMap.values.map { it.toJson() } if (jsonElements.isNotEmpty()) { - val gson = GsonBuilder().create() - val root = JsonObject() - val mangaEntries = gson.toJsonTree(jsonElements) - - root["mangas"] = mangaEntries + val mangaEntries = Json.encodeToJsonElement(jsonElements) + val root = buildJsonObject { + put("mangas", mangaEntries) + } editJson.delete() - editJson.writeText(gson.toJson(root)) + editJson.writeText(Json.encodeToString(root)) } } - fun Manga.toJson(): MangaJson { + private fun Manga.toJson(): MangaJson { return MangaJson( id!!, title, @@ -93,6 +96,7 @@ class CustomMangaManager(val context: Context) { ) } + @Serializable data class MangaJson( val id: Long, val title: String? = null, diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionGithubApi.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionGithubApi.kt index b10fb476f..1dd939b1f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionGithubApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionGithubApi.kt @@ -1,8 +1,6 @@ package eu.kanade.tachiyomi.extension.api 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.extension.model.Extension import eu.kanade.tachiyomi.extension.model.LoadResult diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/LocalSource.kt b/app/src/main/java/eu/kanade/tachiyomi/source/LocalSource.kt index 5c4fdd099..4d6c90f2c 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/source/LocalSource.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/LocalSource.kt @@ -1,7 +1,6 @@ package eu.kanade.tachiyomi.source import android.content.Context -import com.google.gson.GsonBuilder import com.google.gson.JsonParser import eu.kanade.tachiyomi.R 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 junrar.Archive import junrar.rarfile.FileHeader +import kotlinx.serialization.Serializable +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json import rx.Observable import timber.log.Timber 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 { it.exists() } ?: return - val gson = GsonBuilder().setPrettyPrinting().create() + val json = Json { + prettyPrint = true + } val existingFileName = directory.listFiles()?.find { it.extension == "json" }?.name 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()) } + @Serializable data class MangaJson( val title: String, val author: String?, diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt index 378e6f8c0..2e186ed07 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt @@ -3,15 +3,6 @@ package eu.kanade.tachiyomi.source.online.all import android.content.Context import android.net.Uri 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.data.database.models.Manga import eu.kanade.tachiyomi.data.preference.PreferencesHelper @@ -57,6 +48,17 @@ import exh.util.trimAll import exh.util.trimOrNull import exh.util.urlImportFetchSearchManga 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.CookieJar import okhttp3.Headers @@ -938,29 +940,33 @@ class EHentai( val gallery = lastSplit.first() val pageToken = uri.pathSegments.elementAt(1) - val json = JsonObject() - json["method"] = "gtoken" - json["pagelist"] = JsonArray().apply { - add( - JsonArray().apply { - add(gallery.toInt()) - add(pageToken) - add(pageNum.toInt()) + val json = buildJsonObject { + put("method", "gtoken") + put( + "pagelist", + buildJsonArray { + add( + buildJsonArray { + add(gallery.toInt()) + add(pageToken) + add(pageNum.toInt()) + } + ) } ) } - val outJson = JsonParser.parseString( + val outJson = Json.decodeFromString( client.newCall( Request.Builder() .url(EH_API_BASE) .post(json.toString().toRequestBody(JSON)) .build() ).execute().body!!.string() - ).obj + ) - val obj = outJson["tokenlist"].array.first() - return "${uri.scheme}://${uri.host}/g/${obj["gid"].int}/${obj["token"].string}/" + val obj = outJson["tokenlist"]!!.jsonArray.first().jsonObject + return "${uri.scheme}://${uri.host}/g/${obj["gid"]!!.jsonPrimitive.int}/${obj["token"]!!.jsonPrimitive.content}/" } override fun getDescriptionAdapter(controller: MangaController): EHentaiDescriptionAdapter { diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/Hitomi.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/Hitomi.kt index bf2cb7acc..b0bc477a7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/Hitomi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/Hitomi.kt @@ -68,8 +68,8 @@ class Hitomi(delegate: HttpSource, val context: Context) : tags += RaisedTag("group", group!!, RaisedSearchMetadata.TAG_TYPE_VIRTUAL) } "type" -> { - type = content.text() - tags += RaisedTag("type", type!!, RaisedSearchMetadata.TAG_TYPE_VIRTUAL) + genre = content.text() + tags += RaisedTag("type", genre!!, RaisedSearchMetadata.TAG_TYPE_VIRTUAL) } "series" -> { series = content.select("a").map { it.text() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/NHentai.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/NHentai.kt index 40a1e8bb9..854d18bc8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/NHentai.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/NHentai.kt @@ -2,12 +2,7 @@ package eu.kanade.tachiyomi.source.online.all import android.content.Context import android.net.Uri -import com.github.salomonbrys.kotson.get -import com.github.salomonbrys.kotson.nullArray -import com.github.salomonbrys.kotson.nullLong -import com.github.salomonbrys.kotson.nullObj -import com.github.salomonbrys.kotson.nullString -import com.google.gson.JsonParser +import com.elvishew.xlog.XLog import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.MangasPage @@ -23,6 +18,10 @@ import exh.metadata.metadata.base.RaisedTag import exh.source.DelegatedHttpSource import exh.ui.metadata.adapters.NHentaiDescriptionAdapter 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 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( UNICODE_ESCAPE_REGEX ) { it.groupValues[1].toInt(radix = 16).toChar().toString() } - val obj = JsonParser.parseString(json).asJsonObject + val jsonResponse = jsonParser.decodeFromString(json) 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 -> - japaneseTitle = title["japanese"].nullString - shortTitle = title["pretty"].nullString - englishTitle = title["english"].nullString + jsonResponse.title?.let { title -> + japaneseTitle = title.japanese + shortTitle = title.pretty + englishTitle = title.english } - obj["images"].nullObj?.let { images -> - coverImageType = images["cover"]?.get("t").nullString - images["pages"].nullArray?.mapNotNull { - it?.asJsonObject?.get("t").nullString - }?.let { + jsonResponse.images?.let { images -> + coverImageType = images.cover?.type + images.pages.mapNotNull { + it.type + }.let { pageImageTypes = it } - thumbnailImageType = images["thumbnail"]?.get("t").nullString + thumbnailImageType = images.thumbnail?.type } - scanlator = obj["scanlator"].nullString + scanlator = jsonResponse.scanlator - obj["tags"]?.asJsonArray?.map { - val asObj = it.asJsonObject - Pair(asObj["type"].nullString, asObj["name"].nullString) - }?.apply { + jsonResponse.tags.map { + it.type to it.name + }.apply { tags.clear() - }?.forEach { + }.forEach { 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)) } @@ -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 = 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 = 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 ensureDelegateCompatible() { @@ -127,6 +168,10 @@ class NHentai(delegate: HttpSource, val context: Context) : companion object { const val otherId = 7309872737163460316L + private val jsonParser = Json { + ignoreUnknownKeys = true + } + private val GALLERY_JSON_REGEX = Regex(".parse\\(\"(.*)\"\\);") private val UNICODE_ESCAPE_REGEX = Regex("\\\\u([0-9a-fA-F]{4})") } diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/PervEden.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/PervEden.kt index 1652a8cda..9a482bde9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/PervEden.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/PervEden.kt @@ -96,7 +96,7 @@ class PervEden(delegate: HttpSource, val context: Context) : if (it is TextNode) { val text = it.text().trim() if (!text.isBlank()) { - type = text + genre = text } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceController.kt index 38246b247..c70e55745 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceController.kt @@ -53,8 +53,8 @@ import eu.kanade.tachiyomi.util.view.shrinkOnScroll import eu.kanade.tachiyomi.util.view.snack import eu.kanade.tachiyomi.widget.AutofitRecyclerView import eu.kanade.tachiyomi.widget.EmptyView -import exh.EXHSavedSearch import exh.isEhBasedSource +import exh.savedsearches.EXHSavedSearch import exh.source.EnhancedHttpSource.Companion.getMainSource import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.main_activity.root_coordinator diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt index 150299240..fd3bb1f98 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt @@ -1,11 +1,6 @@ package eu.kanade.tachiyomi.ui.browse.source.browse 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.ISectionable 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.util.chapter.ChapterSettingsHelper 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.Subscription import rx.android.schedulers.AndroidSchedulers @@ -134,8 +136,8 @@ open class BrowseSourcePresenter( // SY --> if (filters != null) { - val filters = JsonParser.parseString(filters).obj - filterSerializer.deserialize(sourceFilters, filters["filters"].array) + val filters = Json.decodeFromString(filters) + filterSerializer.deserialize(sourceFilters, filters["filters"]!!.jsonArray) } val allDefault = sourceFilters == source.getFilterList() // SY <-- @@ -446,11 +448,11 @@ open class BrowseSourcePresenter( !it.startsWith("${source.id}:") } val newSerialized = searches.map { - "${source.id}:" + jsonObject( - "name" to it.name, - "query" to it.query, - "filters" to filterSerializer.serialize(it.filterList) - ).toString() + "${source.id}:" + buildJsonObject { + put("name", it.name) + put("query", it.query) + put("filters", filterSerializer.serialize(it.filterList)) + }.toString() } prefs.eh_savedSearches().set((otherSerialized + newSerialized).toSet()) } @@ -461,12 +463,12 @@ open class BrowseSourcePresenter( try { val id = it.substringBefore(':').toLong() if (id != source.id) return@map null - val content = JsonParser.parseString(it.substringAfter(':')).obj + val content = Json.decodeFromString(it.substringAfter(':')) val originalFilters = source.getFilterList() - filterSerializer.deserialize(originalFilters, content["filters"].array) + filterSerializer.deserialize(originalFilters, content.filters) EXHSavedSearch( - content["name"].string, - content["query"].string, + content.name, + content.query, originalFilters ) } catch (t: RuntimeException) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/RecommendsPager.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/RecommendsPager.kt index 94e3e778a..0eac0104c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/RecommendsPager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/RecommendsPager.kt @@ -1,16 +1,5 @@ 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.network.await import eu.kanade.tachiyomi.source.model.MangasPage @@ -22,6 +11,15 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job 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.MediaType.Companion.toMediaTypeOrNull import okhttp3.OkHttpClient @@ -73,17 +71,18 @@ class MyAnimeList() : API("https://api.jikan.moe/v3/") { if (body.isEmpty()) { throw Exception("Null Response") } - val data = JsonParser.parseString(body).obj - val recommendations = data["recommendations"].nullArray + val data = Json.decodeFromString(body) + val recommendations = data["recommendations"] as? JsonArray ?: throw Exception("Unexpected response") val recs = recommendations.map { rec -> + rec as? JsonObject ?: throw Exception("Invalid json") Timber.tag("RECOMMENDATIONS") - .d("MYANIMELIST > FOUND RECOMMENDATION > %s", rec["title"].string) + .d("MYANIMELIST > FOUND RECOMMENDATION > %s", rec["title"]!!.jsonPrimitive.content) SMangaImpl().apply { - this.title = rec["title"].string - this.thumbnail_url = rec["image_url"].string + this.title = rec["title"]!!.jsonPrimitive.content + this.thumbnail_url = rec["image_url"]!!.jsonPrimitive.content this.initialized = true - this.url = rec["url"].string + this.url = rec["url"]!!.jsonPrimitive.content } } callback.invoke(recs, null) @@ -121,15 +120,15 @@ class MyAnimeList() : API("https://api.jikan.moe/v3/") { if (body.isEmpty()) { throw Exception("Null Response") } - val data = JsonParser.parseString(body).obj - val results = data["results"].nullArray ?: throw Exception("Unexpected response") - if (results.size() <= 0) { + val data = Json.decodeFromString(body) + val results = data["results"] as? JsonArray ?: throw Exception("Unexpected response") + if (results.size <= 0) { throw Exception("'$search' not found") } - val result = results.first().obj + val result = results.first().jsonObject Timber.tag("RECOMMENDATIONS") - .d("MYANIMELIST > FOUND TITLE > %s", result["title"].string) - val id = result["mal_id"].string + .d("MYANIMELIST > FOUND TITLE > %s", result["title"]!!.jsonPrimitive.content) + val id = result["mal_id"]!!.jsonPrimitive.content getRecsById(id, callback) } } @@ -138,19 +137,21 @@ class MyAnimeList() : API("https://api.jikan.moe/v3/") { class Anilist() : API("https://graphql.anilist.co/") { private fun countOccurrence(arr: JsonArray, search: String): Int { return arr.count { - val synonym = it.string + val synonym = it.jsonPrimitive.content synonym.contains(search, true) } } 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 { - return obj["title"].obj["romaji"].nullString - ?: obj["title"].obj["english"].nullString - ?: obj["title"].obj["native"].string + return obj["title"]!!.jsonObject.let { + it["romaji"]?.jsonPrimitive?.content + ?: it["english"]?.jsonPrimitive?.content + ?: it["native"]!!.jsonPrimitive.content + } } override fun getRecsBySearch( @@ -189,11 +190,13 @@ class Anilist() : API("https://graphql.anilist.co/") { |} |} |""".trimMargin() - val variables = jsonObject("search" to search) - val payload = jsonObject( - "query" to query, - "variables" to variables - ) + val variables = buildJsonObject { + put("search", search) + } + val payload = buildJsonObject { + put("query", query) + put("variables", variables) + } val payloadBody = payload.toString().toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull()) val request = Request.Builder() @@ -211,33 +214,33 @@ class Anilist() : API("https://graphql.anilist.co/") { if (body.isEmpty()) { throw Exception("Null Response") } - val data = JsonParser.parseString(body).obj["data"].nullObj + val data = Json.decodeFromString(body)["data"] as? JsonObject ?: throw Exception("Unexpected response") - val page = data["Page"].obj - val media = page["media"].array - if (media.size() <= 0) { + val page = data["Page"]!!.jsonObject + val media = page["media"]!!.jsonArray + if (media.size <= 0) { throw Exception("'$search' not found") } val result = media.sortedWith( compareBy( - { languageContains(it.obj, "romaji", search) }, - { languageContains(it.obj, "english", search) }, - { languageContains(it.obj, "native", search) }, - { countOccurrence(it.obj["synonyms"].array, search) > 0 } + { languageContains(it.jsonObject, "romaji", search) }, + { languageContains(it.jsonObject, "english", search) }, + { languageContains(it.jsonObject, "native", search) }, + { countOccurrence(it.jsonObject["synonyms"]!!.jsonArray, search) > 0 } ) - ).last().obj + ).last().jsonObject Timber.tag("RECOMMENDATIONS") .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 rec = it["node"]["mediaRecommendation"].obj + val rec = it.jsonObject["node"]!!.jsonObject["mediaRecommendation"]!!.jsonObject Timber.tag("RECOMMENDATIONS") .d("ANILIST: FOUND RECOMMENDATION: %s", getTitle(rec)) SMangaImpl().apply { 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.url = rec["siteUrl"].string + this.url = rec["siteUrl"]!!.jsonPrimitive.content } } callback.invoke(recs, null) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterSheet.kt index 16ed32051..880c20318 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterSheet.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterSheet.kt @@ -18,7 +18,7 @@ import eu.kanade.tachiyomi.databinding.SourceFilterSheetBinding import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.online.BrowseSourceFilterHeader import eu.kanade.tachiyomi.widget.SimpleNavigationView -import exh.EXHSavedSearch +import exh.savedsearches.EXHSavedSearch import exh.source.EnhancedHttpSource.Companion.getMainSource class SourceFilterSheet( diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/index/IndexController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/index/IndexController.kt index aa3a2c42a..2d53c34ce 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/index/IndexController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/index/IndexController.kt @@ -11,7 +11,6 @@ import androidx.core.view.isVisible import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.afollestad.materialdialogs.MaterialDialog -import com.github.salomonbrys.kotson.jsonObject import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton import eu.kanade.tachiyomi.R 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.launchIn import kotlinx.coroutines.flow.onEach +import kotlinx.serialization.json.buildJsonObject import reactivecircus.flowbinding.android.view.clicks import reactivecircus.flowbinding.appcompat.QueryTextEvent import reactivecircus.flowbinding.appcompat.queryTextEvents @@ -203,7 +203,7 @@ open class IndexController : val allDefault = presenter.sourceFilters == presenter.source.getFilterList() filterSheet?.dismiss() 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()) } }, @@ -232,7 +232,7 @@ open class IndexController : filterSheet?.dismiss() 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()) } }, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/index/IndexPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/index/IndexPresenter.kt index ef09d1865..de922014f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/index/IndexPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/index/IndexPresenter.kt @@ -1,10 +1,6 @@ package eu.kanade.tachiyomi.ui.browse.source.index 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.kanade.tachiyomi.data.database.DatabaseHelper 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.ui.base.presenter.BasePresenter 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 kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -23,6 +20,8 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.flow.singleOrNull import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json import rx.Observable import rx.Subscription import rx.android.schedulers.AndroidSchedulers @@ -232,12 +231,12 @@ open class IndexPresenter( try { val id = it.substringBefore(':').toLong() if (id != source.id) return@map null - val content = JsonParser.parseString(it.substringAfter(':')).obj + val content = Json.decodeFromString(it.substringAfter(':')) val originalFilters = source.getFilterList() - filterSerializer.deserialize(originalFilters, content["filters"].array) + filterSerializer.deserialize(originalFilters, content.filters) EXHSavedSearch( - content["name"].string, - content["query"].string, + content.name, + content.query, originalFilters ) } catch (t: RuntimeException) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsEhController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsEhController.kt index 84d1e1251..2cbdfce77 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsEhController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsEhController.kt @@ -12,8 +12,6 @@ import com.afollestad.materialdialogs.input.getInputField import com.afollestad.materialdialogs.input.input import com.bluelinelabs.conductor.RouterTransaction import com.bluelinelabs.conductor.changehandler.FadeChangeHandler -import com.github.salomonbrys.kotson.fromJson -import com.google.gson.Gson import com.tfcporciuncula.flow.Preference import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.DatabaseHelper @@ -116,6 +114,8 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.take import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json import uy.kohesive.injekt.injectLazy import java.util.Date import kotlin.time.Duration @@ -128,7 +128,6 @@ import kotlin.time.hours */ class SettingsEhController : SettingsController() { - private val gson: Gson by injectLazy() private val db: DatabaseHelper by injectLazy() private fun Preference<*>.reconfigure(): Boolean { @@ -687,7 +686,7 @@ class SettingsEhController : SettingsController() { val updateInfo = try { val stats = preferences.eh_autoUpdateStats().get().nullIfBlank()?.let { - gson.fromJson(it) + Json.decodeFromString(it) } val statsText = if (stats != null) { diff --git a/app/src/main/java/exh/EXHMigrations.kt b/app/src/main/java/exh/EXHMigrations.kt index 568bd12cd..57707b531 100644 --- a/app/src/main/java/exh/EXHMigrations.kt +++ b/app/src/main/java/exh/EXHMigrations.kt @@ -2,9 +2,6 @@ package exh import android.content.Context 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.RawQuery 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 exh.merged.sql.models.MergedMangaReference 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 java.io.File import java.net.URI @@ -36,7 +37,6 @@ import java.net.URISyntaxException object EXHMigrations { private val db: DatabaseHelper by injectLazy() private val sourceManager: SourceManager by injectLazy() - private val gson: Gson by injectLazy() private val logger = XLog.tag("EXHMigrations") @@ -167,7 +167,7 @@ object EXHMigrations { .executeAsBlocking() 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()) { val mangaToUpdate = mutableListOf() val mergedMangaReferences = mutableListOf() @@ -237,7 +237,7 @@ object EXHMigrations { .prepare() .executeAsBlocking() 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() 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 { @@ -339,23 +339,25 @@ object EXHMigrations { } } + @Serializable private data class UrlConfig( - @SerializedName("s") + @SerialName("s") val source: Long, - @SerializedName("u") + @SerialName("u") val url: String, - @SerializedName("m") + @SerialName("m") val mangaUrl: String ) + @Serializable private data class MangaConfig( - @SerializedName("c") + @SerialName("c") val children: List ) { companion object { - fun readFromUrl(gson: Gson, url: String): MangaConfig? { + fun readFromUrl(url: String): MangaConfig? { return try { - gson.fromJson(url) + Json.decodeFromString(url) } catch (e: Exception) { null } @@ -363,14 +365,15 @@ object EXHMigrations { } } - private fun readMangaConfig(manga: SManga, gson: Gson): MangaConfig? { - return MangaConfig.readFromUrl(gson, manga.url) + private fun readMangaConfig(manga: SManga): MangaConfig? { + return MangaConfig.readFromUrl(manga.url) } + @Serializable private data class MangaSource( - @SerializedName("s") + @SerialName("s") val source: Long, - @SerializedName("u") + @SerialName("u") val url: String ) { 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 { - gson.fromJson(url) + Json.decodeFromString(url) } catch (e: Exception) { null } diff --git a/app/src/main/java/exh/debug/DebugFunctions.kt b/app/src/main/java/exh/debug/DebugFunctions.kt index 9a578ada1..c200b7de8 100644 --- a/app/src/main/java/exh/debug/DebugFunctions.kt +++ b/app/src/main/java/exh/debug/DebugFunctions.kt @@ -2,11 +2,6 @@ package exh.debug import android.app.Application 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 eu.kanade.tachiyomi.data.database.DatabaseHelper 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 exh.EH_SOURCE_ID import exh.EXHMigrations -import exh.EXHSavedSearch import exh.EXH_SOURCE_ID import exh.eh.EHentaiThrottleManager import exh.eh.EHentaiUpdateWorker import exh.metadata.metadata.EHentaiSearchMetadata import exh.metadata.metadata.base.getFlatMetadataForManga import exh.metadata.metadata.base.insertFlatMetadata +import exh.savedsearches.JsonSavedSearch import exh.util.await import exh.util.cancellable import exh.util.jobScheduler @@ -31,8 +26,10 @@ import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.toList import kotlinx.coroutines.runBlocking +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json import uy.kohesive.injekt.injectLazy -import xyz.nulldev.ts.api.http.serializer.FilterSerializer import java.lang.RuntimeException @OptIn(FlowPreview::class) @@ -236,22 +233,13 @@ object DebugFunctions { fun copyEHentaiSavedSearchesToExhentai() { runBlocking { - val filterSerializer = FilterSerializer() val source = sourceManager.get(EH_SOURCE_ID) as? CatalogueSource ?: return@runBlocking val newSource = sourceManager.get(EXH_SOURCE_ID) as? CatalogueSource ?: return@runBlocking val savedSearches = prefs.eh_savedSearches().get().mapNotNull { try { val id = it.substringBefore(':').toLong() if (id != source.id) return@mapNotNull null - val content = JsonParser.parseString(it.substringAfter(':')).obj - - val originalFilters = source.getFilterList() - filterSerializer.deserialize(originalFilters, content["filters"].array) - EXHSavedSearch( - content["name"].string, - content["query"].string, - originalFilters - ) + Json.decodeFromString(it.substringAfter(':')) } catch (t: RuntimeException) { // Load failed XLog.e("Failed to load saved search!", t) @@ -263,15 +251,7 @@ object DebugFunctions { try { val id = it.substringBefore(':').toLong() if (id != newSource.id) return@mapNotNull null - val content = JsonParser.parseString(it.substringAfter(':')).obj - - val originalFilters = source.getFilterList() - filterSerializer.deserialize(originalFilters, content["filters"].array) - EXHSavedSearch( - content["name"].string, - content["query"].string, - originalFilters - ) + Json.decodeFromString(it.substringAfter(':')) } catch (t: RuntimeException) { // Load failed XLog.e("Failed to load saved search!", t) @@ -284,11 +264,7 @@ object DebugFunctions { !it.startsWith("${newSource.id}:") } val newSerialized = savedSearches.map { - "${newSource.id}:" + jsonObject( - "name" to it.name, - "query" to it.query, - "filters" to filterSerializer.serialize(it.filterList) - ).toString() + "${newSource.id}:" + Json.encodeToString(it) } prefs.eh_savedSearches().set((otherSerialized + newSerialized).toSet()) } @@ -296,22 +272,13 @@ object DebugFunctions { fun copyExhentaiSavedSearchesToEHentai() { runBlocking { - val filterSerializer = FilterSerializer() val source = sourceManager.get(EXH_SOURCE_ID) as? CatalogueSource ?: return@runBlocking val newSource = sourceManager.get(EH_SOURCE_ID) as? CatalogueSource ?: return@runBlocking val savedSearches = prefs.eh_savedSearches().get().mapNotNull { try { val id = it.substringBefore(':').toLong() if (id != source.id) return@mapNotNull null - val content = JsonParser.parseString(it.substringAfter(':')).obj - - val originalFilters = source.getFilterList() - filterSerializer.deserialize(originalFilters, content["filters"].array) - EXHSavedSearch( - content["name"].string, - content["query"].string, - originalFilters - ) + Json.decodeFromString(it.substringAfter(':')) } catch (t: RuntimeException) { // Load failed XLog.e("Failed to load saved search!", t) @@ -323,15 +290,7 @@ object DebugFunctions { try { val id = it.substringBefore(':').toLong() if (id != newSource.id) return@mapNotNull null - val content = JsonParser.parseString(it.substringAfter(':')).obj - - val originalFilters = source.getFilterList() - filterSerializer.deserialize(originalFilters, content["filters"].array) - EXHSavedSearch( - content["name"].string, - content["query"].string, - originalFilters - ) + Json.decodeFromString(it.substringAfter(':')) } catch (t: RuntimeException) { // Load failed XLog.e("Failed to load saved search!", t) @@ -344,11 +303,7 @@ object DebugFunctions { !it.startsWith("${newSource.id}:") } val newSerialized = savedSearches.map { - "${newSource.id}:" + jsonObject( - "name" to it.name, - "query" to it.query, - "filters" to filterSerializer.serialize(it.filterList) - ).toString() + "${newSource.id}:" + Json.encodeToString(it) } prefs.eh_savedSearches().set((otherSerialized + newSerialized).toSet()) } diff --git a/app/src/main/java/exh/eh/EHentaiUpdateWorker.kt b/app/src/main/java/exh/eh/EHentaiUpdateWorker.kt index 96072aaa0..f94a3eebf 100644 --- a/app/src/main/java/exh/eh/EHentaiUpdateWorker.kt +++ b/app/src/main/java/exh/eh/EHentaiUpdateWorker.kt @@ -8,7 +8,6 @@ import android.content.ComponentName import android.content.Context import android.os.Build import com.elvishew.xlog.XLog -import com.google.gson.Gson import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.models.Chapter 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.launch import kotlinx.coroutines.runBlocking +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json import rx.schedulers.Schedulers import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -54,7 +55,6 @@ class EHentaiUpdateWorker : JobService(), CoroutineScope { private val db: DatabaseHelper by injectLazy() private val prefs: PreferencesHelper by injectLazy() - private val gson: Gson by injectLazy() private val sourceManager: SourceManager by injectLazy() private val updateHelper: EHentaiUpdateHelper by injectLazy() private val logger = XLog.tag("EHUpdater") @@ -242,7 +242,7 @@ class EHentaiUpdateWorker : JobService(), CoroutineScope { } } finally { prefs.eh_autoUpdateStats().set( - gson.toJson( + Json.encodeToString( EHentaiUpdaterStats( startTime, allMeta.size, diff --git a/app/src/main/java/exh/eh/EHentaiUpdaterStats.kt b/app/src/main/java/exh/eh/EHentaiUpdaterStats.kt index c8151063e..340c3a3fb 100644 --- a/app/src/main/java/exh/eh/EHentaiUpdaterStats.kt +++ b/app/src/main/java/exh/eh/EHentaiUpdaterStats.kt @@ -1,5 +1,8 @@ package exh.eh +import kotlinx.serialization.Serializable + +@Serializable data class EHentaiUpdaterStats( val startTime: Long, val possibleUpdates: Int, diff --git a/app/src/main/java/exh/log/EHNetworkLogging.kt b/app/src/main/java/exh/log/EHNetworkLogging.kt index 8a4229e8a..aaa0f14cb 100644 --- a/app/src/main/java/exh/log/EHNetworkLogging.kt +++ b/app/src/main/java/exh/log/EHNetworkLogging.kt @@ -1,20 +1,19 @@ package exh.log import com.elvishew.xlog.XLog -import com.google.gson.Gson +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor fun OkHttpClient.Builder.maybeInjectEHLogger(): OkHttpClient.Builder { if (EHLogLevel.shouldLog(EHLogLevel.EXTREME)) { - val logger: HttpLoggingInterceptor.Logger = object : HttpLoggingInterceptor.Logger { - override fun log(message: String) { - try { - Gson().fromJson(message, Any::class.java) - XLog.tag("||EH-NETWORK-JSON").nst().json(message) - } catch (ex: Exception) { - XLog.tag("||EH-NETWORK").nb().nst().d(message) - } + val logger: HttpLoggingInterceptor.Logger = HttpLoggingInterceptor.Logger { message -> + try { + Json.decodeFromString(message) + XLog.tag("||EH-NETWORK-JSON").nst().json(message) + } catch (ex: Exception) { + XLog.tag("||EH-NETWORK").nb().nst().d(message) } } return addInterceptor(HttpLoggingInterceptor(logger).apply { level = HttpLoggingInterceptor.Level.BODY }) diff --git a/app/src/main/java/exh/md/handlers/ApiChapterParser.kt b/app/src/main/java/exh/md/handlers/ApiChapterParser.kt index dfe64fee7..86f678748 100644 --- a/app/src/main/java/exh/md/handlers/ApiChapterParser.kt +++ b/app/src/main/java/exh/md/handlers/ApiChapterParser.kt @@ -1,24 +1,27 @@ package exh.md.handlers -import com.github.salomonbrys.kotson.string -import com.google.gson.JsonParser 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 java.util.Date class ApiChapterParser { fun pageListParse(response: Response): List { val jsonData = response.body!!.string() - val json = JsonParser.parseString(jsonData).asJsonObject + val json = Json.decodeFromString(jsonData) val pages = mutableListOf() - val hash = json.get("hash").string - val pageArray = json.getAsJsonArray("page_array") - val server = json.get("server").string + val hash = json["hash"]!!.jsonPrimitive.content + val pageArray = json["page_array"]!!.jsonArray + val server = json["server"]!!.jsonPrimitive.content 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)) } @@ -27,8 +30,8 @@ class ApiChapterParser { fun externalParse(response: Response): String { val jsonData = response.body!!.string() - val json = JsonParser.parseString(jsonData).asJsonObject - val external = json.get("external").string + val json = Json.decodeFromString(jsonData) + val external = json["external"]!!.jsonPrimitive.content return external.substringAfterLast("/") } } diff --git a/app/src/main/java/exh/md/handlers/ApiMangaParser.kt b/app/src/main/java/exh/md/handlers/ApiMangaParser.kt index a06481066..405a4ee56 100644 --- a/app/src/main/java/exh/md/handlers/ApiMangaParser.kt +++ b/app/src/main/java/exh/md/handlers/ApiMangaParser.kt @@ -1,9 +1,6 @@ package exh.md.handlers 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.models.Manga 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.insertFlatMetadata 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 rx.Completable import rx.Single @@ -232,8 +234,8 @@ class ApiMangaParser(private val langs: List) { throw Exception("Null Response") } - val jsonObject = JsonParser.parseString(body).obj - return jsonObject["manga_id"]?.nullInt ?: throw Exception("No manga associated with chapter") + val jsonObject = Json.decodeFromString(body) + return jsonObject["manga_id"]?.jsonPrimitive?.intOrNull ?: throw Exception("No manga associated with chapter") } catch (e: Exception) { XLog.e(e) throw e diff --git a/app/src/main/java/exh/metadata/metadata/EHentaiSearchMetadata.kt b/app/src/main/java/exh/metadata/metadata/EHentaiSearchMetadata.kt index 77701d229..51d53cd4c 100644 --- a/app/src/main/java/exh/metadata/metadata/EHentaiSearchMetadata.kt +++ b/app/src/main/java/exh/metadata/metadata/EHentaiSearchMetadata.kt @@ -9,10 +9,12 @@ import exh.metadata.EX_DATE_FORMAT import exh.metadata.ONGOING_SUFFIX import exh.metadata.humanReadableByteCount import exh.metadata.metadata.base.RaisedSearchMetadata +import kotlinx.serialization.Serializable import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.util.Date +@Serializable class EHentaiSearchMetadata : RaisedSearchMetadata() { var gId: String? get() = indexedExtra diff --git a/app/src/main/java/exh/metadata/metadata/EightMusesSearchMetadata.kt b/app/src/main/java/exh/metadata/metadata/EightMusesSearchMetadata.kt index 8696db23f..8e65978c8 100644 --- a/app/src/main/java/exh/metadata/metadata/EightMusesSearchMetadata.kt +++ b/app/src/main/java/exh/metadata/metadata/EightMusesSearchMetadata.kt @@ -4,7 +4,9 @@ import android.content.Context import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.model.SManga import exh.metadata.metadata.base.RaisedSearchMetadata +import kotlinx.serialization.Serializable +@Serializable class EightMusesSearchMetadata : RaisedSearchMetadata() { var path: List = emptyList() diff --git a/app/src/main/java/exh/metadata/metadata/HBrowseSearchMetadata.kt b/app/src/main/java/exh/metadata/metadata/HBrowseSearchMetadata.kt index eabd5cf0a..9fdf045ee 100644 --- a/app/src/main/java/exh/metadata/metadata/HBrowseSearchMetadata.kt +++ b/app/src/main/java/exh/metadata/metadata/HBrowseSearchMetadata.kt @@ -5,7 +5,9 @@ import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.model.SManga import exh.metadata.metadata.EightMusesSearchMetadata.Companion.ARTIST_NAMESPACE import exh.metadata.metadata.base.RaisedSearchMetadata +import kotlinx.serialization.Serializable +@Serializable class HBrowseSearchMetadata : RaisedSearchMetadata() { var hbId: Long? = null diff --git a/app/src/main/java/exh/metadata/metadata/HentaiCafeSearchMetadata.kt b/app/src/main/java/exh/metadata/metadata/HentaiCafeSearchMetadata.kt index d7c32c9ba..baac866db 100644 --- a/app/src/main/java/exh/metadata/metadata/HentaiCafeSearchMetadata.kt +++ b/app/src/main/java/exh/metadata/metadata/HentaiCafeSearchMetadata.kt @@ -4,7 +4,9 @@ import android.content.Context import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.model.SManga import exh.metadata.metadata.base.RaisedSearchMetadata +import kotlinx.serialization.Serializable +@Serializable class HentaiCafeSearchMetadata : RaisedSearchMetadata() { var hcId: String? = null var readerId: String? = null diff --git a/app/src/main/java/exh/metadata/metadata/HitomiSearchMetadata.kt b/app/src/main/java/exh/metadata/metadata/HitomiSearchMetadata.kt index 9c60ec289..5c9deb39b 100644 --- a/app/src/main/java/exh/metadata/metadata/HitomiSearchMetadata.kt +++ b/app/src/main/java/exh/metadata/metadata/HitomiSearchMetadata.kt @@ -5,9 +5,10 @@ import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.model.SManga import exh.metadata.EX_DATE_FORMAT import exh.metadata.metadata.base.RaisedSearchMetadata -import exh.plusAssign +import kotlinx.serialization.Serializable import java.util.Date +@Serializable class HitomiSearchMetadata : RaisedSearchMetadata() { var url get() = hlId?.let { urlFromHlId(it) } set(a) { @@ -26,7 +27,7 @@ class HitomiSearchMetadata : RaisedSearchMetadata() { var group: String? = null - var type: String? = null + var genre: String? = null var language: String? = null @@ -101,7 +102,7 @@ class HitomiSearchMetadata : RaisedSearchMetadata() { pairs += Pair(context.getString(R.string.artist), artists) } 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) } val series = series.joinToString() if (series.isNotBlank()) { diff --git a/app/src/main/java/exh/metadata/metadata/MangaDexSearchMetadata.kt b/app/src/main/java/exh/metadata/metadata/MangaDexSearchMetadata.kt index b5598ba8e..e78a22da3 100644 --- a/app/src/main/java/exh/metadata/metadata/MangaDexSearchMetadata.kt +++ b/app/src/main/java/exh/metadata/metadata/MangaDexSearchMetadata.kt @@ -5,7 +5,9 @@ import androidx.core.net.toUri import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.model.SManga import exh.metadata.metadata.base.RaisedSearchMetadata +import kotlinx.serialization.Serializable +@Serializable class MangaDexSearchMetadata : RaisedSearchMetadata() { var mdId: String? = null diff --git a/app/src/main/java/exh/metadata/metadata/NHentaiSearchMetadata.kt b/app/src/main/java/exh/metadata/metadata/NHentaiSearchMetadata.kt index 1f635eb2a..f38a577db 100644 --- a/app/src/main/java/exh/metadata/metadata/NHentaiSearchMetadata.kt +++ b/app/src/main/java/exh/metadata/metadata/NHentaiSearchMetadata.kt @@ -6,9 +6,11 @@ import eu.kanade.tachiyomi.source.model.SManga import exh.metadata.EX_DATE_FORMAT import exh.metadata.ONGOING_SUFFIX import exh.metadata.metadata.base.RaisedSearchMetadata +import kotlinx.serialization.Serializable import uy.kohesive.injekt.api.get import java.util.Date +@Serializable class NHentaiSearchMetadata : RaisedSearchMetadata() { var url get() = nhId?.let { BASE_URL + nhIdToPath(it) } set(a) { diff --git a/app/src/main/java/exh/metadata/metadata/PervEdenSearchMetadata.kt b/app/src/main/java/exh/metadata/metadata/PervEdenSearchMetadata.kt index fab068824..99cf5cf60 100644 --- a/app/src/main/java/exh/metadata/metadata/PervEdenSearchMetadata.kt +++ b/app/src/main/java/exh/metadata/metadata/PervEdenSearchMetadata.kt @@ -6,7 +6,9 @@ import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.model.SManga import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedTitle +import kotlinx.serialization.Serializable +@Serializable class PervEdenSearchMetadata : RaisedSearchMetadata() { var pvId: String? = null @@ -23,7 +25,7 @@ class PervEdenSearchMetadata : RaisedSearchMetadata() { var artist: String? = null - var type: String? = null + var genre: String? = null var rating: Float? = null @@ -102,7 +104,7 @@ class PervEdenSearchMetadata : RaisedSearchMetadata() { pairs += Pair(context.getString(R.string.alt_titles), altTitles) } 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()) } status?.let { pairs += Pair(context.getString(R.string.status), it) } lang?.let { pairs += Pair(context.getString(R.string.language), it) } diff --git a/app/src/main/java/exh/metadata/metadata/PururinSearchMetadata.kt b/app/src/main/java/exh/metadata/metadata/PururinSearchMetadata.kt index 80bbaa226..091d86e79 100644 --- a/app/src/main/java/exh/metadata/metadata/PururinSearchMetadata.kt +++ b/app/src/main/java/exh/metadata/metadata/PururinSearchMetadata.kt @@ -4,7 +4,9 @@ import android.content.Context import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.model.SManga import exh.metadata.metadata.base.RaisedSearchMetadata +import kotlinx.serialization.Serializable +@Serializable class PururinSearchMetadata : RaisedSearchMetadata() { var prId: Int? = null diff --git a/app/src/main/java/exh/metadata/metadata/TsuminoSearchMetadata.kt b/app/src/main/java/exh/metadata/metadata/TsuminoSearchMetadata.kt index cced84702..e8bea7a17 100644 --- a/app/src/main/java/exh/metadata/metadata/TsuminoSearchMetadata.kt +++ b/app/src/main/java/exh/metadata/metadata/TsuminoSearchMetadata.kt @@ -6,10 +6,12 @@ import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.model.SManga import exh.metadata.EX_DATE_FORMAT import exh.metadata.metadata.base.RaisedSearchMetadata +import kotlinx.serialization.Serializable import java.text.SimpleDateFormat import java.util.Date import java.util.Locale +@Serializable class TsuminoSearchMetadata : RaisedSearchMetadata() { var tmId: Int? = null diff --git a/app/src/main/java/exh/metadata/metadata/base/FlatMetadata.kt b/app/src/main/java/exh/metadata/metadata/base/FlatMetadata.kt index 3b65dae96..0df33b144 100644 --- a/app/src/main/java/exh/metadata/metadata/base/FlatMetadata.kt +++ b/app/src/main/java/exh/metadata/metadata/base/FlatMetadata.kt @@ -5,10 +5,15 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper import exh.metadata.sql.models.SearchMetadata import exh.metadata.sql.models.SearchTag import exh.metadata.sql.models.SearchTitle +import kotlinx.serialization.InternalSerializationApi +import kotlinx.serialization.Serializable +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.serializer import rx.Completable import rx.Single import kotlin.reflect.KClass +@Serializable data class FlatMetadata( val metadata: SearchMetadata, val tags: List, @@ -16,9 +21,10 @@ data class FlatMetadata( ) { inline fun raise(): T = raise(T::class) + @OptIn(InternalSerializationApi::class) fun raise(clazz: KClass): T = - RaisedSearchMetadata.raiseFlattenGson - .fromJson(metadata.extra, clazz.java).apply { + RaisedSearchMetadata.raiseFlattenJson + .decodeFromString(clazz.serializer(), metadata.extra).apply { fillBaseFields(this@FlatMetadata) } } diff --git a/app/src/main/java/exh/metadata/metadata/base/RaisedSearchMetadata.kt b/app/src/main/java/exh/metadata/metadata/base/RaisedSearchMetadata.kt index d800c0033..85282a4a4 100644 --- a/app/src/main/java/exh/metadata/metadata/base/RaisedSearchMetadata.kt +++ b/app/src/main/java/exh/metadata/metadata/base/RaisedSearchMetadata.kt @@ -1,17 +1,35 @@ package exh.metadata.metadata.base import android.content.Context -import com.google.gson.Gson -import com.google.gson.GsonBuilder import eu.kanade.tachiyomi.source.model.SManga 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.SearchTag import exh.metadata.sql.models.SearchTitle 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.reflect.KProperty +@Polymorphic +@Serializable abstract class RaisedSearchMetadata { @Transient var mangaId: Long = -1 @@ -67,7 +85,7 @@ abstract class RaisedSearchMetadata { fun flatten(): FlatMetadata { require(mangaId != -1L) - val extra = raiseFlattenGson.toJson(this) + val extra = raiseFlattenJson.encodeToString(this) return FlatMetadata( SearchMetadata( mangaId, @@ -122,7 +140,25 @@ abstract class RaisedSearchMetadata { (this).filter { it.type != TAG_TYPE_VIRTUAL } .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 { /** diff --git a/app/src/main/java/exh/metadata/metadata/base/RaisedTag.kt b/app/src/main/java/exh/metadata/metadata/base/RaisedTag.kt index 2745f1084..4c33606f2 100644 --- a/app/src/main/java/exh/metadata/metadata/base/RaisedTag.kt +++ b/app/src/main/java/exh/metadata/metadata/base/RaisedTag.kt @@ -1,5 +1,8 @@ package exh.metadata.metadata.base +import kotlinx.serialization.Serializable + +@Serializable data class RaisedTag( val namespace: String?, val name: String, diff --git a/app/src/main/java/exh/metadata/metadata/base/RaisedTitle.kt b/app/src/main/java/exh/metadata/metadata/base/RaisedTitle.kt index bc901bc9a..db4927c3d 100644 --- a/app/src/main/java/exh/metadata/metadata/base/RaisedTitle.kt +++ b/app/src/main/java/exh/metadata/metadata/base/RaisedTitle.kt @@ -1,5 +1,8 @@ package exh.metadata.metadata.base +import kotlinx.serialization.Serializable + +@Serializable data class RaisedTitle( val title: String, val type: Int = 0 diff --git a/app/src/main/java/exh/metadata/sql/models/SearchMetadata.kt b/app/src/main/java/exh/metadata/sql/models/SearchMetadata.kt index b4bb92b42..bb1bf377f 100644 --- a/app/src/main/java/exh/metadata/sql/models/SearchMetadata.kt +++ b/app/src/main/java/exh/metadata/sql/models/SearchMetadata.kt @@ -1,5 +1,9 @@ package exh.metadata.sql.models +import kotlinx.serialization.Contextual +import kotlinx.serialization.Serializable + +@Serializable data class SearchMetadata( // Manga ID this gallery is linked to val mangaId: Long, @@ -17,5 +21,6 @@ data class SearchMetadata( val extraVersion: Int ) { // Transient information attached to this piece of metadata, useful for caching - var transientCache: Map? = null + + var transientCache: Map? = null } diff --git a/app/src/main/java/exh/metadata/sql/models/SearchTag.kt b/app/src/main/java/exh/metadata/sql/models/SearchTag.kt index c52a36633..87260b9ac 100644 --- a/app/src/main/java/exh/metadata/sql/models/SearchTag.kt +++ b/app/src/main/java/exh/metadata/sql/models/SearchTag.kt @@ -1,5 +1,8 @@ package exh.metadata.sql.models +import kotlinx.serialization.Serializable + +@Serializable data class SearchTag( // Tag identifier, unique val id: Long?, diff --git a/app/src/main/java/exh/metadata/sql/models/SearchTitle.kt b/app/src/main/java/exh/metadata/sql/models/SearchTitle.kt index c212cde0f..fa346b841 100644 --- a/app/src/main/java/exh/metadata/sql/models/SearchTitle.kt +++ b/app/src/main/java/exh/metadata/sql/models/SearchTitle.kt @@ -1,5 +1,8 @@ package exh.metadata.sql.models +import kotlinx.serialization.Serializable + +@Serializable data class SearchTitle( // Title identifier, unique val id: Long?, diff --git a/app/src/main/java/exh/EXHSavedSearch.kt b/app/src/main/java/exh/savedsearches/EXHSavedSearch.kt similarity index 85% rename from app/src/main/java/exh/EXHSavedSearch.kt rename to app/src/main/java/exh/savedsearches/EXHSavedSearch.kt index 5a1570664..7c1e7564e 100644 --- a/app/src/main/java/exh/EXHSavedSearch.kt +++ b/app/src/main/java/exh/savedsearches/EXHSavedSearch.kt @@ -1,4 +1,4 @@ -package exh +package exh.savedsearches import eu.kanade.tachiyomi.source.model.FilterList diff --git a/app/src/main/java/exh/savedsearches/JsonSavedSearch.kt b/app/src/main/java/exh/savedsearches/JsonSavedSearch.kt new file mode 100644 index 000000000..560551cc4 --- /dev/null +++ b/app/src/main/java/exh/savedsearches/JsonSavedSearch.kt @@ -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 +) diff --git a/app/src/main/java/exh/ui/captcha/BrowserActionActivity.kt b/app/src/main/java/exh/ui/captcha/BrowserActionActivity.kt index d7c8611af..620a2ed85 100644 --- a/app/src/main/java/exh/ui/captcha/BrowserActionActivity.kt +++ b/app/src/main/java/exh/ui/captcha/BrowserActionActivity.kt @@ -13,9 +13,6 @@ import android.webkit.WebView import androidx.appcompat.app.AppCompatActivity import com.afollestad.materialdialogs.MaterialDialog 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.data.preference.PreferencesHelper import eu.kanade.tachiyomi.network.NetworkHelper @@ -28,6 +25,12 @@ import exh.source.DelegatedHttpSource import exh.util.melt import kotlinx.android.synthetic.main.eh_activity_captcha.toolbar 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.MediaType.Companion.toMediaTypeOrNull import okhttp3.MultipartBody @@ -153,9 +156,9 @@ class BrowserActionActivity : AppCompatActivity() { .asObservableSuccess() .subscribeOn(Schedulers.io()) .map { - val json = JsonParser.parseString(it.body!!.string()) + val json = Json.decodeFromString(it.body!!.string()) it.close() - json["token"].string + json["token"]!!.jsonPrimitive.content }.melt() webview.addJavascriptInterface(this@BrowserActionActivity, "exh") @@ -317,7 +320,7 @@ class BrowserActionActivity : AppCompatActivity() { .build() ).asObservableSuccess() }.map { response -> - JsonParser.parseString(response.body!!.string())["results"][0]["alternatives"][0]["transcript"].string.trim() + Json.decodeFromString(response.body!!.string())["results"]!!.jsonArray[0].jsonObject["alternatives"]!!.jsonArray[0].jsonObject["transcript"]!!.jsonPrimitive.content.trim() }.toSingle() } diff --git a/app/src/main/java/exh/ui/metadata/adapters/HitomiDescriptionAdapter.kt b/app/src/main/java/exh/ui/metadata/adapters/HitomiDescriptionAdapter.kt index 7d85e0273..fb2ca17ab 100644 --- a/app/src/main/java/exh/ui/metadata/adapters/HitomiDescriptionAdapter.kt +++ b/app/src/main/java/exh/ui/metadata/adapters/HitomiDescriptionAdapter.kt @@ -47,7 +47,7 @@ class HitomiDescriptionAdapter( val meta = controller.presenter.meta if (meta == null || meta !is HitomiSearchMetadata) return - val genre = meta.type + val genre = meta.genre if (genre != null) { val pair = when (genre) { "doujinshi" -> Pair(SourceTagsUtil.DOUJINSHI_COLOR, R.string.doujinshi) diff --git a/app/src/main/java/exh/ui/metadata/adapters/PervEdenDescriptionAdapter.kt b/app/src/main/java/exh/ui/metadata/adapters/PervEdenDescriptionAdapter.kt index 9c77cd684..e25e861af 100644 --- a/app/src/main/java/exh/ui/metadata/adapters/PervEdenDescriptionAdapter.kt +++ b/app/src/main/java/exh/ui/metadata/adapters/PervEdenDescriptionAdapter.kt @@ -47,7 +47,7 @@ class PervEdenDescriptionAdapter( val meta = controller.presenter.meta if (meta == null || meta !is PervEdenSearchMetadata) return - val genre = meta.type + val genre = meta.genre if (genre != null) { val pair = when (genre) { "Doujinshi" -> Pair(SourceTagsUtil.DOUJINSHI_COLOR, R.string.doujinshi) diff --git a/app/src/main/java/xyz/nulldev/ts/api/http/serializer/FilterSerializer.kt b/app/src/main/java/xyz/nulldev/ts/api/http/serializer/FilterSerializer.kt index de1a9f3b1..a4d4110fa 100644 --- a/app/src/main/java/xyz/nulldev/ts/api/http/serializer/FilterSerializer.kt +++ b/app/src/main/java/xyz/nulldev/ts/api/http/serializer/FilterSerializer.kt @@ -1,21 +1,20 @@ 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.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.full.isSubclassOf @@ -35,7 +34,7 @@ class FilterSerializer { SortSerializer(this) ) - fun serialize(filters: FilterList) = JsonArray().apply { + fun serialize(filters: FilterList) = buildJsonArray { filters.forEach { @Suppress("UNCHECKED_CAST") add(serialize(it as Filter)) @@ -43,26 +42,31 @@ class FilterSerializer { } fun serialize(filter: Filter): JsonObject { - val out = JsonObject() for (serializer in serializers) { if (filter::class.isSubclassOf(serializer.clazz)) { // TODO Not sure how to deal with the mess of types here @Suppress("UNCHECKED_CAST") serializer as Serializer> - serializer.serialize(out, filter) + return buildJsonObject { + with(serializer) { serialize(filter) } - out[CLASS_MAPPINGS] = JsonObject() + val classMappings = mutableListOf>() - serializer.mappings().forEach { - val res = it.second.get(filter) - out[it.first] = res - out[CLASS_MAPPINGS][it.first] = res?.javaClass?.name ?: "null" + serializer.mappings().forEach { + val res = it.second.get(filter) + put(it.first, res.toString()) + 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!") @@ -71,13 +75,13 @@ class FilterSerializer { fun deserialize(filters: FilterList, json: JsonArray) { filters.zip(json).forEach { (filter, obj) -> @Suppress("UNCHECKED_CAST") - deserialize(filter as Filter, obj.obj) + deserialize(filter as Filter, obj.jsonObject) } } fun deserialize(filter: Filter, json: JsonObject) { val serializer = serializers.find { - it.type == json[TYPE].string + it.type == json[TYPE]!!.jsonPrimitive.content } ?: throw IllegalArgumentException("Cannot deserialize this type!") // TODO Not sure how to deal with the mess of types here @@ -88,17 +92,17 @@ class FilterSerializer { serializer.mappings().forEach { if (it.second is KMutableProperty1) { - val obj = json[it.first] - val res: Any? = when (json[CLASS_MAPPINGS][it.first].string) { + val obj = json[it.first]!!.jsonPrimitive + val res: Any? = when (json[CLASS_MAPPINGS]!!.jsonObject[it.first]!!.jsonPrimitive.content) { java.lang.Integer::class.java.name -> obj.int java.lang.Long::class.java.name -> obj.long java.lang.Float::class.java.name -> obj.float java.lang.Double::class.java.name -> obj.double - java.lang.String::class.java.name -> obj.string - java.lang.Boolean::class.java.name -> obj.bool - java.lang.Byte::class.java.name -> obj.byte - java.lang.Short::class.java.name -> obj.short - java.lang.Character::class.java.name -> obj.char + java.lang.String::class.java.name -> obj.content + java.lang.Boolean::class.java.name -> obj.boolean + java.lang.Byte::class.java.name -> obj.content.toByte() + java.lang.Short::class.java.name -> obj.content.toShort() + java.lang.Character::class.java.name -> obj.content[0] "null" -> null else -> throw IllegalArgumentException("Cannot deserialize this type!") } diff --git a/app/src/main/java/xyz/nulldev/ts/api/http/serializer/FilterSerializerModels.kt b/app/src/main/java/xyz/nulldev/ts/api/http/serializer/FilterSerializerModels.kt index 0b25804f7..1b73d06a3 100644 --- a/app/src/main/java/xyz/nulldev/ts/api/http/serializer/FilterSerializerModels.kt +++ b/app/src/main/java/xyz/nulldev/ts/api/http/serializer/FilterSerializerModels.kt @@ -1,20 +1,23 @@ 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 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.KProperty1 interface Serializer> { - fun serialize(json: JsonObject, filter: T) {} + fun JsonObjectBuilder.serialize(filter: T) {} fun deserialize(json: JsonObject, filter: T) {} /** @@ -76,9 +79,9 @@ class SelectSerializer(override val serializer: FilterSerializer) : Serializer) { + override fun JsonObjectBuilder.serialize(filter: Filter.Select) { // Serialize values to JSON - json[VALUES] = JsonArray().apply { + putJsonArray(VALUES) { filter.values.map { it.toString() }.forEach { add(it) } @@ -146,15 +149,15 @@ class GroupSerializer(override val serializer: FilterSerializer) : Serializer) { - json[STATE] = JsonArray().apply { + override fun JsonObjectBuilder.serialize(filter: Filter.Group) { + putJsonArray(STATE) { filter.state.forEach { add( if (it is Filter<*>) { @Suppress("UNCHECKED_CAST") serializer.serialize(it as Filter) } else { - JsonNull.INSTANCE + JsonNull } ) } @@ -162,10 +165,10 @@ class GroupSerializer(override val serializer: FilterSerializer) : Serializer) { - json[STATE].asJsonArray.forEachIndexed { index, jsonElement -> - if (!jsonElement.isJsonNull) { + json[STATE]!!.jsonArray.forEachIndexed { index, jsonElement -> + if (jsonElement !is JsonNull) { @Suppress("UNCHECKED_CAST") - serializer.deserialize(filter.state[index] as Filter, jsonElement.asJsonObject) + serializer.deserialize(filter.state[index] as Filter, jsonElement.jsonObject) } } } @@ -184,27 +187,29 @@ class SortSerializer(override val serializer: FilterSerializer) : Serializer - JsonObject().apply { - this[STATE_INDEX] = index - this[STATE_ASCENDING] = ascending - } - } ?: JsonNull.INSTANCE + put( + STATE, + filter.state?.let { (index, ascending) -> + buildJsonObject { + put(STATE_INDEX, index) + put(STATE_ASCENDING, ascending) + } + } ?: JsonNull + ) } override fun deserialize(json: JsonObject, filter: Filter.Sort) { // Deserialize state - filter.state = json[STATE].nullObj?.let { + filter.state = (json[STATE] as? JsonObject)?.let { Filter.Sort.Selection( - it[STATE_INDEX].int, - it[STATE_ASCENDING].bool + it[STATE_INDEX]!!.jsonPrimitive.int, + it[STATE_ASCENDING]!!.jsonPrimitive.boolean ) } } @@ -227,19 +232,17 @@ class AutoCompleteSerializer(override val serializer: FilterSerializer) : Serial override val type = "AUTOCOMPLETE" 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 - json[STATE] = JsonArray().apply { + putJsonArray(STATE) { filter.state.forEach { add(it) } } } override fun deserialize(json: JsonObject, filter: Filter.AutoComplete) { // Deserialize state - json[STATE].nullArray?.let { array -> - filter.state = array.map { - it.string - } + filter.state = json[STATE]!!.jsonArray.map { + it.jsonPrimitive.content } }