Convert SY specific usages of Gson to Kotlin Serialization
Cleanup saved searches a bit Cleanup json parsing
This commit is contained in:
parent
bbfce97125
commit
f3365cef67
@ -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<JsonSavedSearch>(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<JsonSavedSearch>(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
|
||||
)
|
||||
)
|
||||
|
@ -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<JsonObject>(
|
||||
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,
|
||||
|
@ -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
|
||||
|
@ -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?,
|
||||
|
@ -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 {
|
||||
val json = buildJsonObject {
|
||||
put("method", "gtoken")
|
||||
put(
|
||||
"pagelist",
|
||||
buildJsonArray {
|
||||
add(
|
||||
JsonArray().apply {
|
||||
buildJsonArray {
|
||||
add(gallery.toInt())
|
||||
add(pageToken)
|
||||
add(pageNum.toInt())
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
val outJson = JsonParser.parseString(
|
||||
val outJson = Json.decodeFromString<JsonObject>(
|
||||
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 {
|
||||
|
@ -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() }
|
||||
|
@ -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<JsonResponse>(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<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 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})")
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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<JsonObject>(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<JsonSavedSearch>(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) {
|
||||
|
@ -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<JsonObject>(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<JsonObject>(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<JsonObject>(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)
|
||||
|
@ -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(
|
||||
|
@ -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())
|
||||
}
|
||||
},
|
||||
|
@ -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<JsonSavedSearch>(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) {
|
||||
|
@ -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<EHentaiUpdaterStats>(it)
|
||||
Json.decodeFromString<EHentaiUpdaterStats>(it)
|
||||
}
|
||||
|
||||
val statsText = if (stats != null) {
|
||||
|
@ -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<Manga>()
|
||||
val mergedMangaReferences = mutableListOf<MergedMangaReference>()
|
||||
@ -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<Chapter>()
|
||||
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<MangaSource>
|
||||
) {
|
||||
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
|
||||
}
|
||||
|
@ -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<JsonSavedSearch>(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<JsonSavedSearch>(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<JsonSavedSearch>(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<JsonSavedSearch>(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())
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -1,5 +1,8 @@
|
||||
package exh.eh
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class EHentaiUpdaterStats(
|
||||
val startTime: Long,
|
||||
val possibleUpdates: Int,
|
||||
|
@ -1,22 +1,21 @@
|
||||
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) {
|
||||
val logger: HttpLoggingInterceptor.Logger = HttpLoggingInterceptor.Logger { message ->
|
||||
try {
|
||||
Gson().fromJson(message, Any::class.java)
|
||||
Json.decodeFromString<Any>(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 })
|
||||
}
|
||||
return this
|
||||
|
@ -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<Page> {
|
||||
val jsonData = response.body!!.string()
|
||||
val json = JsonParser.parseString(jsonData).asJsonObject
|
||||
val json = Json.decodeFromString<JsonObject>(jsonData)
|
||||
|
||||
val pages = mutableListOf<Page>()
|
||||
|
||||
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<JsonObject>(jsonData)
|
||||
val external = json["external"]!!.jsonPrimitive.content
|
||||
return external.substringAfterLast("/")
|
||||
}
|
||||
}
|
||||
|
@ -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<String>) {
|
||||
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<JsonObject>(body)
|
||||
return jsonObject["manga_id"]?.jsonPrimitive?.intOrNull ?: throw Exception("No manga associated with chapter")
|
||||
} catch (e: Exception) {
|
||||
XLog.e(e)
|
||||
throw e
|
||||
|
@ -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
|
||||
|
@ -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<String> = emptyList()
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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()) {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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) }
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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<SearchTag>,
|
||||
@ -16,9 +21,10 @@ data class FlatMetadata(
|
||||
) {
|
||||
inline fun <reified T : RaisedSearchMetadata> raise(): T = raise(T::class)
|
||||
|
||||
@OptIn(InternalSerializationApi::class)
|
||||
fun <T : RaisedSearchMetadata> raise(clazz: KClass<T>): T =
|
||||
RaisedSearchMetadata.raiseFlattenGson
|
||||
.fromJson(metadata.extra, clazz.java).apply {
|
||||
RaisedSearchMetadata.raiseFlattenJson
|
||||
.decodeFromString(clazz.serializer(), metadata.extra).apply {
|
||||
fillBaseFields(this@FlatMetadata)
|
||||
}
|
||||
}
|
||||
|
@ -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<RaisedSearchMetadata, String?> {
|
||||
/**
|
||||
|
@ -1,5 +1,8 @@
|
||||
package exh.metadata.metadata.base
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class RaisedTag(
|
||||
val namespace: String?,
|
||||
val name: String,
|
||||
|
@ -1,5 +1,8 @@
|
||||
package exh.metadata.metadata.base
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class RaisedTitle(
|
||||
val title: String,
|
||||
val type: Int = 0
|
||||
|
@ -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<String, Any>? = null
|
||||
|
||||
var transientCache: Map<String, @Contextual Any>? = null
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
package exh.metadata.sql.models
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class SearchTag(
|
||||
// Tag identifier, unique
|
||||
val id: Long?,
|
||||
|
@ -1,5 +1,8 @@
|
||||
package exh.metadata.sql.models
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class SearchTitle(
|
||||
// Title identifier, unique
|
||||
val id: Long?,
|
||||
|
@ -1,4 +1,4 @@
|
||||
package exh
|
||||
package exh.savedsearches
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
|
11
app/src/main/java/exh/savedsearches/JsonSavedSearch.kt
Normal file
11
app/src/main/java/exh/savedsearches/JsonSavedSearch.kt
Normal 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
|
||||
)
|
@ -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<JsonObject>(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<JsonObject>(response.body!!.string())["results"]!!.jsonArray[0].jsonObject["alternatives"]!!.jsonArray[0].jsonObject["transcript"]!!.jsonPrimitive.content.trim()
|
||||
}.toSingle()
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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<Any?>))
|
||||
@ -43,26 +42,31 @@ class FilterSerializer {
|
||||
}
|
||||
|
||||
fun serialize(filter: Filter<Any?>): 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<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 {
|
||||
val res = it.second.get(filter)
|
||||
out[it.first] = res
|
||||
out[CLASS_MAPPINGS][it.first] = res?.javaClass?.name ?: "null"
|
||||
put(it.first, res.toString())
|
||||
classMappings += it.first to (res?.javaClass?.name ?: "null")
|
||||
}
|
||||
|
||||
out[TYPE] = serializer.type
|
||||
putJsonObject(CLASS_MAPPINGS) {
|
||||
classMappings.forEach { (t, u) ->
|
||||
put(t, u.toString())
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
put(TYPE, serializer.type)
|
||||
}
|
||||
}
|
||||
}
|
||||
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<Any?>, obj.obj)
|
||||
deserialize(filter as Filter<Any?>, obj.jsonObject)
|
||||
}
|
||||
}
|
||||
|
||||
fun deserialize(filter: Filter<Any?>, 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!")
|
||||
}
|
||||
|
@ -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<in T : Filter<out Any?>> {
|
||||
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<F
|
||||
override val type = "SELECT"
|
||||
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
|
||||
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<Fi
|
||||
override val type = "GROUP"
|
||||
override val clazz = Filter.Group::class
|
||||
|
||||
override fun serialize(json: JsonObject, filter: Filter.Group<Any?>) {
|
||||
json[STATE] = JsonArray().apply {
|
||||
override fun JsonObjectBuilder.serialize(filter: Filter.Group<Any?>) {
|
||||
putJsonArray(STATE) {
|
||||
filter.state.forEach {
|
||||
add(
|
||||
if (it is Filter<*>) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
serializer.serialize(it as Filter<Any?>)
|
||||
} 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?>) {
|
||||
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<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 clazz = Filter.Sort::class
|
||||
|
||||
override fun serialize(json: JsonObject, filter: Filter.Sort) {
|
||||
override fun JsonObjectBuilder.serialize(filter: Filter.Sort) {
|
||||
// Serialize values
|
||||
json[VALUES] = JsonArray().apply {
|
||||
putJsonArray(VALUES) {
|
||||
filter.values.forEach { add(it) }
|
||||
}
|
||||
|
||||
// Serialize state
|
||||
json[STATE] = filter.state?.let { (index, ascending) ->
|
||||
JsonObject().apply {
|
||||
this[STATE_INDEX] = index
|
||||
this[STATE_ASCENDING] = ascending
|
||||
put(
|
||||
STATE,
|
||||
filter.state?.let { (index, ascending) ->
|
||||
buildJsonObject {
|
||||
put(STATE_INDEX, index)
|
||||
put(STATE_ASCENDING, ascending)
|
||||
}
|
||||
} ?: JsonNull.INSTANCE
|
||||
} ?: 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
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user